biosphere 0.0.14 → 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.
- checksums.yaml +4 -4
- data/bin/biosphere +172 -111
- data/lib/biosphere.rb +5 -0
- data/lib/biosphere/deployment.rb +165 -0
- data/lib/biosphere/kube.rb +26 -8
- data/lib/biosphere/node.rb +52 -9
- data/lib/biosphere/s3.rb +7 -5
- data/lib/biosphere/settings.rb +72 -0
- data/lib/biosphere/state.rb +60 -0
- data/lib/biosphere/suite.rb +99 -101
- data/lib/biosphere/terraformproxy.rb +44 -126
- data/lib/biosphere/version.rb +1 -1
- metadata +33 -2
data/lib/biosphere/kube.rb
CHANGED
@@ -114,7 +114,7 @@ class Biosphere
|
|
114
114
|
|
115
115
|
end
|
116
116
|
|
117
|
-
end
|
117
|
+
end # end of class Client
|
118
118
|
|
119
119
|
def kube_test(str)
|
120
120
|
return str
|
@@ -182,6 +182,29 @@ class Biosphere
|
|
182
182
|
end
|
183
183
|
end
|
184
184
|
|
185
|
+
#
|
186
|
+
# Applies seleted properties from the current resource (as fetched from apiserver) to the new_version (as read from manifest file)
|
187
|
+
#
|
188
|
+
#
|
189
|
+
def self.kube_merge_resource_for_put!(current, new_version)
|
190
|
+
if current[:metadata]
|
191
|
+
new_version[:metadata][:selfLink] = current[:metadata][:selfLink] if current[:metadata][:selfLink]
|
192
|
+
new_version[:metadata][:uid] = current[:metadata][:uid] if current[:metadata][:uid]
|
193
|
+
new_version[:metadata][:resourceVersion] = current[:metadata][:resourceVersion] if current[:metadata][:resourceVersion]
|
194
|
+
end
|
195
|
+
|
196
|
+
if current[:spec]
|
197
|
+
new_version[:spec] = {} if !new_version[:spec]
|
198
|
+
|
199
|
+
# handle spec.clusterIP
|
200
|
+
if new_version[:spec][:clusterIP] && new_version[:spec][:clusterIP] != current[:spec][:clusterIP]
|
201
|
+
raise ArgumentError, "Tried to modify spec.clusterIP from #{current[:spec][:clusterIP]} to #{new_version[:spec][:clusterIP]} but the field is immutable"
|
202
|
+
end
|
203
|
+
new_version[:spec][:clusterIP] = current[:spec][:clusterIP] if current[:spec][:clusterIP]
|
204
|
+
|
205
|
+
end
|
206
|
+
return new_version
|
207
|
+
end
|
185
208
|
|
186
209
|
def kube_apply_resource(client, resource)
|
187
210
|
name = resource[:metadata][:name]
|
@@ -206,15 +229,10 @@ class Biosphere
|
|
206
229
|
puts "Updating resource #{response[:resource]}"
|
207
230
|
|
208
231
|
# Get the current full resource from apiserver
|
209
|
-
|
232
|
+
current_resource = response[:body]
|
210
233
|
|
211
|
-
|
212
|
-
# TODO: this doesn't remove fields from the update_resource which have been removed from the to-be-updated resource
|
213
|
-
update_resource.merge(resource)
|
234
|
+
update_resource = Kube.kube_merge_resource_for_put!(current_resource, resource)
|
214
235
|
|
215
|
-
# Remove fields which apiserver refuses to accept in PUT requests
|
216
|
-
update_resource.delete(:apiVersion)
|
217
|
-
|
218
236
|
begin
|
219
237
|
responses << client.put(update_resource)
|
220
238
|
rescue RestClient::Exception => e
|
data/lib/biosphere/node.rb
CHANGED
@@ -2,34 +2,77 @@ require 'pp'
|
|
2
2
|
require 'awesome_print'
|
3
3
|
|
4
4
|
class Biosphere
|
5
|
+
|
5
6
|
class Node
|
7
|
+
|
8
|
+
class Attribute < Hash
|
9
|
+
def deep_set(*args)
|
10
|
+
#puts "deep_set: #{args}"
|
11
|
+
raise ArgumentError, "must pass at least one key, and a value" if args.length < 2
|
12
|
+
value = args.pop
|
13
|
+
args = args.first if args.length == 1 && args.first.kind_of?(Array)
|
14
|
+
|
15
|
+
key = args.first
|
16
|
+
raise ArgumentError, "must be a number" if self.kind_of?(Array) && !key.kind_of?(Numeric)
|
17
|
+
|
18
|
+
if args.length == 1
|
19
|
+
self[key] = value
|
20
|
+
else
|
21
|
+
child = self[key]
|
22
|
+
unless child.respond_to?(:store_path)
|
23
|
+
self[key] = self.class.new
|
24
|
+
child = self[key]
|
25
|
+
end
|
26
|
+
child.deep_set(args[1..-1].push, value)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
6
31
|
attr_reader :data
|
7
|
-
def initialize(
|
8
|
-
if
|
9
|
-
blob = Marshal.load(
|
10
|
-
|
32
|
+
def initialize(from = nil)
|
33
|
+
if from && from.is_a?(String)
|
34
|
+
blob = Marshal.load(from)
|
35
|
+
if blob.class == Biosphere::Node
|
36
|
+
raise "Tried to load old state format. Unfortunately we are not backwards compatible"
|
37
|
+
end
|
38
|
+
@data = blob
|
39
|
+
elsif from
|
40
|
+
@data = from
|
11
41
|
else
|
12
|
-
@data =
|
42
|
+
@data = Attribute.new
|
13
43
|
end
|
14
44
|
end
|
15
45
|
|
46
|
+
def data
|
47
|
+
return @data
|
48
|
+
end
|
49
|
+
|
50
|
+
def data=(s)
|
51
|
+
@data = s
|
52
|
+
end
|
53
|
+
|
16
54
|
def include?(symbol)
|
17
55
|
@data.include?(symbol)
|
18
56
|
end
|
19
57
|
|
58
|
+
def deep_set(*args)
|
59
|
+
@data.deep_set(*args)
|
60
|
+
end
|
61
|
+
|
20
62
|
def []=(symbol, *args)
|
21
63
|
@data[symbol] = args[0]
|
22
64
|
end
|
23
65
|
|
24
66
|
def [](symbol, *args)
|
25
|
-
if !@data[symbol]
|
26
|
-
@data[symbol] = Node.new
|
27
|
-
end
|
28
67
|
return @data[symbol]
|
29
68
|
end
|
30
69
|
|
70
|
+
def merge!(obj)
|
71
|
+
@data.deep_merge!(obj)
|
72
|
+
end
|
73
|
+
|
31
74
|
def save()
|
32
|
-
return Marshal.dump(
|
75
|
+
return Marshal.dump(@data)
|
33
76
|
end
|
34
77
|
|
35
78
|
def values
|
data/lib/biosphere/s3.rb
CHANGED
@@ -8,13 +8,14 @@ class S3
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def save(path_to_file)
|
11
|
-
puts "\nSaving #{path_to_file} to S3"
|
12
11
|
filename = path_to_file.split('/')[-1]
|
12
|
+
key = "#{@main_key}/#{filename}"
|
13
|
+
puts "Saving #{path_to_file} to S3 #{key}"
|
13
14
|
begin
|
14
15
|
File.open(path_to_file, 'rb') do |file|
|
15
16
|
@client.put_object({
|
16
17
|
:bucket => @bucket_name,
|
17
|
-
:key =>
|
18
|
+
:key => key,
|
18
19
|
:body => file
|
19
20
|
})
|
20
21
|
end
|
@@ -27,17 +28,18 @@ class S3
|
|
27
28
|
|
28
29
|
def retrieve(path_to_file)
|
29
30
|
filename = path_to_file.split('/')[-1]
|
30
|
-
|
31
|
+
key = "#{@main_key}/#{filename}"
|
32
|
+
puts "Fetching #{filename} from S3 from #{key}"
|
31
33
|
begin
|
32
34
|
resp = @client.get_object({
|
33
35
|
:bucket => @bucket_name,
|
34
|
-
:key =>
|
36
|
+
:key => key
|
35
37
|
})
|
36
38
|
File.open(path_to_file, 'w') do |f|
|
37
39
|
f.puts(resp.body.read)
|
38
40
|
end
|
39
41
|
rescue Aws::S3::Errors::NoSuchKey
|
40
|
-
puts "
|
42
|
+
puts "Couldn't find remote file #{filename} from S3 at #{key}"
|
41
43
|
rescue
|
42
44
|
puts "\nError occurred while fetching the remote state, can't continue."
|
43
45
|
puts "Error: #{$!}"
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'deep_dup'
|
3
|
+
|
4
|
+
class Biosphere
|
5
|
+
|
6
|
+
class Settings
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def class_attribute(*attrs)
|
10
|
+
singleton_class.class_eval do
|
11
|
+
attr_accessor(*attrs)
|
12
|
+
end
|
13
|
+
|
14
|
+
class_attributes.merge(attrs)
|
15
|
+
end
|
16
|
+
|
17
|
+
def class_attributes
|
18
|
+
@class_attributes ||= ::Set.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def inherited(subclass)
|
22
|
+
class_attributes.each do |attr|
|
23
|
+
value = send(attr)
|
24
|
+
value = DeepDup.deep_dup(value) # rescue value
|
25
|
+
subclass.class_attribute attr
|
26
|
+
subclass.send("#{attr}=", value)
|
27
|
+
|
28
|
+
p = path
|
29
|
+
if p != ""
|
30
|
+
p = p + "/"
|
31
|
+
end
|
32
|
+
subclass.send("path=", p + subclass.name.split('::').last)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def settings(settings=nil)
|
37
|
+
if settings
|
38
|
+
c = @settings_hash ||= ::Hash.new
|
39
|
+
c = DeepDup.deep_dup(c)
|
40
|
+
c.deep_merge!(settings)
|
41
|
+
@settings_hash = c
|
42
|
+
end
|
43
|
+
return @settings_hash
|
44
|
+
end
|
45
|
+
|
46
|
+
def path()
|
47
|
+
return @path
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class_attribute :settings_hash, :path
|
52
|
+
|
53
|
+
attr_accessor :settings, :path
|
54
|
+
|
55
|
+
def initialize(settings = {})
|
56
|
+
@settings = DeepDup.deep_dup(self.class.settings_hash)
|
57
|
+
@path = self.class.path
|
58
|
+
if settings
|
59
|
+
@settings.deep_merge!(settings)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def [](key)
|
64
|
+
return @settings[key]
|
65
|
+
end
|
66
|
+
|
67
|
+
settings({})
|
68
|
+
|
69
|
+
self.path = ""
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'ipaddress'
|
3
|
+
require 'biosphere/node'
|
4
|
+
require 'deep_merge'
|
5
|
+
|
6
|
+
class Biosphere
|
7
|
+
class State
|
8
|
+
attr_accessor :filename, :node
|
9
|
+
|
10
|
+
def initialize(filename = nil)
|
11
|
+
if filename
|
12
|
+
load(filename)
|
13
|
+
else
|
14
|
+
self.reset()
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def reset()
|
19
|
+
@node = Node.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def load(filename=nil)
|
23
|
+
if filename
|
24
|
+
@filename = filename
|
25
|
+
end
|
26
|
+
data = Marshal.load(File.read(@filename))
|
27
|
+
puts "Loading data from file #{@filename}: #{data}"
|
28
|
+
load_from_structure!(data)
|
29
|
+
end
|
30
|
+
|
31
|
+
def node(name=nil)
|
32
|
+
if name
|
33
|
+
return @node[name]
|
34
|
+
else
|
35
|
+
return @node
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def merge!(settings)
|
40
|
+
@node.merge!(settings)
|
41
|
+
end
|
42
|
+
|
43
|
+
def save(filename=nil)
|
44
|
+
if !filename && @filename
|
45
|
+
filename = @filename
|
46
|
+
end
|
47
|
+
str = Marshal.dump(@node)
|
48
|
+
File.write(filename, str)
|
49
|
+
puts "Saving state to #{filename}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def load_from_structure!(structure)
|
53
|
+
if @node
|
54
|
+
@node.data.deep_merge(structure.data)
|
55
|
+
else
|
56
|
+
@node = structure
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/biosphere/suite.rb
CHANGED
@@ -3,105 +3,103 @@ require 'ipaddress'
|
|
3
3
|
require 'biosphere/node'
|
4
4
|
|
5
5
|
class Biosphere
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
end
|
6
|
+
class Suite
|
7
|
+
|
8
|
+
attr_accessor :files
|
9
|
+
attr_accessor :actions
|
10
|
+
attr_reader :deployments, :biosphere_settings, :state
|
11
|
+
|
12
|
+
def initialize(state)
|
13
|
+
|
14
|
+
@files = {}
|
15
|
+
@actions = {}
|
16
|
+
@state = state
|
17
|
+
@deployments = {}
|
18
|
+
@biosphere_settings = {}
|
19
|
+
@biosphere_path = ""
|
20
|
+
end
|
21
|
+
|
22
|
+
def register(deployment)
|
23
|
+
if @deployments[deployment.name]
|
24
|
+
raise RuntimeException.new("Deployment #{deployment.name} already registered")
|
25
|
+
end
|
26
|
+
@deployments[deployment.name] = deployment
|
27
|
+
if !@state.node[:deployments]
|
28
|
+
@state.node[:deployments] = Node::Attribute.new
|
29
|
+
end
|
30
|
+
@state.node[:deployments][deployment.name] = Node::Attribute.new
|
31
|
+
deployment.node = Node.new(@state.node[:deployments][deployment.name])
|
32
|
+
#@state.node.deep_set(:deployments, deployment.name, deployment.node.data)
|
33
|
+
deployment.state = @state
|
34
|
+
|
35
|
+
if deployment._settings[:biosphere]
|
36
|
+
@biosphere_settings.deep_merge!(deployment._settings[:biosphere])
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
def evaluate_resources()
|
42
|
+
@deployments.each do |name, deployment|
|
43
|
+
deployment.evaluate_resources()
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def node
|
48
|
+
@state.node
|
49
|
+
end
|
50
|
+
|
51
|
+
def load_all(directory)
|
52
|
+
@directory = directory
|
53
|
+
files = Dir::glob("#{directory}/*.rb")
|
54
|
+
|
55
|
+
for file in files
|
56
|
+
proxy = Biosphere::TerraformProxy.new(file, self)
|
57
|
+
|
58
|
+
@files[file[directory.length+1..-1]] = proxy
|
59
|
+
end
|
60
|
+
|
61
|
+
@files.each do |file_name, proxy|
|
62
|
+
proxy.load_from_file()
|
63
|
+
|
64
|
+
proxy.actions.each do |key, value|
|
65
|
+
|
66
|
+
if @actions[key]
|
67
|
+
raise "Action '#{key}' already defined at #{value[:location]}"
|
68
|
+
end
|
69
|
+
@actions[key] = value
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
return @files.length
|
74
|
+
end
|
75
|
+
|
76
|
+
def call_action(name, context)
|
77
|
+
found = false
|
78
|
+
@files.each do |file_name, proxy|
|
79
|
+
if proxy.actions[name]
|
80
|
+
found = true
|
81
|
+
proxy.call_action(name, context)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
return found
|
86
|
+
end
|
87
|
+
|
88
|
+
def write_json_to(destination_dir)
|
89
|
+
if !File.directory?(destination_dir)
|
90
|
+
Dir.mkdir(destination_dir)
|
91
|
+
end
|
92
|
+
|
93
|
+
@deployments.each do |name, deployment|
|
94
|
+
json_name = deployment.name + ".json.tf"
|
95
|
+
str = deployment.to_json(true) + "\n"
|
96
|
+
destination_name = destination_dir + "/" + json_name
|
97
|
+
File.write(destination_name, str)
|
98
|
+
|
99
|
+
yield deployment.name, destination_name, str, deployment if block_given?
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
107
105
|
end
|