biosphere 0.0.14 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ca246e427a415272896db19c4a1ba36cba31e831
4
- data.tar.gz: 13934dc8a5d8655c30df4147db315659ce5c53e5
3
+ metadata.gz: 8a63904122980f99e791afdbd4e49ad165ef26ac
4
+ data.tar.gz: d7d5ea118f8321eb6cac9173d633b2bd5ca49522
5
5
  SHA512:
6
- metadata.gz: ed071e46536020e52696368e6e7e994745144229d873eeac4ce68e1c0aa909df7d07b91169da69ad2de506e38a8cb27d0169b62ea91a8748ad7babe0b74355cd
7
- data.tar.gz: 81b5e1dfdf363a8ecbfb58e0086e8759966cd26a7ad17d61b467ac42cf52f880aff29b28a398b4f3faf4a8462952304a434257c8a936b4cacc5fb16deaf9a2a3
6
+ metadata.gz: 4b3666b2efec558ca7d2d29356d8194078534dc777bbe02d26aae34a7db284d900b0e6ae96e2e0cb419cebaa3996f06baa78dc72de6e7f0f83020bc097461da0
7
+ data.tar.gz: 1af9536e8c8cbe9b0ebed3e93fbf0e2be42b6333640dc73ef56ac60fecb5aadd501998e5b09bd2443642c62c87b270d1f779c5a6d23a2e20061047ad3b5f3c7a
data/bin/biosphere CHANGED
@@ -10,163 +10,224 @@ require 'biosphere/s3.rb'
10
10
 
11
11
  class BiosphereOpts
12
12
 
13
- def self.normalize_path(path)
14
- if path[-1] != '/'
15
- path += '/'
16
- end
17
- return path
18
- end
13
+ def self.parse(args)
19
14
 
20
- def self.parse(args)
15
+ options = OpenStruct.new
16
+ options.build_dir = "build"
17
+ options.src = "./"
18
+ options.version = ::Biosphere::Version
21
19
 
22
- options = OpenStruct.new
23
- options.build_dir = "build/"
24
- options.src = "./"
25
- options.version = ::Biosphere::Version
20
+ opt_parser = OptionParser.new do |opts|
26
21
 
27
- opt_parser = OptionParser.new do |opts|
28
22
 
23
+ opts.banner = "Usage: \"biosphere [options] <action>\""
29
24
 
30
- opts.banner = "Usage: \"biosphere [options] <action>\""
25
+ opts.separator ""
26
+ opts.separator "Commands:"
27
+ opts.separator "\tbuild\tWrite tf files as json into build directory"
28
+ opts.separator "\tplan\tRun the planning phase"
29
+ opts.separator "\tcommit\tCommit changes and update the infrastructure"
30
+ opts.separator "\tlock\tAcquire lock for remote state"
31
+ opts.separator "\tunlock\tRelease lock for remote state"
32
+ opts.separator "\taction [action]\tCall an action defined in the application .rb files"
33
+ opts.separator ""
31
34
 
32
- opts.separator ""
33
- opts.separator "Commands:"
34
- opts.separator "\tbuild\tWrite tf files as json into build directory"
35
- opts.separator "\tplan\tRun the planning phase"
36
- opts.separator "\tcommit\tCommit changes and update the infrastructure"
37
- opts.separator "\tlock\tAcquire lock for remote state"
38
- opts.separator "\tunlock\tRelease lock for remote state"
39
- opts.separator "\taction [action]\tCall an action defined in the application .rb files"
40
- opts.separator ""
35
+ opts.on_tail("-h", "--help", "Show this message") do
36
+ puts opts
37
+ exit
38
+ end
41
39
 
42
- opts.on_tail("-h", "--help", "Show this message") do
43
- puts opts
44
- exit
45
- end
40
+ opts.on("--src PATH", "Directory where the application .rb files are") do |path|
41
+ options.src = path
42
+ end
46
43
 
47
- opts.on("--src PATH", "Directory where the application .rb files are") do |path|
48
- options.src = normalize_path(path)
49
- end
44
+ opts.on("--build-dir PATH", "Directory where to build json files") do |path|
45
+ options.build_dir = path
46
+ end
50
47
 
51
- opts.on("--build-dir PATH", "Directory where to build json files") do |path|
52
- options.build_dir = normalize_path(path)
53
- end
48
+ opts.on_tail("-v", "--version", "Show version") do
49
+ puts options.version
50
+ exit
51
+ end
54
52
 
55
- end
53
+ end
56
54
 
57
- opt_parser.parse!(args)
58
- options
59
- end
55
+ opt_parser.parse!(args)
56
+ options
57
+ end
60
58
 
61
59
  end
62
60
 
63
61
  if !STDOUT.isatty
64
- String.disable_colorization true
62
+ String.disable_colorization true
65
63
  end
66
64
 
67
65
  options = BiosphereOpts.parse(ARGV)
68
66
 
69
67
  if ARGV.length == 0
70
- STDERR.puts "No action spesified. Use -h to get help."
71
- exit -1
68
+ STDERR.puts "No action spesified. Use -h to get help."
69
+ exit -1
72
70
  end
73
71
 
74
72
  if !File.directory?(options.src)
75
- STDERR.puts "Directory #{options.build_dir} is not a directory or it doesn't exists."
76
- exit -1
73
+ STDERR.puts "Directory #{options.build_dir} is not a directory or it doesn't exists."
74
+ exit -1
77
75
  end
78
76
 
79
- if options.src
80
- suite = Biosphere::Suite.new(options.src)
81
- if options.src == "./"
82
- STDERR.puts "Loading suite from current directory (#{File.expand_path(options.src)}). Use --src to change the path"
83
- end
84
-
85
- if suite.load_all() == 0
86
- STDERR.puts "No files found. Are you in the right directory where your biosphere .rb files are?"
87
- exit -1
88
- end
89
-
90
- if suite.node[:settings][:s3_bucket].nil? || suite.node[:settings][:s3_bucket].empty? || suite.node[:settings][:cluster_name].nil? || suite.node[:settings][:cluster_name].empty?
91
- puts "\nNo S3 bucket or cluster name defined in configuration, can't continue"
92
- exit 1
93
- end
94
- s3 = S3.new(suite.node[:settings][:s3_bucket], suite.node[:settings][:cluster_name])
77
+ if options.build_dir
78
+ if !File.directory?(options.build_dir)
79
+ STDERR.puts "Creating build directory #{options.build_dir} because it was missing"
80
+ Dir.mkdir(options.build_dir)
81
+ end
95
82
  end
96
83
 
97
- if options.build_dir
98
- if !File.directory?(options.build_dir)
99
- STDERR.puts "Creating build directory #{options.build_dir} because it was missing"
100
- Dir.mkdir(options.build_dir)
101
- end
84
+ if options.src
85
+ state = Biosphere::State.new
86
+ suite = Biosphere::Suite.new(state)
87
+
88
+ if options.src == "./"
89
+ STDERR.puts "Loading suite from current directory (#{File.expand_path(options.src)}). Use --src to change the path"
90
+ end
91
+
92
+ if suite.load_all(options.src) == 0
93
+ STDERR.puts "No files found. Are you in the right directory where your biosphere .rb files are?"
94
+ exit -1
95
+ end
96
+
97
+ if suite.biosphere_settings[:s3_bucket].nil? || suite.biosphere_settings[:s3_bucket].empty? ||
98
+ suite.biosphere_settings[:state_name].nil? || suite.biosphere_settings[:state_name].empty?
99
+ puts "\nNo S3 bucket or cluster name defined in configuration, can't continue"
100
+ exit 1
101
+ end
102
+ s3 = S3.new(suite.biosphere_settings[:s3_bucket], suite.biosphere_settings[:state_name])
103
+ s3.retrieve("#{options.build_dir}/state.node")
104
+
105
+ # This will update the state which is already passed to the suite.
106
+ state.filename = "#{options.build_dir}/state.node"
107
+ if File.exists?(state.filename)
108
+ puts "Loading state from #{state.filename}"
109
+ state.load()
110
+ end
111
+
102
112
  end
103
113
 
104
114
  if ARGV[0] == "build" && options.src
105
- suite.evaluate_resources()
115
+ suite.evaluate_resources()
106
116
 
107
- if !File.directory?(options.build_dir)
108
- STDERR.puts "Directory #{options.build_dir} is not a directory or it doesn't exists."
109
- exit -1
110
- end
117
+ if !File.directory?(options.build_dir)
118
+ STDERR.puts "Directory #{options.build_dir} is not a directory or it doesn't exists."
119
+ exit -1
120
+ end
111
121
 
112
- count = 0
113
- suite.write_json_to(options.build_dir) do |file_name, destination, str, proxy|
114
- puts "Wrote #{str.length} bytes from #{file_name} to #{destination} (#{proxy.export["resource"].length} resources)"
115
- count = count + 1
116
- end
122
+ count = 0
123
+ suite.write_json_to(options.build_dir) do |file_name, destination, str, deployment|
124
+ puts "Wrote #{str.length} bytes from #{file_name} to #{destination} (#{deployment.export["resource"].length} resources)"
125
+ count = count + 1
126
+ end
117
127
 
118
- puts "Wrote #{count} files into #{options.build_dir}"
119
- suite.save_node()
128
+ puts "Wrote #{count} files into #{options.build_dir}"
129
+ state.save()
130
+ s3.save("#{options.build_dir}/state.node")
120
131
 
121
132
  elsif ARGV[0] == "plan" && options.src
122
- suite.evaluate_plans()
123
- ap suite.node, :indent=>-4
133
+ suite.evaluate_plans()
134
+ ap suite.node, :indent=>-4
135
+
136
+ elsif ARGV[0] == "state" && options.src
137
+ ap suite.state.node, :indent=>-4
124
138
 
125
139
  elsif ARGV[0] == "action" && options.src
126
- s3.retrieve("#{options.build_dir}terraform.tfstate")
127
- s3.retrieve("#{options.build_dir}state.node")
128
- context = Biosphere::ActionContext.new()
129
- context.build_directory = options.build_dir
140
+ context = Biosphere::ActionContext.new()
141
+ context.build_directory = options.build_dir
130
142
 
131
- if suite.call_action(ARGV[1], context)
132
- STDERR.puts "Executing action #{ARGV[1]}"
133
- else
134
- STDERR.puts "Could not find action #{ARGV[1]}"
135
- end
143
+ if suite.call_action(ARGV[1], context)
144
+ STDERR.puts "Executing action #{ARGV[1]}"
145
+ else
146
+ STDERR.puts "Could not find action #{ARGV[1]}"
147
+ end
148
+ state.save()
149
+ s3.save("#{options.build_dir}/state.node")
136
150
 
137
- suite.save_node()
151
+ elsif ARGV[0] == "deployment" && options.src
152
+
153
+ suite.deployments.each do |name, deployment|
154
+ puts "Deployment: #{name}"
155
+ end
138
156
 
139
157
  elsif ARGV[0] == "commit" && options.src
140
- s3.set_lock()
141
- s3.retrieve("#{options.build_dir}terraform.tfstate")
142
- tf_plan = %x( terraform plan -state=#{options.build_dir}terraform.tfstate #{options.build_dir} )
143
- puts "\n" + tf_plan
144
- answer = ""
145
- while answer.empty? || (answer != "y" && answer != "n")
146
- print "\nDoes the plan look reasonable? (Answering yes will apply the changes) y/n: "
147
- answer = STDIN.gets.chomp
148
- end
149
-
150
- if answer == "n"
151
- puts "\nOk, will not proceed with commit"
152
- elsif answer == "y"
153
- puts "\nApplying the changes (this may take several minutes)"
154
- tf_apply = %x( terraform apply -state=#{options.build_dir}terraform.tfstate #{options.build_dir})
155
- puts "\n" + tf_apply
156
- s3.save("#{options.build_dir}terraform.tfstate")
157
- s3.save("#{options.src}state.node")
158
- end
159
-
160
- s3.release_lock()
158
+
159
+ if !ARGV[1]
160
+ puts "Please specify deployment name as the second parameter."
161
+ puts "Available deployments:"
162
+ suite.deployments.each do |name, deployment|
163
+ puts "\t#{name}"
164
+ end
165
+ exit -1
166
+ end
167
+ deployment = ARGV[1]
168
+
169
+ s3.set_lock()
170
+ s3.retrieve("#{options.build_dir}/#{deployment}.tfstate")
171
+ tf_plan = %x( terraform plan -state=#{options.build_dir}/#{deployment}.tfstate #{options.build_dir} )
172
+ puts "\n" + tf_plan
173
+ answer = ""
174
+ while answer.empty? || (answer != "y" && answer != "n")
175
+ print "\nDoes the plan look reasonable? (Answering yes will apply the changes) y/n: "
176
+ answer = STDIN.gets.chomp
177
+ end
178
+
179
+ if answer == "n"
180
+ puts "\nOk, will not proceed with commit"
181
+ elsif answer == "y"
182
+ puts "\nApplying the changes (this may take several minutes)"
183
+ tf_apply = %x( terraform apply -state=#{options.build_dir}/#{deployment}.tfstate #{options.build_dir})
184
+ puts "\n" + tf_apply
185
+ s3.save("#{options.build_dir}/#{deployment}.tfstate")
186
+ s3.save("#{options.build_dir}/state.node")
187
+ end
188
+
189
+ s3.release_lock()
190
+
191
+ elsif ARGV[0] == "destroy" && options.src
192
+
193
+ if !ARGV[1]
194
+ puts "Please specify deployment name as the second parameter."
195
+ puts "Available deployments:"
196
+ suite.deployments.each do |name, deployment|
197
+ puts "\t#{name}"
198
+ end
199
+ exit -1
200
+ end
201
+ deployment = ARGV[1]
202
+
203
+ s3.set_lock()
204
+ s3.retrieve("#{options.build_dir}/#{deployment}.tfstate")
205
+ answer = ""
206
+ while answer.empty? || (answer != "y" && answer != "n")
207
+ print "\nYou are about to destroy deployment #{deployment}? (Answering yes will nuke it from the orbit) y/n: "
208
+ answer = STDIN.gets.chomp
209
+ end
210
+
211
+ if answer == "n"
212
+ puts "\nAborted!"
213
+ elsif answer == "y"
214
+ puts "\nDestroying deployment #{deployment} (this may take several minutes)"
215
+ tf_apply = %x( terraform destroy -state=#{options.build_dir}/#{deployment}.tfstate #{options.build_dir})
216
+ puts "\n" + tf_apply
217
+ s3.save("#{options.build_dir}/#{deployment}.tfstate")
218
+ s3.save("#{options.build_dir}/state.node")
219
+ end
220
+
221
+ s3.release_lock()
161
222
 
162
223
  elsif ARGV[0] == "lock"
163
- s3.set_lock()
224
+ s3.set_lock()
164
225
 
165
226
  elsif ARGV[0] == "unlock"
166
- s3.release_lock()
227
+ s3.release_lock()
167
228
 
168
229
  else
169
- STDERR.puts "\nERROR: Unknown command #{ARGV[0]}. Maybe you wanted to do: \"biosphere action #{ARGV[0]}\"?"
170
- exit -1
230
+ STDERR.puts "\nERROR: Unknown command #{ARGV[0]}. Maybe you wanted to do: \"biosphere action #{ARGV[0]}\"?"
231
+ exit -1
171
232
  end
172
233
 
data/lib/biosphere.rb CHANGED
@@ -1,10 +1,15 @@
1
1
  class Biosphere
2
2
 
3
3
  end
4
+ require 'deep_merge'
4
5
 
5
6
  require "biosphere/ipaddressallocator"
6
7
  require "biosphere/version"
8
+ require "biosphere/settings"
7
9
  require "biosphere/node"
10
+ require "biosphere/state"
11
+ require "biosphere/deployment"
8
12
  require "biosphere/terraformproxy"
9
13
  require "biosphere/suite"
14
+ require "biosphere/s3"
10
15
 
@@ -0,0 +1,165 @@
1
+ require 'pp'
2
+ require 'awesome_print'
3
+
4
+ class Biosphere
5
+
6
+ class Deployment
7
+
8
+ attr_reader :export, :name, :_settings
9
+ attr_accessor :state, :node
10
+ def initialize(*args)
11
+
12
+ @parent = nil
13
+ @name = "unnamed"
14
+ if args[0].kind_of?(::Biosphere::Deployment) || args[0].kind_of?(::Biosphere::Suite)
15
+ @parent = args.shift
16
+ elsif args[0].kind_of?(String)
17
+ @name = args.shift
18
+ end
19
+
20
+ settings = {}
21
+ @_settings = {}
22
+ if args[0].kind_of?(Hash)
23
+ settings = args.shift
24
+ @_settings = settings
25
+ elsif args[0].kind_of?(::Biosphere::Settings)
26
+ @_settings = args.shift
27
+ settings = @_settings.settings
28
+ end
29
+
30
+ @export = {
31
+ "provider" => {},
32
+ "resource" => {},
33
+ "variable" => {},
34
+ "output" => {}
35
+ }
36
+
37
+ if @parent.is_a?(::Biosphere::Suite)
38
+ if settings[:deployment_name]
39
+ @name = settings[:deployment_name]
40
+ else
41
+ puts "\nYou need to specify :deployment_name in the Deployment settings. For example:"
42
+ puts "cluster = AdsDeliveryCluster.new(suite, MyDeliveryTestSettings.new({deployment_name: \"my-delivery-test-cluster\"})\n\n"
43
+ raise RuntimeError.new "No :deployment_name specified in Deployment settings"
44
+ end
45
+
46
+ @parent.register(self)
47
+ elsif @parent
48
+ @node = @parent.node
49
+ @state = @parent.state
50
+ @export = @parent.export
51
+ @parent.register(self)
52
+
53
+ else
54
+ @node = Node.new
55
+ end
56
+
57
+ @delayed = []
58
+ @resources = []
59
+ @actions = {}
60
+ @deployments = []
61
+
62
+ self.setup(settings)
63
+
64
+ end
65
+
66
+ def setup(settings)
67
+ end
68
+
69
+ def node
70
+ return @node
71
+ end
72
+
73
+ def state
74
+ if @state != nil
75
+ return @state
76
+ end
77
+
78
+ if @parent
79
+ return @parent.state
80
+ end
81
+ end
82
+
83
+ def register(deployment)
84
+ @deployments << deployment
85
+ end
86
+
87
+ def variable(name, value)
88
+ @export["variable"][name] = {
89
+ "default" => value
90
+ }
91
+ end
92
+
93
+ def provider(name, spec={})
94
+ @export["provider"][name.to_s] = spec
95
+ end
96
+
97
+ def delayed(&block)
98
+ delayed_call = {
99
+ :block => block
100
+ }
101
+ @delayed << delayed_call
102
+ end
103
+
104
+ def resource(type, name, &block)
105
+ @export["resource"][type.to_s] ||= {}
106
+ if @export["resource"][type.to_s][name.to_s]
107
+ throw "Tried to create a resource of type #{type} called '#{name}' when one already exists"
108
+ end
109
+
110
+ spec = {}
111
+ resource = {
112
+ :name => name,
113
+ :type => type,
114
+ :location => caller[0] + "a"
115
+ }
116
+
117
+ if block_given?
118
+ resource[:block] = block
119
+ else
120
+ STDERR.puts("WARNING: No block set for resource call '#{type}', '#{name}' at #{caller[0]}")
121
+ end
122
+
123
+ @resources << resource
124
+
125
+ end
126
+
127
+ def output(name, value)
128
+ @export["output"][name] = {
129
+ "value" => value
130
+ }
131
+ end
132
+
133
+ def evaluate_resources()
134
+
135
+ # Call first sub-deployments
136
+ @deployments.each do |deployment|
137
+ deployment.evaluate_resources()
138
+ end
139
+
140
+ # Then all delayed calls
141
+ @delayed.each do |delayed_call|
142
+ proxy = ResourceProxy.new(self)
143
+ proxy.instance_eval(&delayed_call[:block])
144
+ end
145
+
146
+ # And finish with our own resources
147
+ @resources.each do |resource|
148
+ proxy = ResourceProxy.new(self)
149
+ proxy.instance_eval(&resource[:block])
150
+
151
+ @export["resource"][resource[:type].to_s][resource[:name].to_s] = proxy.output
152
+ end
153
+
154
+ end
155
+
156
+ def to_json(pretty=false)
157
+ if pretty
158
+ return JSON.pretty_generate(@export)
159
+ else
160
+ return JSON.generate(@export)
161
+ end
162
+ end
163
+
164
+ end
165
+ end