class Apcera::Stager

Constants

PKG_NAME
UPDATED_PKG_NAME

Attributes

app_path[RW]
pkg_path[RW]
root_path[RW]
stager_url[RW]
system_options[RW]
updated_pkg_path[RW]

Public Class Methods

new(options = {}) click to toggle source
# File lib/apcera/stager/stager.rb, line 8
def initialize(options = {})
  # Require stager url. Needed to talk to the Staging Coordinator.
  @stager_url = options[:stager_url] || ENV["STAGER_URL"]
  raise Apcera::Error::StagerURLRequired.new("stager_url required") unless @stager_url

  # Setup the environment, some test items here.
  setup_environment
end

Public Instance Methods

complete() click to toggle source

Finish staging, compress your app dir and send to the staging coordinator. Then tell the staging coordinator we are done.

# File lib/apcera/stager/stager.rb, line 254
def complete
  upload
  done
end
dependencies_add(type, name) click to toggle source

Add dependencies to package.

# File lib/apcera/stager/stager.rb, line 168
def dependencies_add(type, name)
  exists = self.meta["dependencies"].detect { |dep| dep["type"] == type && dep["name"] == name }
  return false if exists

  response = RestClient.put(stager_meta_url, {
    :resource => "dependencies",
    :action => "add",
    :type => type,
    :name => name
  })

  true
rescue => e
  fail e
end
dependencies_remove(type, name) click to toggle source

Delete dependencies from package.

# File lib/apcera/stager/stager.rb, line 185
def dependencies_remove(type, name)
  exists = self.meta["dependencies"].detect { |dep| dep["type"] == type && dep["name"] == name}
  return false if !exists

  response = RestClient.put(stager_meta_url, {
    :resource => "dependencies",
    :action => "remove",
    :type => type,
    :name => name
  })

  true
rescue => e
  fail e
end
done() click to toggle source

Tell the staging coordinator you are done.

# File lib/apcera/stager/stager.rb, line 237
def done
  response = RestClient.post(@stager_url+"/done", {})
  exit0r 0
rescue => e
  fail e
end
download() click to toggle source

Download a package from the staging coordinator. We use Net::HTTP here because it supports streaming downloads.

# File lib/apcera/stager/stager.rb, line 35
def download
  uri = URI(@stager_url + "/data")

  Net::HTTP.start(uri.host.to_s, uri.port.to_s) do |http|
    request = Net::HTTP::Get.new uri.request_uri

    http.request request do |response|
      if response.code.to_i == 200
        open @pkg_path, 'wb' do |io|
          response.read_body do |chunk|
            io.write chunk
          end
        end
      else
        raise Apcera::Error::DownloadError.new("package download failed.\n")
      end
    end
  end
rescue => e
  fail e
end
environment_add(key, value) click to toggle source

Add environment variable to package.

# File lib/apcera/stager/stager.rb, line 121
def environment_add(key, value)
  response = RestClient.put(stager_meta_url, {
    :resource => "environment",
    :action => "add",
    :key => key,
    :value => value
  })
rescue => e
  fail e
end
environment_remove(key) click to toggle source

Delete environment variable from package.

# File lib/apcera/stager/stager.rb, line 133
def environment_remove(key)
  response = RestClient.put(stager_meta_url, {
    :resource => "environment",
    :action => "remove",
    :key => key
  })
rescue => e
  fail e
end
execute(cmd) click to toggle source

Execute a command in the shell. We don’t want real commands in tests.

# File lib/apcera/stager/stager.rb, line 59
def execute(cmd)
  Bundler.with_clean_env do
    result = system(cmd, @system_options)
    if !result
      raise Apcera::Error::ExecuteError.new("failed to execute: #{cmd}.\n")
    end

    result
  end
rescue => e
  fail e
end
execute_app(cmd) click to toggle source

Execute a command in the app dir. Useful helper.

# File lib/apcera/stager/stager.rb, line 73
def execute_app(cmd)
  raise_app_path_error if @app_path == nil
  Bundler.with_clean_env do
    Dir.chdir(@app_path) do |app_path|
      result = system(cmd, @system_options)
      if !result
        raise Apcera::Error::ExecuteError.new("failed to execute: #{cmd}.\n")
      end

      result
    end
  end
rescue => e
  fail e
end
exit0r(code) click to toggle source

Exit, needed for tests to not quit.

# File lib/apcera/stager/stager.rb, line 290
def exit0r(code)
  exit code
end
extract(location) click to toggle source

Extract the package to a given location.

# File lib/apcera/stager/stager.rb, line 90
def extract(location)
  @app_path = File.join(@root_path, location)
  Dir.mkdir(@app_path) unless Dir.exists?(@app_path)

  execute_app("tar -zxf #{@pkg_path}")
rescue => e
  fail e
end
fail(error = nil) click to toggle source

Fail the stager, something went wrong.

# File lib/apcera/stager/stager.rb, line 280
def fail(error = nil)
  output_error "Error: #{error.message}.\n" if error
  RestClient.post(@stager_url+"/failed", {})
rescue => e
  output_error "Error: #{e.message}.\n"
ensure
  exit0r 1
end
meta() click to toggle source

Get metadata for the package being staged.

# File lib/apcera/stager/stager.rb, line 228
def meta
  response = RestClient.get(stager_meta_url)
  return JSON.parse(response.to_s)
rescue => e
  output_error "Error: #{e.message}.\n"
  raise e
end
output(text) click to toggle source

Output to stdout

# File lib/apcera/stager/stager.rb, line 300
def output(text)
  $stdout.puts text
end
output_error(text) click to toggle source

Output to stderr

# File lib/apcera/stager/stager.rb, line 295
def output_error(text)
  $stderr.puts text
end
provides_add(type, name) click to toggle source

Add provides to package.

# File lib/apcera/stager/stager.rb, line 144
def provides_add(type, name)
  response = RestClient.put(stager_meta_url, {
    :resource => "provides",
    :action => "add",
    :type => type,
    :name => name
  })
rescue => e
  fail e
end
provides_remove(type, name) click to toggle source

Delete provides from package.

# File lib/apcera/stager/stager.rb, line 156
def provides_remove(type, name)
  response = RestClient.put(stager_meta_url, {
    :resource => "provides",
    :action => "remove",
    :type => type,
    :name => name
  })
rescue => e
  fail e
end
relaunch() click to toggle source

Tell the staging coordinator you need to relaunch.

# File lib/apcera/stager/stager.rb, line 245
def relaunch
  response = RestClient.post(@stager_url+"/relaunch", {})
  exit0r 0
rescue => e
  fail e
end
setup_chroot() click to toggle source

Setup /stagerfs chroot environment so it is ready to run commands from pulled in dependencies. This does the following:

  • Setup working resolv.conf

  • Bind mounts /proc to /stagerfs/proc

  • Recursively bind mounts /dev to /stagerfs/dev

# File lib/apcera/stager/stager.rb, line 22
def setup_chroot
  execute("sudo mkdir -p /stagerfs/etc")
  execute("sudo cp /etc/resolv.conf /stagerfs/etc/resolv.conf")

  execute("sudo mkdir -p /stagerfs/proc")
  execute("sudo mount --bind /proc /stagerfs/proc")

  execute("sudo mkdir -p /stagerfs/dev")
  execute("sudo mount --rbind /dev /stagerfs/dev")
end
snapshot() click to toggle source

Snapshot the stager filesystem for app

# File lib/apcera/stager/stager.rb, line 114
def snapshot
  response = RestClient.post(@stager_url+"/snapshot", {})
rescue => e
  fail e
end
start_command() click to toggle source

Returns the start command for the package.

# File lib/apcera/stager/stager.rb, line 260
def start_command
  self.meta["environment"]["START_COMMAND"]
end
start_command=(val) click to toggle source

Easily set the start command

# File lib/apcera/stager/stager.rb, line 265
def start_command=(val)
  self.environment_add("START_COMMAND", val)
end
start_path() click to toggle source

Returns the start path for the package.

# File lib/apcera/stager/stager.rb, line 270
def start_path
  self.meta["environment"]["START_PATH"]
end
start_path=(val) click to toggle source

Easily set the start path

# File lib/apcera/stager/stager.rb, line 275
def start_path=(val)
  self.environment_add("START_PATH", val)
end
templates_add(path, left_delimiter = "{{", right_delimiter = "}}") click to toggle source

Add template to package.

# File lib/apcera/stager/stager.rb, line 202
def templates_add(path, left_delimiter = "{{", right_delimiter = "}}")
  response = RestClient.put(stager_meta_url, {
    :resource => "templates",
    :action => "add",
    :path => path,
    :left_delimiter => left_delimiter,
    :right_delimiter => right_delimiter
  })
rescue => e
  fail e
end
templates_remove(path, left_delimiter = "{{", right_delimiter = "}}") click to toggle source

Delete template from package.

# File lib/apcera/stager/stager.rb, line 215
def templates_remove(path, left_delimiter = "{{", right_delimiter = "}}")
  response = RestClient.put(stager_meta_url, {
    :resource => "templates",
    :action => "remove",
    :path => path,
    :left_delimiter => left_delimiter,
    :right_delimiter => right_delimiter
  })
rescue => e
  fail e
end
upload() click to toggle source

Upload the new package to the staging coordinator

# File lib/apcera/stager/stager.rb, line 100
def upload
  raise_app_path_error if @app_path == nil
  app_dir = Pathname.new(@app_path).relative_path_from(Pathname.new(@root_path)).to_s
  execute_app("cd #{app_path}/.. && tar czf #{@updated_pkg_path} #{app_dir}")

  sha256 = Digest::SHA256.file(@updated_pkg_path)
  File.open(@updated_pkg_path, "rb") do |f|
    response = RestClient.post(@stager_url+"/data?sha256=#{sha256.to_s}", f, { :content_type => "application/octet-stream" } )
  end
rescue => e
  fail e
end

Private Instance Methods

raise_app_path_error() click to toggle source
# File lib/apcera/stager/stager.rb, line 306
def raise_app_path_error
  raise Apcera::Error::AppPathError.new("app path not set, please run extract!\n")
end
setup_environment() click to toggle source
# File lib/apcera/stager/stager.rb, line 310
def setup_environment
  # When staging we use the root path. These are overridden in tests.
  @root_path = "/tmp"
  @pkg_path = File.join(@root_path, PKG_NAME)
  @updated_pkg_path = File.join(@root_path, UPDATED_PKG_NAME)
  @system_options = {}
end
stager_meta_url() click to toggle source
# File lib/apcera/stager/stager.rb, line 318
def stager_meta_url
  @stager_url + "/meta"
end