Class: Apcera::Stager

Inherits:
Object
  • Object
show all
Defined in:
lib/apcera/stager/stager.rb

Constant Summary collapse

PKG_NAME =
"pkg.tar.gz"
UPDATED_PKG_NAME =
"updated.tar.gz"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Stager

Returns a new instance of Stager.



8
9
10
11
12
13
14
15
# 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

Instance Attribute Details

#app_pathObject

Returns the value of attribute app_path.



3
4
5
# File 'lib/apcera/stager/stager.rb', line 3

def app_path
  @app_path
end

#pkg_pathObject

Returns the value of attribute pkg_path.



3
4
5
# File 'lib/apcera/stager/stager.rb', line 3

def pkg_path
  @pkg_path
end

#root_pathObject

Returns the value of attribute root_path.



3
4
5
# File 'lib/apcera/stager/stager.rb', line 3

def root_path
  @root_path
end

#stager_urlObject

Returns the value of attribute stager_url.



3
4
5
# File 'lib/apcera/stager/stager.rb', line 3

def stager_url
  @stager_url
end

#system_optionsObject

Returns the value of attribute system_options.



3
4
5
# File 'lib/apcera/stager/stager.rb', line 3

def system_options
  @system_options
end

#updated_pkg_pathObject

Returns the value of attribute updated_pkg_path.



3
4
5
# File 'lib/apcera/stager/stager.rb', line 3

def updated_pkg_path
  @updated_pkg_path
end

Instance Method Details

#completeObject

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



238
239
240
241
# File 'lib/apcera/stager/stager.rb', line 238

def complete
  upload
  done
end

#dependencies_add(type, name) ⇒ Object

Add dependencies to package.



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/apcera/stager/stager.rb', line 152

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) ⇒ Object

Delete dependencies from package.



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/apcera/stager/stager.rb', line 169

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

#doneObject

Tell the staging coordinator you are done.



221
222
223
224
225
226
# File 'lib/apcera/stager/stager.rb', line 221

def done
  response = RestClient.post(@stager_url+"/done", {})
  exit0r 0
rescue => e
  fail e
end

#downloadObject

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



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/apcera/stager/stager.rb', line 19

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) ⇒ Object

Add environment variable to package.



105
106
107
108
109
110
111
112
113
114
# File 'lib/apcera/stager/stager.rb', line 105

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) ⇒ Object

Delete environment variable from package.



117
118
119
120
121
122
123
124
125
# File 'lib/apcera/stager/stager.rb', line 117

def environment_remove(key)
  response = RestClient.put(stager_meta_url, {
    :resource => "environment",
    :action => "remove",
    :key => key
  })
rescue => e
  fail e
end

#execute(cmd) ⇒ Object

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



43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/apcera/stager/stager.rb', line 43

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) ⇒ Object

Execute a command in the app dir. Useful helper.



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/apcera/stager/stager.rb', line 57

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) ⇒ Object

Exit, needed for tests to not quit.



274
275
276
# File 'lib/apcera/stager/stager.rb', line 274

def exit0r(code)
  exit code
end

#extract(location) ⇒ Object

Extract the package to a given location.



74
75
76
77
78
79
80
81
# File 'lib/apcera/stager/stager.rb', line 74

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) ⇒ Object

Fail the stager, something went wrong.



264
265
266
267
268
269
270
271
# File 'lib/apcera/stager/stager.rb', line 264

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

#metaObject

Get metadata for the package being staged.



212
213
214
215
216
217
218
# File 'lib/apcera/stager/stager.rb', line 212

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) ⇒ Object

Output to stdout



284
285
286
# File 'lib/apcera/stager/stager.rb', line 284

def output(text)
  $stdout.puts text
end

#output_error(text) ⇒ Object

Output to stderr



279
280
281
# File 'lib/apcera/stager/stager.rb', line 279

def output_error(text)
  $stderr.puts text
end

#provides_add(type, name) ⇒ Object

Add provides to package.



128
129
130
131
132
133
134
135
136
137
# File 'lib/apcera/stager/stager.rb', line 128

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(key, value) ⇒ Object

Delete provides from package.



140
141
142
143
144
145
146
147
148
149
# File 'lib/apcera/stager/stager.rb', line 140

def provides_remove(key, value)
  response = RestClient.put(stager_meta_url, {
    :resource => "provides",
    :action => "remove",
    :type => type,
    :name => name
  })
rescue => e
  fail e
end

#relaunchObject

Tell the staging coordinator you need to relaunch.



229
230
231
232
233
234
# File 'lib/apcera/stager/stager.rb', line 229

def relaunch
  response = RestClient.post(@stager_url+"/relaunch", {})
  exit0r 0
rescue => e
  fail e
end

#snapshotObject

Snapshot the stager filesystem for app



98
99
100
101
102
# File 'lib/apcera/stager/stager.rb', line 98

def snapshot
  response = RestClient.post(@stager_url+"/snapshot", {})
rescue => e
  fail e
end

#start_commandObject

Returns the start command for the package.



244
245
246
# File 'lib/apcera/stager/stager.rb', line 244

def start_command
  self.meta["environment"]["START_COMMAND"]
end

#start_command=(val) ⇒ Object

Easily set the start command



249
250
251
# File 'lib/apcera/stager/stager.rb', line 249

def start_command=(val)
  self.environment_add("START_COMMAND", val)
end

#start_pathObject

Returns the start path for the package.



254
255
256
# File 'lib/apcera/stager/stager.rb', line 254

def start_path
  self.meta["environment"]["START_PATH"]
end

#start_path=(val) ⇒ Object

Easily set the start path



259
260
261
# File 'lib/apcera/stager/stager.rb', line 259

def start_path=(val)
  self.environment_add("START_PATH", val)
end

#templates_add(path, left_delimiter = "{{", right_delimiter = "}}") ⇒ Object

Add template to package.



186
187
188
189
190
191
192
193
194
195
196
# File 'lib/apcera/stager/stager.rb', line 186

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 = "}}") ⇒ Object

Delete template from package.



199
200
201
202
203
204
205
206
207
208
209
# File 'lib/apcera/stager/stager.rb', line 199

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

#uploadObject

Upload the new package to the staging coordinator



84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/apcera/stager/stager.rb', line 84

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}")

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