biosphere 0.0.1

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.
@@ -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: []