cloudshaper 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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