cloudshaper 0.0.4 → 0.0.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 720390f8ef2c81bf799239154777e8b0177a7d4b
4
- data.tar.gz: 952a846501c2d3d5e32b34800cd1d70fd9b7e81d
3
+ metadata.gz: 993c81cd51249cfba9467ea7fcbdf922a1d520dd
4
+ data.tar.gz: c2ac2bdea9eb45c58004053069af40567aced2d9
5
5
  SHA512:
6
- metadata.gz: 610e4fc75444a2e2721aaccf6a0cfc76e8c56a979848a65d500f0d87934661d91f81cc06e2d91744450d742a357a4067d1f2352eb4efae53e8e462d40d76e5a1
7
- data.tar.gz: 8def0b2aef1901b0e1b84fba5c3d061aa9bdab0062dca156200efe18f402a2d4bfe3f3c448e8a8b297b44bffaff74170874dfc78f42ebedf1855e148bc8c2608
6
+ metadata.gz: 27de4a1c4291b51e313559fc4fe23614ec25889c0134704e5c0414ec2136809b71de27d4bd815f16e04f8f2e1c8b6e2cc817ed370457e0496c8d1beb9f44b7b5
7
+ data.tar.gz: c576febad8b279a24f829ba7ed1b16857958e5dc18bbfb81d20869eda2866fabf12fb9032c246f4bfccb2d0b3d1067737388006375d5e52a34aa2192811f4841
data/Gemfile.lock CHANGED
@@ -2,7 +2,7 @@ PATH
2
2
  remote: .
3
3
  specs:
4
4
  cloudshaper (0.0.4)
5
- rake (~> 10.4)
5
+ thor (~> 0.19.1)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
@@ -16,6 +16,7 @@ GEM
16
16
  json (~> 1.8)
17
17
  simplecov-html (~> 0.10.0)
18
18
  simplecov-html (0.10.0)
19
+ thor (0.19.1)
19
20
 
20
21
  PLATFORMS
21
22
  ruby
@@ -24,4 +25,5 @@ DEPENDENCIES
24
25
  bundler
25
26
  cloudshaper!
26
27
  minitest (~> 5.6)
28
+ rake (~> 10.4)
27
29
  simplecov (= 0.10.0)
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- [![Build Status](https://travis-ci.org/dalehamel/terraform_dsl.svg)](https://travis-ci.org/dalehamel/terraform_dsl)
1
+ [![Build Status](https://travis-ci.org/dalehamel/cloudshaper.svg)](https://travis-ci.org/dalehamel/cloudshaper)
2
2
 
3
- # Terraform DSL
3
+ # Cloudshaper
4
4
 
5
5
  This is a simple DSL for wrapping hashicorp's [terraform configuration](https://terraform.io/docs/configuration/index.html).
6
6
 
@@ -50,13 +50,13 @@ Create a stack module, like one of our [examples](examples), such as our [simple
50
50
  Generally, you need to do:
51
51
 
52
52
  ```
53
- require 'terraform_dsl'
53
+ require 'cloudshaper'
54
54
  ```
55
55
 
56
- And then subclass Terraform::StackModule
56
+ And then subclass Cloudshaper::StackModule
57
57
 
58
58
  ```
59
- class MyAwesomeStackModule < Terraform::StackModule
59
+ class MyAwesomeStackModule < Cloudshaper::StackModule
60
60
  ```
61
61
 
62
62
  Within that class, define resources using a similar syntax to [terraform's configuration](https://terraform.io/docs/configuration/index.html).
@@ -107,7 +107,7 @@ common:
107
107
  region: us-east-1
108
108
  stacks:
109
109
  - name: teststack
110
- uuid: 1433296428_0safh8-Y # must be unique
110
+ uuid: 8adcbfb1-fdcc-4558-8958-ea8a9e1874ea # must be unique
111
111
  description: just a test stack
112
112
  root: simpleapp
113
113
  variables:
@@ -117,43 +117,13 @@ stacks:
117
117
 
118
118
  You may also specify a 'common' block, that will be merged into all stacks.
119
119
 
120
- Terraform stacks need somewhere to store their state. By default, this will be the local filesystem.
120
+ Cloudshaper stacks need somewhere to store their state. By default, this will be the local filesystem.
121
121
 
122
122
  It's highly recommended that you use a [remote backend](https://www.terraform.io/docs/commands/remote-config.html) instead, so that you can share your stacks.
123
123
 
124
- ### Tasks
124
+ ### Commands
125
125
 
126
- Create a rake file that loads your modules, and calls Terraform::Tasks.loadall.
127
-
128
- ```
129
- ## Loads terraform tasks and modules
130
- require 'terraform_dsl'
131
- Terraform::Tasks.loadall
132
-
133
- ```
134
-
135
- This will add some terraform tasks for managing your terraform stacks:
136
-
137
- ```
138
- rake terraform:apply[name] # Apply pending changes for a stack
139
- rake terraform:apply_all # Apply all pending stack changes
140
- rake terraform:destroy[name] # Destroy a stack
141
- rake terraform:get[name] # Fetch modules for a stack
142
- rake terraform:get_all # Fetch modules for all stacks
143
- rake terraform:init # Initialize stacks.yml if it does not exist
144
- rake terraform:list # List all available stacks
145
- rake terraform:load # Loads available stack modules
146
- rake terraform:plan[name] # Show pending changes for a stack
147
- rake terraform:pull[name] # Pulls stack state from remote location
148
- rake terraform:pull_all # Pulls stack states from remote location
149
- rake terraform:push[name] # Push stack state to remote location
150
- rake terraform:push_all # Push stack states to remote location
151
- rake terraform:remote_config[name] # Sets up remote config for a stack
152
- rake terraform:remote_config_all # Sets up remote config for all stacks that support it
153
- rake terraform:show[name] # Show details about a stack by name
154
- rake terraform:show_all # Show all pending stack changes
155
- rake terraform:uuid # Generate a UUID for a stack, so stacks do not clobber each other
156
- ```
126
+ TODO
157
127
 
158
128
  # Credits
159
129
 
data/bin/cloudshaper ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
3
+ require 'cloudshaper/cli'
4
+
5
+ begin
6
+ ENV["THOR_DEBUG"] = "1"
7
+ Cloudshaper::CLI.start(ARGV)
8
+ rescue Thor::UndefinedCommandError, Thor::UnknownArgumentError, Thor::AmbiguousCommandError, Thor::InvocationError => e
9
+ $stderr.puts(e.message)
10
+ exit(64)
11
+ rescue Thor::Error => e
12
+ $stderr.puts(e.message)
13
+ exit(1)
14
+ end
@@ -1,16 +1,16 @@
1
1
  lib = File.expand_path('../lib', __FILE__)
2
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require 'terraform_dsl/version'
3
+ require 'cloudshaper/version'
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = 'cloudshaper'
7
- spec.version = Terraform::VERSION
7
+ spec.version = Cloudshaper::VERSION
8
8
  spec.summary = 'Wrap hashicorps "terraform" in a ruby DSL for managing stack templates'
9
- spec.description = 'Terraform DSL provides a syntax for managing terraform infrastructure entirely in git'
9
+ spec.description = 'Cloudshaper provides a syntax for managing terraform infrastructure entirely in git'
10
10
  spec.authors = ['Dale Hamel']
11
11
  spec.email = 'dale.hamel@srvthe.net'
12
12
  spec.files = Dir['lib/**/*']
13
- spec.homepage = 'https://rubygems.org/gems/terraform_dsl'
13
+ spec.homepage = 'https://github.com/dalehamel/cloudshaper'
14
14
  spec.license = 'MIT'
15
15
 
16
16
  spec.files = `git ls-files`.split($/)
@@ -18,8 +18,9 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_runtime_dependency 'rake', '~> 10.4'
21
+ spec.add_runtime_dependency 'thor', '~> 0.19.1'
22
22
 
23
23
  spec.add_development_dependency 'bundler'
24
+ spec.add_development_dependency 'rake', '~> 10.4'
24
25
  spec.add_development_dependency 'minitest', '~> 5.6'
25
26
  end
@@ -1,4 +1,4 @@
1
- module Terraform
1
+ module Cloudshaper
2
2
  module Aws
3
3
  # Support AWS S3 remote state backend
4
4
  module RemoteS3
@@ -1,4 +1,4 @@
1
- module Terraform
1
+ module Cloudshaper
2
2
  # Aws provider-specific functionality, to be mixed in to stack elements
3
3
  module Aws
4
4
  def self.taggable?(resource_type)
@@ -17,7 +17,7 @@ module Terraform
17
17
  def post_processing_aws
18
18
  return unless Aws.taggable?(@resource_type)
19
19
  @fields[:tags] ||= {}
20
- @fields[:tags][:terraform_stack_id] = var(:terraform_stack_id)
20
+ @fields[:tags][:cloudshaper_stack_id] = var(:cloudshaper_stack_id)
21
21
  end
22
22
  end
23
23
  end
@@ -0,0 +1,98 @@
1
+ require 'thor'
2
+ require 'cloudshaper'
3
+
4
+ module Cloudshaper
5
+ class CLI < Thor
6
+ class_option "template_dir", type: "string", default: "templates"
7
+ class_option "remote_state", type: "boolean"
8
+
9
+ desc "list", "List all available stacks"
10
+ def list
11
+ Cloudshaper::Stacks.stacks.each do |name, _stack|
12
+ puts name
13
+ end
14
+ end
15
+
16
+ desc "show NAME", "Show details about a stack by name"
17
+ def show(name)
18
+ stack = load_stack(name, options)
19
+ puts stack
20
+ end
21
+
22
+ desc "plan NAME", "Show pending changes for a stack"
23
+ def plan(name)
24
+ stack = load_stack(name, options)
25
+ stack.plan
26
+ end
27
+
28
+ desc "apply NAME", "Apply all pending stack changes"
29
+ def apply(name)
30
+ stack = load_stack(name, options)
31
+ stack.apply
32
+ push(name) if remote_state?
33
+ end
34
+
35
+ desc "destroy NAME", "Destroy a stack"
36
+ def destroy(name)
37
+ stack = load_stack(name, options)
38
+ stack.destroy
39
+ push(name) if remote_state?
40
+ end
41
+
42
+ desc "pull NAME", "Pull stack state from remote location"
43
+ def pull(name)
44
+ stack = load_stack(name, options)
45
+ remote_config(name)
46
+ stack.pull
47
+ end
48
+
49
+ desc "push NAME", "Push stack state from remote location"
50
+ def push(name)
51
+ stack = load_stack(name, options)
52
+ remote_config(name)
53
+ stack.push
54
+ end
55
+
56
+ desc "remote_config NAME", "Sets up remote config for a stack"
57
+ def remote_config(name)
58
+ stack = load_stack(name, options)
59
+ stack.remote_config
60
+ end
61
+
62
+ desc "init", "Initialize stacks.yml if it does not exist"
63
+ def init
64
+ Cloudshaper::Stacks.init
65
+ puts "Created stacks.yml, you're ready to configure your stack"
66
+ end
67
+
68
+ desc "uuid", "Generate a UUID for your stacks, so they don't clobber each other"
69
+ def uuid
70
+ puts SecureRandom.uuid
71
+ end
72
+
73
+ desc "version", "Prints the version of cloudshaper"
74
+ def version
75
+ puts Cloudshaper::VERSION
76
+ end
77
+
78
+ private
79
+
80
+ def load_stack(stack, options)
81
+ puts options
82
+ load_modules(options['template_dir'])
83
+ Cloudshaper::Stacks.load
84
+ pull(stack) if remote_state?
85
+ stack = Cloudshaper::Stacks.stacks[stack]
86
+ stack.get
87
+ stack
88
+ end
89
+
90
+ def remote_state?
91
+ options["remote_stack"]
92
+ end
93
+
94
+ def load_modules(dir)
95
+ Dir.glob("#{File.join(Dir.pwd, dir)}/*.rb").each { |t| puts t; require_relative t }
96
+ end
97
+ end
98
+ end
@@ -1,4 +1,4 @@
1
- module Terraform
1
+ module Cloudshaper
2
2
  # Wraps terraform command execution
3
3
  class Command
4
4
  attr_accessor :command
@@ -11,7 +11,7 @@ module Terraform
11
11
 
12
12
  def env
13
13
  vars = {}
14
- @stack.variables.each { |k, v| vars["TF_VAR_#{k}"] = v[:default] }
14
+ @stack.module.each_variable { |k, v| vars["TF_VAR_#{k}"] = v[:default] }
15
15
  @stack.module.secrets.each do |_provider, secrets|
16
16
  secrets.each do |k, v|
17
17
  vars[k.to_s] = v
@@ -0,0 +1,30 @@
1
+ require 'cloudshaper/stack_element'
2
+ require 'cloudshaper/stack_modules'
3
+ require 'cloudshaper/stack_module'
4
+ require 'cloudshaper/stacks'
5
+
6
+ module Cloudshaper
7
+ # Supports terraform 'modules'. In our case, we call them submodules because
8
+ # Module is a ruby keyword. We also support directly referencing other ruby-defined modules.
9
+ class Module < StackElement
10
+ def initialize(parent_module, &block)
11
+ super(parent_module, &block)
12
+
13
+ if StackModules.has? @fields[:source].to_s
14
+ mod = StackModules.get @fields[:source].to_s
15
+ module_path = File.join(Stacks.dir, parent_module.id, mod.name)
16
+ FileUtils.mkdir_p(module_path)
17
+ @fields[:source] = File.expand_path(module_path)
18
+
19
+ file_path = File.join(module_path, 'stack_module.tf.json')
20
+ build_submodule(file_path, parent_module, mod)
21
+ end
22
+ end
23
+
24
+ def build_submodule(file_path, parent_module, child_module)
25
+ return if File.exists? file_path
26
+ child_module.build(cloudshaper_stack_id: parent_module.id)
27
+ File.open(file_path, 'w') { |f| f.write(child_module.generate) }
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,6 @@
1
+ require 'cloudshaper/stack_element'
2
+
3
+ module Cloudshaper
4
+ class Output < StackElement
5
+ end
6
+ end
@@ -1,12 +1,14 @@
1
- require 'terraform_dsl/secrets'
2
- require 'terraform_dsl/stack_element'
1
+ require 'cloudshaper/secrets'
2
+ require 'cloudshaper/stack_element'
3
3
 
4
- module Terraform
4
+ module Cloudshaper
5
5
  # Implements DSL for a terraform provider, and a means of loading secrets.
6
6
  class Provider < StackElement
7
7
  def load_secrets(name)
8
8
  @secrets ||= {}
9
- @secrets[name.to_sym] = SECRETS[name.to_sym]
9
+ if SECRETS.has_key? name.to_sym
10
+ @secrets[name.to_sym] = SECRETS[name.to_sym]
11
+ end
10
12
  @secrets
11
13
  end
12
14
  end
@@ -1,6 +1,6 @@
1
- require 'terraform_dsl/aws/remote_s3'
1
+ require 'cloudshaper/aws/remote_s3'
2
2
 
3
- module Terraform
3
+ module Cloudshaper
4
4
  # Wrap 'remote' commands, such as config, pull, and push
5
5
  # This allows us to store state remotely using different providers
6
6
  class Remote < Command
@@ -1,6 +1,6 @@
1
- require 'terraform_dsl/stack_element'
1
+ require 'cloudshaper/stack_element'
2
2
 
3
- module Terraform
3
+ module Cloudshaper
4
4
  # Defines a terraform resource
5
5
  class Resource < StackElement
6
6
  attr_reader :resource_name
File without changes
@@ -1,9 +1,9 @@
1
- require 'terraform_dsl/stacks'
2
- require 'terraform_dsl/stack_modules'
3
- require 'terraform_dsl/command'
4
- require 'terraform_dsl/remote'
1
+ require 'cloudshaper/stacks'
2
+ require 'cloudshaper/stack_modules'
3
+ require 'cloudshaper/command'
4
+ require 'cloudshaper/remote'
5
5
 
6
- module Terraform
6
+ module Cloudshaper
7
7
  # Wrapper to instantiate a stack from a yaml definition
8
8
  class Stack
9
9
  class MalformedConfig < Exception; end
@@ -20,15 +20,17 @@ module Terraform
20
20
  :stack_dir, :stack_id, :remote
21
21
 
22
22
  def initialize(config)
23
- @name = config['name']
24
- @uuid = config['uuid']
25
- @description = config['description'] || ''
26
- @variables = config['variables'] || {}
23
+ @name = config.fetch('name')
24
+ @uuid = config.fetch('uuid')
27
25
  @remote = config['remote'] || {}
28
- @stack_id = "terraform_#{@name}_#{@uuid}"
29
- @module = StackModules.get(config['root'])
30
- @variables['terraform_stack_id'] = @stack_id
26
+ @description = config['description'] || ''
27
+
28
+ @stack_id = "cloudshaper#{@name}_#{@uuid}"
31
29
  @stack_dir = File.join(Stacks.dir, @stack_id)
30
+
31
+ @module = StackModules.get(config.fetch('root'))
32
+ @variables = config['variables'] || {}
33
+ @variables['cloudshaper_stack_id'] = @stack_id
32
34
  @module.build(@variables.map { |k, v| [k.to_sym, v] }.to_h)
33
35
  end
34
36
 
@@ -64,10 +66,6 @@ module Terraform
64
66
  Remote.new(self, :config).execute
65
67
  end
66
68
 
67
- def variables
68
- @module.variables
69
- end
70
-
71
69
  def to_s
72
70
  <<-eos
73
71
  Name: #{@name}
@@ -1,6 +1,6 @@
1
- require 'terraform_dsl/aws/tagging'
1
+ require 'cloudshaper/aws/tagging'
2
2
 
3
- module Terraform
3
+ module Cloudshaper
4
4
  # Defines a single terraform stack element, subclass for any element defined in terraform DSL
5
5
  class StackElement
6
6
  include Aws
@@ -24,7 +24,7 @@ module Terraform
24
24
  end
25
25
  add_field(symbol, args[0])
26
26
  else
27
- add_field(symbol, Terraform::StackElement.new(@module, &block).fields)
27
+ add_field(symbol, Cloudshaper::StackElement.new(@module, &block).fields)
28
28
  end
29
29
  end
30
30
 
@@ -38,6 +38,11 @@ module Terraform
38
38
  "${var.#{variable_name}}"
39
39
  end
40
40
 
41
+ # Reference a list variable
42
+ def var_list(variable_name)
43
+ ["${split(\",\",var.#{variable_name})}"]
44
+ end
45
+
41
46
  # Syntax to handle interpolation of resource variables
42
47
  def value_of(resource_type, resource_name, value_type)
43
48
  "${#{resource_type}.#{resource_name}.#{value_type}}"
@@ -1,14 +1,14 @@
1
1
  require 'json'
2
2
  require 'fileutils'
3
3
 
4
- require 'terraform_dsl/stack_modules'
5
- require 'terraform_dsl/resource'
6
- require 'terraform_dsl/provider'
7
- require 'terraform_dsl/variable'
8
- require 'terraform_dsl/module'
9
- require 'terraform_dsl/output'
10
-
11
- module Terraform
4
+ require 'cloudshaper/stack_modules'
5
+ require 'cloudshaper/resource'
6
+ require 'cloudshaper/provider'
7
+ require 'cloudshaper/variable'
8
+ require 'cloudshaper/module'
9
+ require 'cloudshaper/output'
10
+
11
+ module Cloudshaper
12
12
  # Stack Modules contain stack elements. A stack is made up of a root module, which may have submodules
13
13
  class StackModule
14
14
  class << self
@@ -30,13 +30,14 @@ module Terraform
30
30
  end
31
31
  end
32
32
 
33
- attr_accessor :secrets
33
+ attr_accessor :name, :secrets
34
34
 
35
- def initialize(_name, &block)
35
+ def initialize(name, &block)
36
+ @name = name
36
37
  @stack_elements = { resource: {}, provider: {}, variable: {}, output: {}, module: {} }
37
38
  @secrets = {}
38
39
  @block = block
39
- variable(:terraform_stack_id) {}
40
+ variable(:cloudshaper_stack_id) { default '' }
40
41
  end
41
42
 
42
43
  def clone
@@ -55,18 +56,24 @@ module Terraform
55
56
  JSON.pretty_generate(elements)
56
57
  end
57
58
 
58
- def variables
59
- elements[:variable]
59
+ def id
60
+ get(:cloudshaper_stack_id)
60
61
  end
61
62
 
62
- def outputs
63
- @stack_elements[:output]
63
+ def get(variable)
64
+ elements[:variable].fetch(variable)[:default]
64
65
  end
65
66
 
66
- def get(variable)
67
- @stack_elements[:variable].fetch(variable)[:default]
67
+ def each_variable(&b)
68
+ elements[:variable].each(&b)
68
69
  end
69
70
 
71
+ def get_resource(type, id)
72
+ @stack_elements[:resource].fetch(type).fetch(id)
73
+ end
74
+
75
+ private
76
+
70
77
  def elements
71
78
  elements = @stack_elements.clone
72
79
  variables = StackModule.flatten_variable_arrays(@stack_elements[:variable])
@@ -77,45 +84,45 @@ module Terraform
77
84
  elements
78
85
  end
79
86
 
80
- def id
81
- get(:terraform_stack_id)
82
- end
83
-
84
- private
85
-
86
87
  def register_resource(resource_type, name, &block)
87
88
  @stack_elements[:resource] ||= {}
88
89
  @stack_elements[:resource][resource_type.to_sym] ||= {}
89
- @stack_elements[:resource][resource_type.to_sym][name.to_sym] = Terraform::Resource.new(self, name, resource_type, &block).fields
90
+ @stack_elements[:resource][resource_type.to_sym][name.to_sym] = Cloudshaper::Resource.new(self, name, resource_type, &block).fields
90
91
  end
91
92
 
92
93
  def register_variable(name, &block)
93
- new_variable = Terraform::Variable.new(self, &block).fields
94
- unless @stack_elements[:variable].key?(name.to_sym)
95
- @stack_elements[:variable][name.to_sym] = { default: new_variable[:default] || '' }
94
+ return if @stack_elements[:variable].key?(name)
95
+
96
+ new_variable = Cloudshaper::Variable.new(self, &block).fields
97
+ if new_variable[:default].nil?
98
+ @stack_elements[:variable][name.to_sym] = {}
99
+ else
100
+ @stack_elements[:variable][name.to_sym] = {
101
+ default: new_variable[:default]
102
+ }
96
103
  end
97
104
  end
98
105
 
99
106
  def register_output(name, &block)
100
- new_output = Terraform::Output.new(self, &block).fields
107
+ new_output = Cloudshaper::Output.new(self, &block).fields
101
108
  @stack_elements[:output][name.to_sym] = new_output
102
109
  end
103
110
 
104
111
  def register_module(name, &block)
105
- new_module = Terraform::Module.new(self, name, &block).fields
112
+ new_module = Cloudshaper::Module.new(self, &block).fields
106
113
  @stack_elements[:module][name.to_sym] = new_module
107
114
  end
108
115
 
109
116
  def register_provider(name, &block)
110
- provider = Terraform::Provider.new(self, &block)
117
+ provider = Cloudshaper::Provider.new(self, &block)
111
118
  @secrets.merge!(provider.load_secrets(name))
112
119
  @stack_elements[:provider][name.to_sym] = provider.fields
113
120
  end
114
121
 
115
- alias_method :resource, :register_resource
116
- alias_method :variable, :register_variable
117
- alias_method :provider, :register_provider
118
- alias_method :output, :register_output
119
- alias_method :submodule, :register_module
122
+ alias_method :resource, :register_resource
123
+ alias_method :variable, :register_variable
124
+ alias_method :provider, :register_provider
125
+ alias_method :output, :register_output
126
+ alias_method :submodule, :register_module
120
127
  end
121
128
  end
@@ -1,4 +1,4 @@
1
- module Terraform
1
+ module Cloudshaper
2
2
  # Stack module factory, register a module and provide clones of it
3
3
  class StackModules
4
4
  class ModuleNotFound < StandardError; end
@@ -10,13 +10,17 @@ module Terraform
10
10
  @stack_modules[name.downcase] = stack_module
11
11
  end
12
12
 
13
+ def has?(stack_module_name)
14
+ @stack_modules.key?(stack_module_name.downcase)
15
+ end
16
+
13
17
  def get(stack_module_name)
14
18
  fail ModuleNotFound, "#{stack_module_name} module module not found" unless @stack_modules.key?(stack_module_name.downcase)
15
19
  @stack_modules[stack_module_name.downcase].clone
16
20
  end
17
21
 
18
22
  def reset!
19
- @stack_modules ||= {}
23
+ @stack_modules = {}
20
24
  end
21
25
  end
22
26
  reset!
@@ -1,9 +1,9 @@
1
1
  require 'yaml'
2
2
  require 'securerandom'
3
3
 
4
- require 'terraform_dsl/stack'
4
+ require 'cloudshaper/stack'
5
5
 
6
- module Terraform
6
+ module Cloudshaper
7
7
  # Singleton to keep track of stack templates
8
8
  class Stacks
9
9
  class MalformedConfig < StandardError; end
@@ -30,10 +30,6 @@ module Terraform
30
30
  end
31
31
  end
32
32
 
33
- def uuid
34
- "#{Time.now.utc.to_i}_#{SecureRandom.urlsafe_base64(6)}"
35
- end
36
-
37
33
  def base_config
38
34
  {
39
35
  'common' => {},
@@ -44,7 +40,7 @@ module Terraform
44
40
  def base_stack_config
45
41
  {
46
42
  'name' => 'SET_NAME',
47
- 'uuid' => uuid,
43
+ 'uuid' => SecureRandom.uuid,
48
44
  'description' => 'SET_A_DESCRIPTION',
49
45
  'root' => 'SET_A_TEMPLATE',
50
46
  'variables' => {}
@@ -0,0 +1,6 @@
1
+ require 'cloudshaper/stack_element'
2
+
3
+ module Cloudshaper
4
+ class Variable < StackElement
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ module Cloudshaper
2
+ VERSION = '0.0.5'
3
+ end
@@ -0,0 +1,4 @@
1
+ require 'cloudshaper/stack'
2
+ require 'cloudshaper/stacks'
3
+ require 'cloudshaper/stack_module'
4
+ require 'cloudshaper/version'
@@ -1,32 +1,32 @@
1
- require 'terraform_dsl'
1
+ require 'cloudshaper'
2
2
 
3
3
  namespace 'terraform' do
4
4
  desc 'Loads available stack modules'
5
5
  task :load do
6
- Terraform::Stacks.load
6
+ Cloudshaper::Stacks.load
7
7
  end
8
8
 
9
9
  desc 'Initialize stacks.yml if it does not exist'
10
10
  task :init do
11
- Terraform::Stacks.init
11
+ Cloudshaper::Stacks.init
12
12
  end
13
13
 
14
14
  desc 'Fetch modules for a stack'
15
15
  task :get, [:name] => :load do |_t, args|
16
- stack = Terraform::Stacks.stacks[args[:name]]
16
+ stack = Cloudshaper::Stacks.stacks[args[:name]]
17
17
  stack.get
18
18
  end
19
19
 
20
20
  desc 'Fetch modules for all stacks'
21
21
  task get_all: :load do
22
- Terraform::Stacks.stacks.each do |_name, stack|
22
+ Cloudshaper::Stacks.stacks.each do |_name, stack|
23
23
  stack.get
24
24
  end
25
25
  end
26
26
 
27
27
  desc 'List all available stacks'
28
28
  task list: :load do
29
- Terraform::Stacks.stacks.each do |name, _stack|
29
+ Cloudshaper::Stacks.stacks.each do |name, _stack|
30
30
  puts name
31
31
  end
32
32
  end
@@ -34,14 +34,14 @@ namespace 'terraform' do
34
34
  desc 'Show details about a stack by name'
35
35
  task :show, [:name] => :load do |_t, args|
36
36
  fail 'Specify a name' unless args[:name]
37
- stack = Terraform::Stacks.stacks[args[:name]]
37
+ stack = Cloudshaper::Stacks.stacks[args[:name]]
38
38
  puts stack
39
39
  stack.plan
40
40
  end
41
41
 
42
42
  desc 'Show all pending stack changes'
43
43
  task show_all: [:load, :get_all] do
44
- Terraform::Stacks.stacks.each do |_name, stack|
44
+ Cloudshaper::Stacks.stacks.each do |_name, stack|
45
45
  puts stack
46
46
  stack.plan
47
47
  end
@@ -50,20 +50,20 @@ namespace 'terraform' do
50
50
  desc 'Show pending changes for a stack'
51
51
  task :plan, [:name] => :load do |_t, args|
52
52
  fail 'Specify a name' unless args[:name]
53
- stack = Terraform::Stacks.stacks[args[:name]]
53
+ stack = Cloudshaper::Stacks.stacks[args[:name]]
54
54
  stack.plan
55
55
  end
56
56
 
57
57
  desc 'Apply pending changes for a stack'
58
58
  task :apply, [:name] => :load do |_t, args|
59
59
  fail 'Specify a name' unless args[:name]
60
- stack = Terraform::Stacks.stacks[args[:name]]
60
+ stack = Cloudshaper::Stacks.stacks[args[:name]]
61
61
  stack.apply
62
62
  end
63
63
 
64
64
  desc 'Apply all pending stack changes'
65
65
  task apply_all: :load do
66
- Terraform::Stacks.stacks.each do |_name, stack|
66
+ Cloudshaper::Stacks.stacks.each do |_name, stack|
67
67
  puts stack
68
68
  stack.apply
69
69
  end
@@ -72,19 +72,19 @@ namespace 'terraform' do
72
72
  desc 'Destroy a stack'
73
73
  task :destroy, [:name] => :load do |_t, args|
74
74
  fail 'Specify a name' unless args[:name]
75
- stack = Terraform::Stacks.stacks[args[:name]]
75
+ stack = Cloudshaper::Stacks.stacks[args[:name]]
76
76
  stack.destroy
77
77
  end
78
78
 
79
79
  desc 'Push stack state to remote location'
80
80
  task :push, [:name] => [:load, :remote_config] do |_t, args|
81
- stack = Terraform::Stacks.stacks[args[:name]]
81
+ stack = Cloudshaper::Stacks.stacks[args[:name]]
82
82
  stack.push
83
83
  end
84
84
 
85
85
  desc 'Push stack states to remote location'
86
86
  task push_all: [:load, :remote_config_all] do
87
- Terraform::Stacks.stacks.each do |_name, stack|
87
+ Cloudshaper::Stacks.stacks.each do |_name, stack|
88
88
  puts stack
89
89
  stack.push
90
90
  end
@@ -92,13 +92,13 @@ namespace 'terraform' do
92
92
 
93
93
  desc 'Pulls stack state from remote location'
94
94
  task :pull, [:name] => [:load, :remote_config] do |_t, args|
95
- stack = Terraform::Stacks.stacks[args[:name]]
95
+ stack = Cloudshaper::Stacks.stacks[args[:name]]
96
96
  stack.pull
97
97
  end
98
98
 
99
99
  desc 'Pulls stack states from remote location'
100
100
  task pull_all: [:load, :remote_config_all] do
101
- Terraform::Stacks.stacks.each do |_name, stack|
101
+ Cloudshaper::Stacks.stacks.each do |_name, stack|
102
102
  puts stack
103
103
  stack.pull
104
104
  end
@@ -106,13 +106,13 @@ namespace 'terraform' do
106
106
 
107
107
  desc 'Sets up remote config for a stack'
108
108
  task :remote_config, [:name] => [:load] do |_t, args|
109
- stack = Terraform::Stacks.stacks[args[:name]]
109
+ stack = Cloudshaper::Stacks.stacks[args[:name]]
110
110
  stack.remote_config
111
111
  end
112
112
 
113
113
  desc 'Sets up remote config for all stacks that support it'
114
114
  task remote_config_all: :load do
115
- Terraform::Stacks.stacks.each do |_name, stack|
115
+ Cloudshaper::Stacks.stacks.each do |_name, stack|
116
116
  puts stack
117
117
  stack.remote_config
118
118
  end
@@ -120,7 +120,7 @@ namespace 'terraform' do
120
120
 
121
121
  desc 'Generate a UUID for a stack, so stacks do not clobber each other'
122
122
  task :uuid do
123
- uuid = Terraform::Stacks.uuid
123
+ uuid = Cloudshaper::Stacks.uuid
124
124
  puts "uuid: #{uuid}"
125
125
  puts 'Add this as a field to a new stack to prevent clobbering stacks with the same name'
126
126
  end
@@ -1,7 +1,18 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class StackModuleTest < Minitest::Test
4
- include Terraform
4
+ include Cloudshaper
5
+
6
+ def setup
7
+ StackModules.reset!
8
+ end
9
+
10
+ def test_multiple_modules_same_name_raises_exception
11
+ StackModule.define 'same_name_test'
12
+ assert_raises(StackModules::ModuleAlreadyRegistered) do
13
+ StackModule.define 'same_name_test'
14
+ end
15
+ end
5
16
 
6
17
  def test_multiple_instantiations_of_module
7
18
  StackModule.define 'factory_instantiation_test' do
@@ -19,48 +30,45 @@ class StackModuleTest < Minitest::Test
19
30
  first.build(ports: '80')
20
31
  second.build(ports: '443')
21
32
 
22
- sg_first = first.elements[:resource][:aws_security_group][:a]
23
- sg_second = second.elements[:resource][:aws_security_group][:a]
33
+ sg_first = first.get_resource(:aws_security_group, :a)
34
+ sg_second = second.get_resource(:aws_security_group, :a)
24
35
 
25
36
  assert_equal '80', sg_first[:ingress].fetch(:from_port)
26
37
  assert_equal '443', sg_second[:ingress].fetch(:from_port)
27
38
  end
28
39
 
29
- # it 'should register variables with default values' do
30
- # mod = StackModule.define('variable_register_default') { variable(:name) { default 'default' } }
31
- # mod.build
40
+ def test_module_build_registers_variable_with_defaults
41
+ mod = StackModule.define('variable_register_default') do
42
+ variable(:name) { default 'spam' }
43
+ end
44
+ mod.build
32
45
 
33
- # expect(mod.variables).to be_a(Hash)
34
- # expect(mod.variables).to include(:name)
35
- # expect(mod.variables[:name]).to be_a(Hash)
36
- # expect(mod.variables[:name]).to include(:default)
37
- # expect(mod.variables[:name][:default]).to eq('default')
38
- # end
46
+ assert_equal 'spam', mod.get(:name)
47
+ end
39
48
 
40
- # it 'should register variables without default values' do
41
- # mod = StackModule.define('variable_register_nodefault') { variable(:name) {} }
42
- # mod.build
49
+ def test_required_variable_has_no_default_value
50
+ mod = StackModule.define('resource_required_variable') { variable(:name) {} }
51
+ mod.build
43
52
 
44
- # expect(mod.variables).to be_a(Hash)
45
- # expect(mod.variables).to include(:name)
46
- # expect(mod.variables[:name]).to be_a(Hash)
47
- # expect(mod.variables[:name]).to include(:default)
48
- # expect(mod.variables[:name][:default]).to eq('')
49
- # end
53
+ assert_nil mod.get(:name)
54
+ end
50
55
 
51
- # it 'should accept variables at runtime' do
52
- # mod = StackModule.define('variable_override') { variable(:name) { default 'default' } }
53
- # mod.build(name: 'not-default')
56
+ def test_module_build_registers_variables_at_runtime
57
+ mod = StackModule.define('variable_override') do
58
+ variable(:name) { default 'default' }
59
+ end
60
+ mod.build(name: 'not-default')
54
61
 
55
- # expect(mod.variables[:name][:default]).to eql('not-default')
56
- # end
57
- # end
62
+ assert_equal 'not-default', mod.get(:name)
63
+ end
58
64
 
59
65
  def test_register_resource
60
- mod = StackModule.define('register_resource') { resource('aws_instance', :a) { default 'default' } }
66
+ mod = StackModule.define('register_resource') do
67
+ resource('aws_instance', :a) { default 'default' }
68
+ end
61
69
  mod.build
62
70
 
63
- instance = mod.elements.fetch(:resource).fetch(:aws_instance).fetch(:a)
71
+ instance = mod.get_resource(:aws_instance, :a)
64
72
  assert_equal 'default', instance.fetch(:default)
65
73
  end
66
74
 
@@ -74,7 +82,7 @@ class StackModuleTest < Minitest::Test
74
82
  end
75
83
  mod.build
76
84
 
77
- instance = mod.elements.fetch(:resource).fetch(:aws_instance).fetch(:a)
85
+ instance = mod.get_resource(:aws_instance, :a)
78
86
  assert_equal 'root', instance.fetch(:connection).fetch(:user)
79
87
  end
80
88
 
@@ -90,7 +98,7 @@ class StackModuleTest < Minitest::Test
90
98
  end
91
99
  mod.build
92
100
 
93
- instance = mod.elements.fetch(:resource).fetch(:aws_instance).fetch(:a)
101
+ instance = mod.get_resource(:aws_instance, :a)
94
102
  provisioner = instance.fetch(:provisioner).first
95
103
  connection = provisioner.fetch(:file).fetch(:connection)
96
104
  assert_equal 'root', connection.fetch(:user)
@@ -105,7 +113,7 @@ class StackModuleTest < Minitest::Test
105
113
  end
106
114
  mod.build
107
115
 
108
- sg = mod.elements.fetch(:resource).fetch(:aws_security_group).fetch(:a)
116
+ sg = mod.get_resource(:aws_security_group, :a)
109
117
  ingress = sg.fetch(:ingress)
110
118
 
111
119
  assert_kind_of Array, ingress
@@ -123,30 +131,8 @@ class StackModuleTest < Minitest::Test
123
131
  end
124
132
  mod.build(ports: '22,80,443')
125
133
 
126
-
127
- sg = mod.elements.fetch(:resource).fetch(:aws_security_group).fetch(:a)
134
+ sg = mod.get_resource(:aws_security_group, :a)
128
135
  ingress = sg.fetch(:ingress)
129
136
  assert_equal [{from_port: "22"}, {from_port: "80"}, {from_port: "443"}], ingress
130
137
  end
131
-
132
-
133
- # it 'it should be able to access overridden default variables at runtime' do
134
- # mod = StackModule.define 'resource_overriden_runtime_variable' do
135
- # variable(:ports) { default '22' }
136
- # resource 'aws_security_group', :a do
137
- # get(:ports).split(',').each do |port|
138
- # ingress { from_port port }
139
- # end
140
- # end
141
- # end
142
-
143
- # mod.build(ports: '22,80,443')
144
-
145
- # sg = mod.elements[:resource][:aws_security_group][:a]
146
- # expect(sg).to include(:ingress)
147
- # expect(sg[:ingress]).to be_a(Array)
148
- # expect(sg[:ingress].first).to eq(from_port: '22')
149
- # expect(sg[:ingress].last).to eq(from_port: '443')
150
- # end
151
- # end
152
138
  end
data/test/test_helper.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  require 'minitest/autorun'
2
2
  require 'minitest/pride'
3
3
 
4
- require 'terraform_dsl'
4
+ require 'cloudshaper'
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudshaper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dale Hamel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-10 00:00:00.000000000 Z
11
+ date: 2015-06-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rake
14
+ name: thor
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '10.4'
19
+ version: 0.19.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '10.4'
26
+ version: 0.19.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.4'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.4'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: minitest
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -52,10 +66,11 @@ dependencies:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
68
  version: '5.6'
55
- description: Terraform DSL provides a syntax for managing terraform infrastructure
56
- entirely in git
69
+ description: Cloudshaper provides a syntax for managing terraform infrastructure entirely
70
+ in git
57
71
  email: dale.hamel@srvthe.net
58
- executables: []
72
+ executables:
73
+ - cloudshaper
59
74
  extensions: []
60
75
  extra_rdoc_files: []
61
76
  files:
@@ -67,6 +82,8 @@ files:
67
82
  - README.md
68
83
  - Rakefile
69
84
  - TODO.md
85
+ - bin/cloudshaper
86
+ - cloudshaper.gemspec
70
87
  - examples/secretconfig/README.md
71
88
  - examples/secretconfig/atlas.json
72
89
  - examples/secretconfig/aws.json
@@ -77,30 +94,29 @@ files:
77
94
  - examples/secretconfig/heroku.json
78
95
  - examples/secretconfig/mailgun.json
79
96
  - examples/simple_app.rb
80
- - lib/tasks/tasks.rb
97
+ - lib/cloudshaper.rb
98
+ - lib/cloudshaper/aws/remote_s3.rb
99
+ - lib/cloudshaper/aws/tagging.rb
100
+ - lib/cloudshaper/cli.rb
101
+ - lib/cloudshaper/command.rb
102
+ - lib/cloudshaper/module.rb
103
+ - lib/cloudshaper/output.rb
104
+ - lib/cloudshaper/provider.rb
105
+ - lib/cloudshaper/remote.rb
106
+ - lib/cloudshaper/resource.rb
107
+ - lib/cloudshaper/secrets.rb
108
+ - lib/cloudshaper/stack.rb
109
+ - lib/cloudshaper/stack_element.rb
110
+ - lib/cloudshaper/stack_module.rb
111
+ - lib/cloudshaper/stack_modules.rb
112
+ - lib/cloudshaper/stacks.rb
113
+ - lib/cloudshaper/variable.rb
114
+ - lib/cloudshaper/version.rb
81
115
  - lib/tasks/terraform.rake
82
- - lib/terraform_dsl.rb
83
- - lib/terraform_dsl/aws/remote_s3.rb
84
- - lib/terraform_dsl/aws/tagging.rb
85
- - lib/terraform_dsl/command.rb
86
- - lib/terraform_dsl/module.rb
87
- - lib/terraform_dsl/output.rb
88
- - lib/terraform_dsl/provider.rb
89
- - lib/terraform_dsl/remote.rb
90
- - lib/terraform_dsl/resource.rb
91
- - lib/terraform_dsl/secrets.rb
92
- - lib/terraform_dsl/stack.rb
93
- - lib/terraform_dsl/stack_element.rb
94
- - lib/terraform_dsl/stack_module.rb
95
- - lib/terraform_dsl/stack_modules.rb
96
- - lib/terraform_dsl/stacks.rb
97
- - lib/terraform_dsl/variable.rb
98
- - lib/terraform_dsl/version.rb
99
- - terraform_dsl.gemspec
100
116
  - test/stack_module_test.rb
101
117
  - test/stack_test.rb
102
118
  - test/test_helper.rb
103
- homepage: https://rubygems.org/gems/terraform_dsl
119
+ homepage: https://github.com/dalehamel/cloudshaper
104
120
  licenses:
105
121
  - MIT
106
122
  metadata: {}
data/lib/tasks/tasks.rb DELETED
@@ -1,12 +0,0 @@
1
- require 'rake'
2
-
3
- module Terraform
4
- # Loads all rake tasks when terraform_dsl is included by a rake script
5
- class Tasks
6
- def self.loadall
7
- Dir.glob("#{File.join(File.dirname(__dir__), 'tasks')}/*.rake").each { |r| load r }
8
- template_path = ENV['TERRAFORM_TEMPLATE_PATH'] || 'templates'
9
- Dir.glob("#{File.join(Dir.pwd, template_path)}/*.rb").each { |t| require_relative t }
10
- end
11
- end
12
- end
@@ -1,38 +0,0 @@
1
- require 'terraform_dsl/stack_element'
2
- require 'terraform_dsl/stack_modules'
3
- require 'terraform_dsl/stack_module'
4
- require 'terraform_dsl/stacks'
5
-
6
- module Terraform
7
- # Supports terraform 'modules'. In our case, we call them submodules because
8
- # Module is a ruby keyword. We also support directly referencing other ruby-defined modules.
9
- class Module < StackElement
10
- def initialize(parent_module, module_name, &block)
11
- super(parent_module, &block)
12
-
13
- if @fields[:source].match(/^module_/)
14
- build_submodule(parent_module, module_name)
15
- end
16
- end
17
-
18
- private
19
-
20
- def build_submodule(parent_module, module_name)
21
- generated = generate_child_module(parent_module)
22
- module_path = File.join(Stacks.dir, parent_module.id, module_name.to_s)
23
- FileUtils.mkdir_p(module_path)
24
- File.open(File.join(module_path, 'stack_module.tf.json'), 'w') { |f| f.write(generated) }
25
- @fields[:source] = File.expand_path(module_path)
26
- end
27
-
28
- def generate_child_module(parent_module)
29
- variables = @fields.clone
30
- variables.delete(:source)
31
- variables[:terraform_stack_id] = parent_module.id
32
- child_name = @fields[:source].gsub(/^module_/, '')
33
- child_module = StackModules.get(child_name)
34
- child_module.build(variables)
35
- child_module.generate
36
- end
37
- end
38
- end
@@ -1,6 +0,0 @@
1
- require 'terraform_dsl/stack_element'
2
-
3
- module Terraform
4
- class Output < StackElement
5
- end
6
- end
@@ -1,6 +0,0 @@
1
- require 'terraform_dsl/stack_element'
2
-
3
- module Terraform
4
- class Variable < StackElement
5
- end
6
- end
@@ -1,3 +0,0 @@
1
- module Terraform
2
- VERSION = '0.0.4'
3
- end
data/lib/terraform_dsl.rb DELETED
@@ -1,6 +0,0 @@
1
- require 'terraform_dsl/stack'
2
- require 'terraform_dsl/stacks'
3
- require 'terraform_dsl/stack_module'
4
- require 'terraform_dsl/version'
5
-
6
- require 'tasks/tasks' if defined? Rake