class Apcera::Stager
Constants
- PKG_NAME
- UPDATED_PKG_NAME
Attributes
Public Class Methods
# 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
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
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
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
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 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
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
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 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 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
Exit, needed for tests to not quit.
# File lib/apcera/stager/stager.rb, line 290 def exit0r(code) exit code end
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 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
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 to stdout
# File lib/apcera/stager/stager.rb, line 300 def output(text) $stdout.puts text end
Output to stderr
# File lib/apcera/stager/stager.rb, line 295 def output_error(text) $stderr.puts text end
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
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
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 /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 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
Returns the start command for the package.
# File lib/apcera/stager/stager.rb, line 260 def start_command self.meta["environment"]["START_COMMAND"] end
Easily set the start command
# File lib/apcera/stager/stager.rb, line 265 def start_command=(val) self.environment_add("START_COMMAND", val) end
Returns the start path for the package.
# File lib/apcera/stager/stager.rb, line 270 def start_path self.meta["environment"]["START_PATH"] end
Easily set the start path
# File lib/apcera/stager/stager.rb, line 275 def start_path=(val) self.environment_add("START_PATH", val) end
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
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 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
# 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
# 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
# File lib/apcera/stager/stager.rb, line 318 def stager_meta_url @stager_url + "/meta" end