continuum-stager-api 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +22 -0
  3. data/.travis.yml +15 -0
  4. data/CHANGELOG.md +16 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE +21 -0
  7. data/README.md +37 -0
  8. data/Rakefile +7 -0
  9. data/apcera-stager-api-contrib.gemspec +25 -0
  10. data/lib/apcera/stager/error.rb +6 -0
  11. data/lib/apcera/stager/loader.rb +10 -0
  12. data/lib/apcera/stager/stager.rb +270 -0
  13. data/lib/continuum-stager-api.rb +1 -0
  14. data/spec/apcera/stager/stager_spec.rb +496 -0
  15. data/spec/fixtures/cassettes/complete.yml +55 -0
  16. data/spec/fixtures/cassettes/dependencies_add.yml +53 -0
  17. data/spec/fixtures/cassettes/dependencies_remove.yml +53 -0
  18. data/spec/fixtures/cassettes/done.yml +28 -0
  19. data/spec/fixtures/cassettes/download.yml +37400 -0
  20. data/spec/fixtures/cassettes/environment_add.yml +53 -0
  21. data/spec/fixtures/cassettes/environment_remove.yml +53 -0
  22. data/spec/fixtures/cassettes/fail.yml +28 -0
  23. data/spec/fixtures/cassettes/invalid/complete.yml +55 -0
  24. data/spec/fixtures/cassettes/invalid/dependencies_add.yml +53 -0
  25. data/spec/fixtures/cassettes/invalid/dependencies_remove.yml +53 -0
  26. data/spec/fixtures/cassettes/invalid/done.yml +55 -0
  27. data/spec/fixtures/cassettes/invalid/download.yml +54 -0
  28. data/spec/fixtures/cassettes/invalid/environment_add.yml +53 -0
  29. data/spec/fixtures/cassettes/invalid/environment_remove.yml +53 -0
  30. data/spec/fixtures/cassettes/invalid/fail.yml +28 -0
  31. data/spec/fixtures/cassettes/invalid/metadata.yml +28 -0
  32. data/spec/fixtures/cassettes/invalid/provides_add.yml +53 -0
  33. data/spec/fixtures/cassettes/invalid/provides_remove.yml +53 -0
  34. data/spec/fixtures/cassettes/invalid/relaunch.yml +53 -0
  35. data/spec/fixtures/cassettes/invalid/snapshot.yml +53 -0
  36. data/spec/fixtures/cassettes/invalid/templates_add.yml +53 -0
  37. data/spec/fixtures/cassettes/invalid/templates_remove.yml +53 -0
  38. data/spec/fixtures/cassettes/invalid/upload.yml +55 -0
  39. data/spec/fixtures/cassettes/metadata.yml +51 -0
  40. data/spec/fixtures/cassettes/provides_add.yml +53 -0
  41. data/spec/fixtures/cassettes/provides_remove.yml +116 -0
  42. data/spec/fixtures/cassettes/relaunch.yml +28 -0
  43. data/spec/fixtures/cassettes/snapshot.yml +28 -0
  44. data/spec/fixtures/cassettes/templates_add.yml +53 -0
  45. data/spec/fixtures/cassettes/templates_remove.yml +53 -0
  46. data/spec/fixtures/cassettes/upload.yml +30 -0
  47. data/spec/spec_helper.rb +31 -0
  48. metadata +189 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MWRiOTA4YWU1ZGNlZDA4YzI4MTMwMjRmYjA2ZjlmYTZiYjMxOTZlMg==
5
+ data.tar.gz: !binary |-
6
+ ZDE3NWEwODI5MGIyNjMyODcyYTNmNDU4NjZmNmI5ZmM4NWY2ODkzOA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ MjUwY2Q0YzVkYzc4MTYxYTVmMTMwNGUxYzIxNGIzMzZiZjQ4MzVmM2JmMjAw
10
+ ODFhNTJmOTE2Yzc4YmMzNjMyMGRmOTUzZjlkYTY2NjIxYWVlZjJiOWIxZjRk
11
+ ZGRlODlmZDJmZDVmOTU0YjlhZmQ3ODU4YzQwZDlhMWQzYjUzNzM=
12
+ data.tar.gz: !binary |-
13
+ MjQwMzdkODU0YzI3NGQxNTIwMzgxZmRmYWM5NzcyMDY2Mjk4ZTk1MDNkODgx
14
+ OGM0MjBlYzI5Y2NlYzdkZTA5ZTI5NmM0ODNkODczMTVmNjVkM2RmYWU2OGYy
15
+ ZWUwMjk0NTI2OTZjY2VhZjVkOTExMWQ2OTc5NGU2MjUwYzUwOGU=
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .ruby-gemset
7
+ .ruby-version
8
+ .rvmrc
9
+ Gemfile.lock
10
+ InstalledFiles
11
+ _yardoc
12
+ coverage
13
+ doc/
14
+ lib/bundler/man
15
+ pkg
16
+ rdoc
17
+ spec/reports
18
+ test/tmp
19
+ test/version_tmp
20
+ tmp
21
+ .powenv
22
+ .idea/
data/.travis.yml ADDED
@@ -0,0 +1,15 @@
1
+ before_install:
2
+ - gem update --system 2.1.11
3
+ language: ruby
4
+ rvm:
5
+ - "1.8.7"
6
+ - "1.9.2"
7
+ - "1.9.3"
8
+ - "2.0.0"
9
+ - "2.1.0"
10
+ - "rbx"
11
+ - "jruby"
12
+ matrix:
13
+ allow_failures:
14
+ - rvm: "rbx"
15
+ - rvm: "jruby"
data/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ ## 0.0.1 Initial version
5
+
6
+ ### Added
7
+ - Nothing.
8
+
9
+ ### Deprecated
10
+ - Nothing.
11
+
12
+ ### Removed
13
+ - Nothing.
14
+
15
+ ### Fixed
16
+ - Nothing.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Apcera
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # continuum-stager-api
2
+
3
+ Simple gem to assist users writing custom Continuum Stagers.
4
+
5
+ ## Installation
6
+
7
+ Add to your `Gemfile`:
8
+
9
+ ```ruby
10
+ gem "continuum-stager-api"
11
+ ```
12
+
13
+ Then `bundle install`.
14
+
15
+ ## License
16
+
17
+ The MIT License (MIT)
18
+
19
+ Copyright (c) 2014 Apcera
20
+
21
+ Permission is hereby granted, free of charge, to any person obtaining a copy
22
+ of this software and associated documentation files (the "Software"), to deal
23
+ in the Software without restriction, including without limitation the rights
24
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
25
+ copies of the Software, and to permit persons to whom the Software is
26
+ furnished to do so, subject to the following conditions:
27
+
28
+ The above copyright notice and this permission notice shall be included in all
29
+ copies or substantial portions of the Software.
30
+
31
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37
+ SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ require File.join('bundler', 'gem_tasks')
3
+ require File.join('rspec', 'core', 'rake_task')
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ Gem::Specification.new do |gem|
3
+ gem.add_dependency "rest-client"
4
+ gem.add_dependency 'json'
5
+
6
+ gem.authors = ["Josh Ellithorpe"]
7
+ gem.email = ["josh@apcera.com"]
8
+ gem.description = %q{Continuum Stager api library}
9
+ gem.summary = %q{Continuum Stager api library which makes it super easy to write stagers for Apcera's Continuum.}
10
+ gem.homepage = "http://apcera.com"
11
+ gem.license = "MIT"
12
+
13
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
+ gem.files = `git ls-files`.split("\n")
15
+ gem.test_files = `git ls-files -- {spec}/*`.split("\n")
16
+ gem.name = "continuum-stager-api"
17
+ gem.require_paths = ["lib"]
18
+ gem.version = "0.1.0"
19
+
20
+ gem.add_development_dependency 'rspec', '~> 2.6.0'
21
+ gem.add_development_dependency 'rake'
22
+ gem.add_development_dependency 'webmock', '1.11'
23
+ gem.add_development_dependency 'simplecov'
24
+ gem.add_development_dependency 'vcr'
25
+ end
@@ -0,0 +1,6 @@
1
+ module Apcera
2
+ module Error
3
+ class StagerURLRequired < StandardError; end
4
+ class ExecuteError < StandardError; end
5
+ end
6
+ end
@@ -0,0 +1,10 @@
1
+ require "bundler"
2
+ Bundler.setup
3
+ require 'rest-client'
4
+ require 'json'
5
+
6
+ base = File.expand_path File.dirname(__FILE__)
7
+
8
+ Dir[File.join(base, '*.rb')].each do |file|
9
+ require file
10
+ end
@@ -0,0 +1,270 @@
1
+ module Apcera
2
+ class Stager
3
+ attr_accessor :stager_url, :app_path, :root_path, :pkg_path, :updated_pkg_path, :system_options
4
+
5
+ PKG_NAME = "pkg.tar.gz"
6
+ UPDATED_PKG_NAME = "updated.tar.gz"
7
+
8
+ def initialize(options = {})
9
+ # Require stager url. Needed to talk to the Staging Coordinator.
10
+ @stager_url = options[:stager_url] || ENV["STAGER_URL"]
11
+ raise Apcera::Error::StagerURLRequired.new("stager_url required") unless @stager_url
12
+
13
+ # Setup the environment, some test items here.
14
+ setup_environment
15
+ end
16
+
17
+ # Download a package from the staging coordinator.
18
+ def download
19
+ response = RestClient.get(@stager_url + "/data")
20
+ File.open(@pkg_path, "wb") do |f|
21
+ f.write(response.to_str)
22
+ end
23
+ rescue => e
24
+ fail e
25
+ end
26
+
27
+ # Execute a command in the shell.
28
+ # We don't want real commands in tests.
29
+ def execute(cmd)
30
+ Bundler.with_clean_env do
31
+ result = system(cmd, @system_options)
32
+ if !result
33
+ raise Apcera::Error::ExecuteError.new("failed to execute: #{cmd}.\n")
34
+ end
35
+
36
+ result
37
+ end
38
+ rescue => e
39
+ fail e
40
+ end
41
+
42
+ # Execute a command in the app dir. Useful helper.
43
+ def execute_app(cmd)
44
+ Bundler.with_clean_env do
45
+ Dir.chdir(@app_path) do |app_path|
46
+ result = system(cmd, @system_options)
47
+ if !result
48
+ raise Apcera::Error::ExecuteError.new("failed to execute: #{cmd}.\n")
49
+ end
50
+
51
+ result
52
+ end
53
+ end
54
+ rescue => e
55
+ fail e
56
+ end
57
+
58
+ # Extract the package to a given location.
59
+ def extract(location)
60
+ @app_path = File.join(@root_path, location)
61
+ Dir.mkdir(@app_path) unless Dir.exists?(@app_path)
62
+
63
+ execute_app("tar -zxf #{@pkg_path}")
64
+ rescue => e
65
+ fail e
66
+ end
67
+
68
+ # Upload the new package to the staging coordinator
69
+ def upload
70
+ execute_app("tar czf #{@updated_pkg_path} .")
71
+
72
+ sha1 = Digest::SHA1.file(@updated_pkg_path)
73
+ File.open(@updated_pkg_path, "rb") do |f|
74
+ response = RestClient.post(@stager_url+"/data?sha1=#{sha1.to_s}", f.read, { :content_type => "application/octet-stream" } )
75
+ end
76
+ rescue => e
77
+ fail e
78
+ end
79
+
80
+ # Snapshot the stager filesystem for app
81
+ def snapshot
82
+ response = RestClient.post(@stager_url+"/snapshot", {})
83
+ rescue => e
84
+ fail e
85
+ end
86
+
87
+ # Add environment variable to package.
88
+ def environment_add(key, value)
89
+ response = RestClient.post(@stager_url+"/meta", {
90
+ :resource => "environment",
91
+ :action => "add",
92
+ :key => key,
93
+ :value => value
94
+ })
95
+ rescue => e
96
+ fail e
97
+ end
98
+
99
+ # Delete environment variable from package.
100
+ def environment_remove(key, value)
101
+ response = RestClient.post(@stager_url+"/meta", {
102
+ :resource => "environment",
103
+ :action => "remove",
104
+ :key => key,
105
+ :value => value
106
+ })
107
+ rescue => e
108
+ fail e
109
+ end
110
+
111
+ # Add provides to package.
112
+ def provides_add(type, name)
113
+ response = RestClient.post(@stager_url+"/meta", {
114
+ :resource => "provides",
115
+ :action => "add",
116
+ :type => type,
117
+ :name => name
118
+ })
119
+ rescue => e
120
+ fail e
121
+ end
122
+
123
+ # Delete provides from package.
124
+ def provides_remove(key, value)
125
+ response = RestClient.post(@stager_url+"/meta", {
126
+ :resource => "provides",
127
+ :action => "remove",
128
+ :type => type,
129
+ :name => name
130
+ })
131
+ rescue => e
132
+ fail e
133
+ end
134
+
135
+ # Add dependencies to package.
136
+ def dependencies_add(type, name)
137
+ response = RestClient.post(@stager_url+"/meta", {
138
+ :resource => "dependencies",
139
+ :action => "add",
140
+ :type => type,
141
+ :name => name
142
+ })
143
+ rescue => e
144
+ fail e
145
+ end
146
+
147
+ # Delete dependencies from package.
148
+ def dependencies_remove(type, name)
149
+ response = RestClient.post(@stager_url+"/meta", {
150
+ :resource => "dependencies",
151
+ :action => "remove",
152
+ :type => type,
153
+ :name => name
154
+ })
155
+ rescue => e
156
+ fail e
157
+ end
158
+
159
+ # Add template to package.
160
+ def templates_add(path, left_delimiter = "{{", right_delimiter = "}}")
161
+ response = RestClient.post(@stager_url+"/meta", {
162
+ :resource => "templates",
163
+ :action => "add",
164
+ :path => path,
165
+ :left_delimiter => left_delimiter,
166
+ :right_delimiter => right_delimiter
167
+ })
168
+ rescue => e
169
+ fail e
170
+ end
171
+
172
+ # Delete template from package.
173
+ def templates_remove(path, left_delimiter = "{{", right_delimiter = "}}")
174
+ response = RestClient.post(@stager_url+"/meta", {
175
+ :resource => "templates",
176
+ :action => "remove",
177
+ :path => path,
178
+ :left_delimiter => left_delimiter,
179
+ :right_delimiter => right_delimiter
180
+ })
181
+ rescue => e
182
+ fail e
183
+ end
184
+
185
+ # Get metadata for the package being staged.
186
+ def meta
187
+ response = RestClient.get(@stager_url+"/meta")
188
+ return JSON.parse(response.to_s)
189
+ rescue => e
190
+ output_error "Error: #{e.message}.\n"
191
+ raise e
192
+ end
193
+
194
+ # Tell the staging coordinator you are done.
195
+ def done
196
+ response = RestClient.post(@stager_url+"/done", {})
197
+ exit0r 0
198
+ rescue => e
199
+ fail e
200
+ end
201
+
202
+ # Tell the staging coordinator you need to relaunch.
203
+ def relaunch
204
+ response = RestClient.post(@stager_url+"/relaunch", {})
205
+ exit0r 0
206
+ rescue => e
207
+ fail e
208
+ end
209
+
210
+ # Finish staging, compress your app dir and send to the staging coordinator.
211
+ # Then tell the staging coordinator we are done.
212
+ def complete
213
+ upload
214
+ done
215
+ end
216
+
217
+ # Returns the start command for the package.
218
+ def start_command
219
+ self.meta["environment"]["START_COMMAND"]
220
+ end
221
+
222
+ # Easily set the start command
223
+ def start_command=(val)
224
+ self.environment_add("START_COMMAND", val)
225
+ end
226
+
227
+ # Returns the start path for the package.
228
+ def start_path
229
+ self.meta["environment"]["START_PATH"]
230
+ end
231
+
232
+ # Easily set the start path
233
+ def start_path=(val)
234
+ self.environment_add("START_PATH", val)
235
+ end
236
+
237
+ # Fail the stager, something went wrong.
238
+ def fail(error = nil)
239
+ output_error "Error: #{error.message}.\n" if error
240
+ RestClient.post(@stager_url+"/failed", {})
241
+ rescue => e
242
+ output_error "Error: #{e.message}.\n"
243
+ ensure
244
+ exit0r 1
245
+ end
246
+
247
+ # Exit, needed for tests to not quit.
248
+ def exit0r(code)
249
+ exit code
250
+ end
251
+
252
+ private
253
+
254
+ def output_error(text)
255
+ $stderr.puts text
256
+ end
257
+
258
+ def output(text)
259
+ $stdout.puts text
260
+ end
261
+
262
+ def setup_environment
263
+ # When staging we use the root path. These are overridden in tests.
264
+ @root_path = "/"
265
+ @pkg_path = File.join(@root_path, PKG_NAME)
266
+ @updated_pkg_path = File.join(@root_path, UPDATED_PKG_NAME)
267
+ @system_options = {}
268
+ end
269
+ end
270
+ end