biosphere 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7db29e14878fca926e12f7a6f393b1b9a469fb10
4
+ data.tar.gz: 77bd008db21e926a6e8fa2d9038a0bccc0f6931d
5
+ SHA512:
6
+ metadata.gz: 3718f49c615032c74a2803843096660bc9e2c1bf9c9e6d1f793f0a289f698917a4ef40c631119242c3afeeca4372df8dbb3c1a2ec83de669f13db7bde585a565
7
+ data.tar.gz: 69ca441a4659994b292630eef52e73bc827a65e9507419e2da8d742dd3041f62d88d1de9f685dd938a1e0c5765181c414176372fce2a0879ca072adf88ce6824
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'biosphere'
4
+ require 'optparse'
5
+ require 'ostruct'
6
+ require 'pp'
7
+ require "awesome_print"
8
+
9
+ class BiosphereOpts
10
+
11
+ def self.parse(args)
12
+
13
+ options = OpenStruct.new
14
+ options.build = "build"
15
+ options.src = "./"
16
+
17
+ opt_parser = OptionParser.new do |opts|
18
+
19
+
20
+ opts.banner = "Usage: \"biosphere [options] <action>\""
21
+
22
+ opts.separator ""
23
+ opts.separator "Commands:"
24
+ opts.separator "\tplan\tRun the planning phase"
25
+ opts.separator "\tjson\tWrite tf files as json into build directory"
26
+ opts.separator "\taction [action]\tCall an action defined in the application .rb files"
27
+ opts.separator ""
28
+
29
+ opts.on_tail("-h", "--help", "Show this message") do
30
+ puts opts
31
+ exit
32
+ end
33
+
34
+ opts.on("--src PATH", "Directory where the application .rb files are") do |path|
35
+ options.src = path
36
+ end
37
+
38
+
39
+ opts.on("--build PATH", "Directory where to build json files") do |path|
40
+ options.build = path
41
+ end
42
+
43
+ end
44
+
45
+ opt_parser.parse!(args)
46
+ options
47
+ end
48
+
49
+ end
50
+
51
+
52
+
53
+ options = BiosphereOpts.parse(ARGV)
54
+
55
+ if ARGV.length == 0
56
+ STDERR.puts "No action spesified. Use -h to get help."
57
+ exit -1
58
+ end
59
+
60
+ if !File.directory?(options.src)
61
+ STDERR.puts "Directory #{options.build} is not a directory or it doesn't exists."
62
+ exit -1
63
+ end
64
+
65
+ if options.src
66
+ suite = Biosphere::Suite.new(options.src)
67
+ suite.load_all()
68
+ else
69
+ STDERR.puts "No --src set"
70
+ end
71
+
72
+ if ARGV[0] == "json" && options.src
73
+ suite.evaluate_resources()
74
+
75
+ if !File.directory?(options.build)
76
+ STDERR.puts "Directory #{options.build} is not a directory or it doesn't exists."
77
+ exit -1
78
+ end
79
+
80
+ count = 0
81
+ suite.write_json_to(options.build) do |file_name, destination, str, proxy|
82
+ puts "Wrote #{str.length} bytes from #{file_name} to #{destination} (#{proxy.export["resource"].length} resources)"
83
+ count = count + 1
84
+ end
85
+
86
+ puts "Wrote #{count} files into #{options.build}"
87
+ suite.save_node()
88
+
89
+ end
90
+
91
+ if ARGV[0] == "plan" && options.src
92
+ suite.evaluate_plans()
93
+ ap suite.node
94
+ end
95
+
96
+ if ARGV[0] == "action" && options.src
97
+ context = Biosphere::ActionContext.new()
98
+
99
+ context.build_directory = options.build
100
+
101
+ suite.call_action(ARGV[1], context)
102
+
103
+ suite.save_node()
104
+ end
105
+
@@ -0,0 +1,24 @@
1
+
2
+ puts "At example.rb"
3
+
4
+ resource "type", "name",
5
+ foo: "one",
6
+ bar: false
7
+
8
+ resource "aws_vpc", "default",
9
+ cidr_block: "1.0.0.0/16",
10
+ enable_dns_hostnames: true,
11
+ tags: {
12
+ Name: "Garo's machine"
13
+ }
14
+
15
+ resource "aws_security_group", "nat",
16
+ name: "vpc-nat",
17
+ ingress: [
18
+ {
19
+ from_port: 80,
20
+ to_port: 80,
21
+ protocol: "tcp",
22
+ cidr_blocks: ["${var.private_subnet_cidr}"]
23
+ }
24
+ ]
@@ -0,0 +1,9 @@
1
+ class Biosphere
2
+
3
+ end
4
+
5
+ require "biosphere/version"
6
+ require "biosphere/node"
7
+ require "biosphere/terraformproxy"
8
+ require "biosphere/suite"
9
+
@@ -0,0 +1,16 @@
1
+
2
+ class Biosphere
3
+ module Mixing
4
+ module FromFile
5
+ def from_file(filename)
6
+
7
+ if File.exists?(filename) && File.readable?(filename)
8
+ self.instance_eval(IO.read(filename), filename, 1)
9
+ else
10
+ raise IOError, "Cannot open or read #{filename}!"
11
+ end
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,34 @@
1
+ require 'pp'
2
+
3
+ class Biosphere
4
+ class Node
5
+ attr_reader :data
6
+ def initialize(from_string = nil)
7
+ if from_string
8
+ blob = Marshal.load(from_string)
9
+ @data = blob.data
10
+ else
11
+ @data = {}
12
+ end
13
+ end
14
+
15
+ def include?(symbol)
16
+ @data.include?(symbol)
17
+ end
18
+
19
+ def []=(symbol, *args)
20
+ @data[symbol] = args[0]
21
+ end
22
+
23
+ def [](symbol, *args)
24
+ if !@data[symbol]
25
+ @data[symbol] = Node.new
26
+ end
27
+ return @data[symbol]
28
+ end
29
+
30
+ def save()
31
+ return Marshal.dump(self)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,100 @@
1
+ require 'pp'
2
+ require 'ipaddress'
3
+ require 'biosphere/node'
4
+
5
+ class Biosphere
6
+ class Suite
7
+
8
+ attr_accessor :files
9
+ attr_accessor :actions
10
+
11
+ def initialize(directory)
12
+
13
+ @files = {}
14
+ @actions = {}
15
+ @directory = directory
16
+
17
+ files = Dir::glob("#{directory}/*.rb")
18
+
19
+ @plan_proxy = PlanProxy.new
20
+
21
+ for file in files
22
+ proxy = Biosphere::TerraformProxy.new(file, @plan_proxy)
23
+
24
+ @files[file[directory.length+1..-1]] = proxy
25
+ end
26
+
27
+ if File.exists?(directory + "/state.node")
28
+ @plan_proxy.node = Marshal.load(File.read(directory + "/state.node"))
29
+ end
30
+ end
31
+
32
+ def node(name=nil)
33
+ if name
34
+ return @plan_proxy.node[name]
35
+ else
36
+ return @plan_proxy.node
37
+ end
38
+ end
39
+
40
+
41
+ def evaluate_resources()
42
+ @files.each do |file_name, proxy|
43
+ proxy.evaluate_resources()
44
+ end
45
+ end
46
+
47
+ def load_all()
48
+ @files.each do |file_name, proxy|
49
+ proxy.load_from_file()
50
+
51
+ proxy.actions.each do |key, value|
52
+
53
+ if @actions[key]
54
+ raise "Action '#{key}' already defined at #{value[:location]}"
55
+ end
56
+ @actions[key] = value
57
+ end
58
+ end
59
+ end
60
+
61
+ def save_node(filename = "state.node")
62
+ str = Marshal.dump(@plan_proxy.node)
63
+ File.write(@directory + "/" + filename, str)
64
+ end
65
+
66
+
67
+ def evaluate_plans()
68
+ @files.each do |file_name, proxy|
69
+ puts "evaluating plans for #{file_name}"
70
+
71
+ proxy.evaluate_plans()
72
+ end
73
+ end
74
+
75
+ def call_action(name, context)
76
+ @files.each do |file_name, proxy|
77
+ if proxy.actions[name]
78
+ proxy.call_action(name, context)
79
+ end
80
+ end
81
+ end
82
+
83
+ def write_json_to(destination_dir)
84
+ if !File.directory?(destination_dir)
85
+ Dir.mkdir(destination_dir)
86
+ end
87
+
88
+ @files.each do |file_name, proxy|
89
+ json_name = file_name[0..-4] + ".json.tf"
90
+ str = proxy.to_json(true) + "\n"
91
+ destination_name = destination_dir + "/" + json_name
92
+ File.write(destination_name, str)
93
+
94
+ yield file_name, destination_name, str, proxy if block_given?
95
+ end
96
+
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,244 @@
1
+ require 'biosphere/mixing/from_file.rb'
2
+ require 'json'
3
+ require 'pathname'
4
+
5
+ class Biosphere
6
+ class ActionContext
7
+ attr_accessor :build_directory
8
+ attr_accessor :caller
9
+
10
+ def initialize()
11
+
12
+ end
13
+
14
+ def method_missing(symbol, *args)
15
+ #puts ">>>>>>>> method missing: #{symbol}, #{args}"
16
+
17
+ if @caller.methods.include?(symbol)
18
+ return @caller.method(symbol).call(*args)
19
+ end
20
+ end
21
+
22
+
23
+ end
24
+
25
+
26
+ class PlanProxy
27
+ attr_accessor :node
28
+ def initialize()
29
+ @node = Node.new
30
+ end
31
+
32
+ end
33
+
34
+ class ResourceProxy
35
+ attr_reader :output
36
+ attr_reader :caller
37
+
38
+ def initialize(caller)
39
+ @output = {}
40
+ @caller = caller
41
+ end
42
+
43
+
44
+ def respond_to?(symbol, include_private = false)
45
+ return true
46
+ end
47
+
48
+ def method_missing(symbol, *args)
49
+ #puts ">>>>>>>> method missing: #{symbol}, #{args}"
50
+
51
+ if @caller.methods.include?(symbol)
52
+ return @caller.method(symbol).call(*args)
53
+ end
54
+
55
+ # Support getter here
56
+ if args.length == 0
57
+ return @output[symbol]
58
+ end
59
+
60
+ # Support setter here
61
+ if [:ingress, :egress, :route].include?(symbol)
62
+ @output[symbol] ||= []
63
+ if args[0].kind_of?(Array)
64
+ @output[symbol] += args[0]
65
+ else
66
+ @output[symbol] << args[0]
67
+ end
68
+ else
69
+ @output[symbol] = args[0]
70
+ end
71
+ end
72
+ end
73
+
74
+ class TerraformProxy
75
+
76
+ attr_accessor :export
77
+ attr_accessor :resources
78
+ attr_accessor :actions
79
+ attr_accessor :plans
80
+ attr_accessor :plan_proxy
81
+ attr_reader :src_path
82
+
83
+
84
+ def initialize(script_name, plan_proxy = nil)
85
+ @script_name = script_name
86
+ @src_path = [File.dirname(script_name)]
87
+
88
+ @export = {
89
+ "provider" => {},
90
+ "resource" => {},
91
+ "variable" => {},
92
+ "output" => {}
93
+ }
94
+ if !plan_proxy
95
+ plan_proxy = PlanProxy.new
96
+ end
97
+ @plan_proxy = plan_proxy
98
+ @resources = []
99
+ @actions = {}
100
+ @plans = []
101
+
102
+ end
103
+
104
+ def load_from_file()
105
+ self.from_file(@script_name)
106
+ end
107
+
108
+ def load_from_block(&block)
109
+ self.instance_eval(&block)
110
+ end
111
+
112
+ def load(filename)
113
+ src_path = Pathname.new(@src_path.last + "/" + File.dirname(filename)).cleanpath.to_s
114
+ # Push current src_path and overwrite @src_path so that it tracks recursive loads
115
+ @src_path << src_path
116
+
117
+ #puts "Trying to open file: " + src_path + "/" + File.basename(filename)
118
+ if File.exists?(src_path + "/" + File.basename(filename))
119
+ self.from_file(src_path + "/" + File.basename(filename))
120
+ elsif File.exists?(src_path + "/" + File.basename(filename) + ".rb")
121
+ self.from_file(src_path + "/" + File.basename(filename) + ".rb")
122
+ else
123
+ raise "Can't find #{filename}"
124
+ end
125
+
126
+ # Restore it as we are unwinding the call stack
127
+ @src_path.pop
128
+ end
129
+
130
+
131
+ include Biosphere::Mixing::FromFile
132
+
133
+ def provider(name, spec={})
134
+ @export["provider"][name.to_s] = spec
135
+ end
136
+
137
+ def variable(name, value)
138
+ @export["variable"][name] = {
139
+ "default" => value
140
+ }
141
+ end
142
+
143
+ def output(name, value)
144
+ @export["output"][name] = {
145
+ "value" => value
146
+ }
147
+ end
148
+
149
+ def action(name, description, &block)
150
+ @actions[name] = {
151
+ :name => name,
152
+ :description => description,
153
+ :block => block,
154
+ :location => caller[0]
155
+ }
156
+ end
157
+
158
+ def plan(name, &block)
159
+ plan = {
160
+ :name => name,
161
+ :block => block,
162
+ :location => caller[0]
163
+ }
164
+ @plans << plan
165
+ end
166
+
167
+ def node
168
+ return @plan_proxy.node
169
+ end
170
+
171
+ def evaluate_plans()
172
+ @plans.each do |resource|
173
+ @plan_proxy.instance_eval(&resource[:block])
174
+ end
175
+
176
+ end
177
+
178
+ def call_action(name, context)
179
+ context.caller = self
180
+
181
+ context.instance_eval(&@actions[name][:block])
182
+ end
183
+
184
+ def resource(type, name, &block)
185
+ @export["resource"][type.to_s] ||= {}
186
+ if @export["resource"][type.to_s][name.to_s]
187
+ throw "Tried to create a resource of type #{type} called '#{name}' when one already exists"
188
+ end
189
+
190
+ spec = {}
191
+ resource = {
192
+ :name => name,
193
+ :type => type,
194
+ :location => caller[0] + "a"
195
+ }
196
+
197
+ if block_given?
198
+ resource[:block] = block
199
+ else
200
+ STDERR.puts("WARNING: No block set for resource call '#{type}', '#{name}' at #{caller[0]}")
201
+ end
202
+
203
+
204
+
205
+ @resources << resource
206
+
207
+ end
208
+
209
+ def evaluate_resources()
210
+ @resources.each do |resource|
211
+ proxy = ResourceProxy.new(self)
212
+ proxy.instance_eval(&resource[:block])
213
+
214
+ @export["resource"][resource[:type].to_s][resource[:name].to_s] = proxy.output
215
+ end
216
+ end
217
+
218
+ def id_of(type,name)
219
+ "${#{type}.#{name}.id}"
220
+ end
221
+
222
+ def output_of(type, name, *values)
223
+ "${#{type}.#{name}.#{values.join(".")}}"
224
+ end
225
+
226
+ def add_resource_alias(type)
227
+ define_singleton_method type.to_sym do |name, spec={}|
228
+ resource(type, name, spec)
229
+ end
230
+ end
231
+
232
+ def use_resource_shortcuts!
233
+ require_relative 'resource_shortcuts'
234
+ end
235
+
236
+ def to_json(pretty=false)
237
+ if pretty
238
+ return JSON.pretty_generate(@export)
239
+ else
240
+ return JSON.generate(@export)
241
+ end
242
+ end
243
+ end
244
+ end
@@ -0,0 +1,3 @@
1
+ class Biosphere
2
+ Version = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: biosphere
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Juho Mäkinen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-11-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 3.4.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 3.4.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: ipaddress
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.8.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 0.8.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: awesome_print
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 1.7.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.7.0
55
+ description: "Terraform's HCL lacks quite many programming features like iterators,
56
+ true variables, advanced string manipulation, functions etc.\n\n This Ruby tool
57
+ provides an easy-to-use DSL to define Terraform compatible .json files which can
58
+ then be used with Terraform side-by-side with HCL files.\n "
59
+ email: juho@unity3d.com
60
+ executables:
61
+ - biosphere
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - bin/biosphere
66
+ - examples/example.rb
67
+ - lib/biosphere.rb
68
+ - lib/biosphere/mixing/from_file.rb
69
+ - lib/biosphere/node.rb
70
+ - lib/biosphere/suite.rb
71
+ - lib/biosphere/terraformproxy.rb
72
+ - lib/biosphere/version.rb
73
+ homepage: http://github.com/UnityTech/biosphere
74
+ licenses:
75
+ - MIT
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 2.4.5.1
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: Tool to provision VPC with Terraform with a Ruby DSL
97
+ test_files: []