continuum-stager-api 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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