topo-provision 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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/README.md +100 -0
  4. data/bin/topo-provision +23 -0
  5. data/lib/topo/converter.rb +80 -0
  6. data/lib/topo/converter/cloudformation/converter.rb +219 -0
  7. data/lib/topo/exporter.rb +15 -0
  8. data/lib/topo/loader.rb +31 -0
  9. data/lib/topo/provision.rb +3 -0
  10. data/lib/topo/provision/aws/generator.rb +55 -0
  11. data/lib/topo/provision/aws/generators/aws_auto_scaling_group.rb +61 -0
  12. data/lib/topo/provision/aws/generators/aws_launch_configuration.rb +65 -0
  13. data/lib/topo/provision/aws/generators/context.rb +33 -0
  14. data/lib/topo/provision/aws/generators/load_balancer.rb +47 -0
  15. data/lib/topo/provision/aws/generators/machine.rb +36 -0
  16. data/lib/topo/provision/aws/generators/machine_image.rb +38 -0
  17. data/lib/topo/provision/aws/generators/node_group.rb +64 -0
  18. data/lib/topo/provision/cli.rb +95 -0
  19. data/lib/topo/provision/generator.rb +185 -0
  20. data/lib/topo/provision/generators/chef_node.rb +35 -0
  21. data/lib/topo/provision/generators/context.rb +70 -0
  22. data/lib/topo/provision/generators/load_balancer.rb +40 -0
  23. data/lib/topo/provision/generators/machine.rb +50 -0
  24. data/lib/topo/provision/generators/machine_image.rb +43 -0
  25. data/lib/topo/provision/generators/node_group.rb +74 -0
  26. data/lib/topo/provision/generators/resource.rb +91 -0
  27. data/lib/topo/provision/generators/templates/context.erb +8 -0
  28. data/lib/topo/provision/generators/templates/machine_deploy.erb +11 -0
  29. data/lib/topo/provision/generators/templates/machine_stop.erb +5 -0
  30. data/lib/topo/provision/generators/templates/node_group_action.erb +6 -0
  31. data/lib/topo/provision/generators/templates/node_group_deploy.erb +5 -0
  32. data/lib/topo/provision/generators/templates/resource_deploy.erb +5 -0
  33. data/lib/topo/provision/generators/templates/resource_undeploy.erb +5 -0
  34. data/lib/topo/provision/topology_generator.rb +84 -0
  35. data/lib/topo/provision/vagrant/generator.rb +33 -0
  36. data/lib/topo/provision/version.rb +5 -0
  37. data/lib/topo/topology.rb +61 -0
  38. data/lib/topo/utils/output.rb +31 -0
  39. data/lib/topo/utils/parsegen.rb +101 -0
  40. metadata +126 -0
@@ -0,0 +1,95 @@
1
+ #
2
+ # Author:: Christine Draper (<christine_draper@thirdwaveinsights.com>)
3
+ # Copyright:: Copyright (c) 2015 ThirdWave Insights LLC
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'mixlib/cli'
20
+ require 'topo/provision'
21
+
22
+ module Topo
23
+ module Provision
24
+ class CLI
25
+ include Mixlib::CLI
26
+
27
+ banner('Usage: topo-provision topofile [option]')
28
+
29
+ option :format,
30
+ long: '--format topo|cloudformation',
31
+ description: 'Specifies format of the input topology file'
32
+
33
+ option :output_topo,
34
+ long: '--output-topo filename',
35
+ description: 'Specifies file to output topology JSON'
36
+
37
+ option :output,
38
+ long: '--output filename',
39
+ description: 'Specifies file to output generated recipe to'
40
+
41
+ option :action,
42
+ long: '--action deploy|undeploy|stop',
43
+ description: 'Specifies action to generate - defaults to deploy',
44
+ default: "deploy"
45
+
46
+ attr_accessor :topology
47
+
48
+ def initialize(argv=[])
49
+ super()
50
+ parse_and_validate_args
51
+ end
52
+
53
+ def parse_and_validate_args
54
+ begin
55
+ parse_options
56
+ @input_file = cli_arguments()[0]
57
+ rescue OptionParser::InvalidOption => e
58
+ STDERR.puts e.message
59
+ puts opt_parser
60
+ exit(-1)
61
+ end
62
+
63
+ if !@input_file
64
+ STDERR.puts opt_parser
65
+ exit(-1)
66
+ end
67
+ end
68
+
69
+ def redirect_stdout(file)
70
+ begin
71
+ $stdout.reopen(file, "w")
72
+ rescue => e
73
+ STDERR.puts "ERROR: Cannot open provisioning output file #{file} - #{e.message}"
74
+ end
75
+ end
76
+
77
+ def run
78
+ @topology = Topo::Loader.from_file(@input_file, @config[:format] || "default")
79
+
80
+ # output topo file
81
+ @topology.to_file(@config[:output_topo]) if(@config[:output_topo])
82
+
83
+ # redirect generated recipe to file
84
+ redirect_stdout(@config[:output]) if(@config[:output])
85
+
86
+ # run generator
87
+ @generator = Topo::Provision::Generator.new(@topology)
88
+ action = @config[:action].to_sym
89
+ @generator.generate_provisioning_recipe(action)
90
+
91
+ end
92
+
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,185 @@
1
+ #
2
+ # Author:: Christine Draper (<christine_draper@thirdwaveinsights.com>)
3
+ # Copyright:: Copyright (c) 2015 ThirdWave Insights LLC
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'topo/utils/parsegen'
20
+ require 'topo/provision/topology_generator'
21
+
22
+ # load the resource generators
23
+ %w[context machine machine_image chef_node load_balancer node_group].each do |gen|
24
+ require_relative 'generators/' + gen
25
+ end
26
+
27
+ #require 'topo/provision/generators/machine_image'
28
+
29
+ #
30
+ # The Generator class invokes driver-specific or base generators to generate
31
+ # elements of the provisioning recipe.
32
+ #
33
+
34
+ module Topo
35
+ module Provision
36
+ class Generator
37
+
38
+ include Topo::ParseGen
39
+
40
+ attr_accessor :topology
41
+
42
+ # Generators for each driver (root driver)
43
+ @@generator_classes = {}
44
+ @driver_name = "default"
45
+
46
+ def self.register_generator(driver, class_name)
47
+ @@generator_classes[driver] = class_name
48
+ end
49
+
50
+ self.register_generator(@driver_name, self.name)
51
+
52
+ def initialize(topo=nil)
53
+ @topology = topo
54
+ @generators = { @driver_name => self }
55
+ end
56
+
57
+ # Get the right generator for the driver in place (e.g. vagrant, fog)
58
+ def generator(driver)
59
+ unless @generators.key?(driver)
60
+
61
+ generator_class = @@generator_classes[driver]
62
+
63
+ unless generator_class
64
+ begin
65
+ require "topo/provision/#{driver}/generator"
66
+ generator_class = @@generator_classes[driver]
67
+ rescue LoadError => e
68
+ STDERR.puts e.message
69
+ STDERR.puts("#{driver} driver cannot be loaded - using default generator instead")
70
+ generator_class = @@generator_classes["default"]
71
+ end
72
+ end
73
+
74
+ @generators[driver] = Object::const_get(generator_class).new(@topology)
75
+ end
76
+
77
+ @generators[driver]
78
+ end
79
+
80
+ # Add resources & their generators to the topology generator, then
81
+ # call them in dependency order to generate the overall recipe
82
+ # TODO: Driver-specific generation of dependencies other than through topo refs, e.g. autoscaling depend on loadbalancer
83
+ def generate_provisioning_recipe(action=:deploy)
84
+
85
+ topology_generator = Topo::Provision::TopologyGenerator.new()
86
+ generator(@topology.driver).generate_context(action)
87
+
88
+ @topology.services.each do |service|
89
+ depends_on = topo_refs(service).to_a
90
+ process_lazy_attrs(service) if depends_on.length > 0
91
+ topology_generator.add(service, depends_on,
92
+ { :resource_generator => resource_generator(service['type'], service) })
93
+ end
94
+
95
+ @topology.nodes.each do |node|
96
+ depends_on = topo_refs(node).to_a
97
+ process_lazy_attrs(node) if depends_on.length > 0
98
+ topology_generator.add(node, depends_on,
99
+ { :resource_generator => resource_generator("node", node) })
100
+ end
101
+
102
+ if [:undeploy, :stop].include?(action)
103
+ topology_generator.reverse_generate(action)
104
+ else
105
+ topology_generator.generate(action)
106
+ end
107
+
108
+ end
109
+
110
+
111
+
112
+ def driver(resource)
113
+ if resource['provisioning'] && resource['provisioning']['driver']
114
+ resource_driver = resource['provisioning']['driver'].split(":",2)[0]
115
+ else
116
+ resource_driver = @topology.driver
117
+ end
118
+ resource_driver
119
+ end
120
+
121
+ # Convert attributes with references to lazy attributes, and return array of resource names
122
+ # that this resource depends on
123
+ def process_lazy_attrs(resource)
124
+ depends_on = Set.new
125
+ if resource['attributes']
126
+ resource['attributes'].each do |key, value|
127
+ deps = topo_refs(value)
128
+ if deps.size > 0
129
+ depends_on.merge(deps)
130
+ resource['lazy_attributes'][key] = lazy_attribute_to_s(value)
131
+ resource['attributes'].delete(key)
132
+ end
133
+ end
134
+ end
135
+ depends_on.to_a
136
+ end
137
+
138
+ # BASE DRIVER GENERATORS
139
+
140
+
141
+ def generate_context(action)
142
+ cxt_gen = context
143
+ if (cxt_gen.respond_to? action)
144
+ cxt_gen.send(action)
145
+ else
146
+ cxt_gen.send("default_action", action)
147
+ end
148
+ end
149
+
150
+ def resource_generator(resource_type, data)
151
+ generator = nil
152
+ driver_generator = generator(driver(data))
153
+ if driver_generator.respond_to?(resource_type)
154
+ generator = driver_generator.send(resource_type, data)
155
+ else
156
+ STDERR.puts "Driver #{@topology.driver} does not support resource type #{resource_type}"
157
+ end
158
+ generator
159
+ end
160
+
161
+ # Functions to return the resource generators
162
+
163
+ def node(data)
164
+ if (data['provisioning'])
165
+ if(data['provisioning']['node_group'] && data['provisioning']['node_group']['size'])
166
+ Topo::Provision::NodeGroupGenerator.new(data)
167
+ else
168
+ Topo::Provision::MachineGenerator.new(data)
169
+ end
170
+ else
171
+ Topo::Provision::ChefNodeGenerator.new(data)
172
+ end
173
+ end
174
+
175
+ def load_balancer(data)
176
+ Topo::Provision::LoadBalancerGenerator.new(data)
177
+ end
178
+
179
+ def context()
180
+ @context ||= Topo::Provision::ContextGenerator.new(@topology.provisioning, @topology.driver)
181
+ end
182
+
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,35 @@
1
+ #
2
+ # Author:: Christine Draper (<christine_draper@thirdwaveinsights.com>)
3
+ # Copyright:: Copyright (c) 2015 ThirdWave Insights LLC
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require_relative 'resource'
20
+
21
+ module Topo
22
+ module Provision
23
+ class ChefNodeGenerator < Topo::Provision::ResourceGenerator
24
+
25
+ def initialize(data)
26
+ @resource_type ||= "chef_node"
27
+ super
28
+ %w[run_list chef_environment tags attributes].each do |key|
29
+ @resource_attributes[key] = data[key] if data.key? key
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,70 @@
1
+ #
2
+ # Author:: Christine Draper (<christine_draper@thirdwaveinsights.com>)
3
+ # Copyright:: Copyright (c) 2015 ThirdWave Insights LLC
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'erb'
20
+ require 'topo/utils/parsegen'
21
+
22
+ #
23
+ # The ContextGenerator class generates the recipe context
24
+ #
25
+
26
+ module Topo
27
+ module Provision
28
+
29
+ class ContextGenerator
30
+ include Topo::ParseGen
31
+
32
+ @@driver_files = {
33
+ 'default' =>'chef/provisioning',
34
+ 'aws' =>'chef/provisioning/aws_driver',
35
+ # 'fog' => 'chef/provisoning/fog_driver/driver', - not currently supported
36
+ 'vagrant' => 'chef/provisioning/vagrant_driver/driver'
37
+ }
38
+ @@template = nil
39
+
40
+
41
+ def initialize(data, default_driver)
42
+ @driver = data['driver'].split(':', 2).first if data['driver']
43
+ @driver ||= default_driver
44
+ @require_driver = @@driver_files['default']
45
+ if @driver && @@driver_files.key?(@driver)
46
+ @require_driver = @@driver_files[@driver]
47
+ end
48
+ @machine_options = convert_keys_to_sym(data['machine_options']) if data['machine_options']
49
+ @driver = data['driver']
50
+ end
51
+
52
+ def deploy()
53
+ puts(template.result(binding))
54
+ end
55
+
56
+ def default_action(action)
57
+ puts(template.result(binding))
58
+ end
59
+
60
+ def template()
61
+ unless @@template
62
+ path = File.expand_path("../templates/context.erb", __FILE__)
63
+ @@template = ERB.new(File.new(path).read, nil, '>')
64
+ end
65
+ @@template
66
+ end
67
+
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,40 @@
1
+ #
2
+ # Author:: Christine Draper (<christine_draper@thirdwaveinsights.com>)
3
+ # Copyright:: Copyright (c) 2015 ThirdWave Insights LLC
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'topo/provision/generators/resource'
20
+
21
+ module Topo
22
+ module Provision
23
+
24
+ class LoadBalancerGenerator < Topo::Provision::ResourceGenerator
25
+ include Topo::ParseGen
26
+
27
+ def initialize(data)
28
+ @resource_type ||= "load_balancer"
29
+ super
30
+ @undeploy_action = "destroy"
31
+ %w[machines load_balancer_options].each do |key|
32
+ @resource_attributes[key] = data['provisioning'][key] if data['provisioning'].key? key
33
+ end
34
+
35
+ # Note: driver-specific classes may need to convert load_balancer_options into symbols
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,50 @@
1
+ #
2
+ # Author:: Christine Draper (<christine_draper@thirdwaveinsights.com>)
3
+ # Copyright:: Copyright (c) 2015 ThirdWave Insights LLC
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'topo/provision/generators/resource'
20
+
21
+ #
22
+ # The ResourceGenerator class generates the recipe resources
23
+ #
24
+
25
+ module Topo
26
+ module Provision
27
+ class MachineGenerator < Topo::Provision::ResourceGenerator
28
+
29
+ attr_reader :machine_options, :normal_attributes, :lazy_attributes
30
+
31
+ def initialize(data)
32
+ @resource_type ||= "machine"
33
+ super
34
+ @undeploy_action = "destroy"
35
+ @normal_attributes = data['attributes']||{}
36
+ @lazy_attributes = data['lazy_attributes']||{}
37
+ %w[run_list chef_environment tags ].each do |key|
38
+ @resource_attributes[key] = data[key] if data.key? key
39
+ end
40
+ opts = data['provisioning']['machine_options']
41
+ @machine_options = convert_keys_to_sym(opts) if opts
42
+ end
43
+
44
+ def stop()
45
+ puts(template("stop").result(binding))
46
+ end
47
+
48
+ end
49
+ end
50
+ end