wombat-cli 0.3.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/.travis.yml +27 -0
  4. data/CHANGELOG.md +15 -1
  5. data/Gemfile +3 -0
  6. data/README.md +2 -2
  7. data/Rakefile +25 -0
  8. data/bin/wombat +1 -1
  9. data/generator_files/cookbooks/automate/metadata.rb +1 -1
  10. data/generator_files/cookbooks/automate/recipes/update-users.rb +1 -1
  11. data/generator_files/cookbooks/chef_server/recipes/default.rb +24 -11
  12. data/generator_files/cookbooks/workstation/.kitchen.ec2.yml +2 -1
  13. data/generator_files/cookbooks/workstation/metadata.rb +1 -1
  14. data/generator_files/cookbooks/workstation/recipes/default.rb +1 -2
  15. data/generator_files/cookbooks/workstation/templates/default/ise_profile.ps1.erb +2 -2
  16. data/generator_files/cookbooks/workstation/test/integration/default/workstation_spec.rb +4 -4
  17. data/generator_files/packer/automate.json +129 -107
  18. data/generator_files/packer/build-node.json +134 -112
  19. data/generator_files/packer/chef-server.json +130 -108
  20. data/generator_files/packer/compliance.json +126 -104
  21. data/generator_files/packer/infranodes-windows.json +136 -97
  22. data/generator_files/packer/infranodes.json +127 -106
  23. data/generator_files/packer/workstation.json +134 -95
  24. data/generator_files/templates/arm.json.erb +576 -0
  25. data/generator_files/wombat.yml +6 -2
  26. data/lib/wombat/aws.rb +67 -0
  27. data/lib/wombat/build.rb +273 -184
  28. data/lib/wombat/cli.rb +182 -147
  29. data/lib/wombat/common.rb +228 -220
  30. data/lib/wombat/crypto.rb +65 -0
  31. data/lib/wombat/delete.rb +48 -18
  32. data/lib/wombat/deploy.rb +147 -34
  33. data/lib/wombat/init.rb +21 -19
  34. data/lib/wombat/latest.rb +27 -0
  35. data/lib/wombat/output.rb +31 -30
  36. data/lib/wombat/update.rb +13 -10
  37. data/lib/wombat/version.rb +1 -1
  38. data/spec/functional/common_spec.rb +26 -0
  39. data/spec/spec_helper.rb +103 -0
  40. data/spec/unit/common_spec.rb +116 -0
  41. data/wombat-cli.gemspec +2 -1
  42. metadata +36 -11
  43. /data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/{delivery.crt → automate.crt} +0 -0
  44. /data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/{delivery.key → automate.key} +0 -0
  45. /data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/{chef-server.crt → chef.crt} +0 -0
  46. /data/generator_files/cookbooks/workstation/test/fixtures/cookbooks/mock_data/files/{chef-server.key → chef.key} +0 -0
data/lib/wombat/deploy.rb CHANGED
@@ -1,47 +1,160 @@
1
1
  require 'wombat/common'
2
2
  require 'aws-sdk'
3
+ require 'ms_rest_azure'
4
+ require 'azure_mgmt_resources'
3
5
 
4
- class DeployRunner
5
- include Common
6
+ module Wombat
7
+ class DeployRunner
8
+ include Wombat::Common
6
9
 
7
- attr_reader :stack, :cloud, :lock_opt, :template_opt
10
+ attr_reader :stack, :cloud, :lock_opt, :template_opt
11
+ attr_accessor :resource_management_client
8
12
 
9
- def initialize(opts)
10
- @stack = opts.stack
11
- @cloud = opts.cloud.nil? ? "aws" : opts.cloud
12
- @lock_opt = opts.update_lock
13
- @template_opt = opts.update_template
14
- end
13
+ def initialize(opts)
14
+ @stack = opts.stack
15
+ @cloud = opts.cloud.nil? ? "aws" : opts.cloud
16
+ @lock_opt = opts.update_lock
17
+ @template_opt = opts.update_template
18
+ @azure_async = opts.azure_async
19
+ end
15
20
 
16
- def start
17
- case cloud
18
- when 'aws'
21
+ def start
19
22
  update_lock(cloud) if lock_opt
20
23
  update_template(cloud) if template_opt
21
24
  create_stack(stack)
22
25
  end
23
- end
24
26
 
25
- private
26
-
27
- def create_stack(stack)
28
- template_file = File.read("#{conf['stack_dir']}/#{stack}.json")
29
- cfn = Aws::CloudFormation::Client.new(region: lock['aws']['region'])
30
-
31
- banner("Creating CloudFormation stack")
32
- resp = cfn.create_stack({
33
- stack_name: "#{stack}",
34
- template_body: template_file,
35
- capabilities: ["CAPABILITY_IAM"],
36
- on_failure: "DELETE",
37
- parameters: [
38
- {
39
- parameter_key: "KeyName",
40
- parameter_value: lock['aws']['keypair'],
41
- }
42
- ]
43
- })
44
- puts "Created: #{resp.stack_id}"
45
- end
27
+ private
28
+
29
+ def create_stack(stack)
30
+
31
+ # Deploy the template to the correct stack
32
+ case @cloud
33
+ when "aws"
34
+
35
+ template_file = File.read("#{conf['stack_dir']}/#{stack}.json")
36
+ cfn = Aws::CloudFormation::Client.new(region: lock['aws']['region'])
37
+
38
+ banner("Creating CloudFormation stack")
39
+ resp = cfn.create_stack({
40
+ stack_name: "#{stack}",
41
+ template_body: template_file,
42
+ capabilities: ["CAPABILITY_IAM"],
43
+ on_failure: "DELETE",
44
+ parameters: [
45
+ {
46
+ parameter_key: "KeyName",
47
+ parameter_value: lock['aws']['keypair'],
48
+ }
49
+ ]
50
+ })
51
+ puts "Created: #{resp.stack_id}"
52
+ when "azure"
53
+
54
+ banner("Creating Azure RM stack")
55
+
56
+ # determine the path to the arm template
57
+ template_file = File.read("#{conf['stack_dir']}/#{stack}.json")
58
+
59
+ # determine the name of the deployment
60
+ deployment_name = format('deploy-%s', Time.now().to_i)
61
+
62
+ # Create the connection to Azure using the information in the environment variables
63
+ subscription_id = ENV['AZURE_SUBSCRIPTION_ID']
64
+ tenant_id = ENV['AZURE_TENANT_ID']
65
+ client_id = ENV['AZURE_CLIENT_ID']
66
+ client_secret = ENV['AZURE_CLIENT_SECRET']
67
+
68
+ token_provider = MsRestAzure::ApplicationTokenProvider.new(tenant_id, client_id, client_secret)
69
+ azure_conn = MsRest::TokenCredentials.new(token_provider)
70
+
71
+ # Create a resource client so that the template can be deployed
72
+ @resource_management_client = Azure::ARM::Resources::ResourceManagementClient.new(azure_conn)
73
+ @resource_management_client.subscription_id = subscription_id
46
74
 
75
+ # Create the deployment definition
76
+ deployment = Azure::ARM::Resources::Models::Deployment.new
77
+ deployment.properties = Azure::ARM::Resources::Models::DeploymentProperties.new
78
+ deployment.properties.mode = Azure::ARM::Resources::Models::DeploymentMode::Incremental
79
+ deployment.properties.template = JSON.parse(template_file)
80
+
81
+ # Perform the deployment to the named resource group
82
+ begin
83
+ resource_management_client.deployments.begin_create_or_update_async(stack, deployment_name, deployment).value!
84
+ rescue MsRestAzure::AzureOperationError => operation_error
85
+ rest_error = operation_error.body['error']
86
+ deployment_active = rest_error['code'] == 'DeploymentActive'
87
+ if deployment_active
88
+ info format("Deployment for resource group '%s' is ongoing", stack)
89
+ else
90
+ warn rest_error
91
+ raise operation_error
92
+ end
93
+ end
94
+
95
+ # Monitor the deployment
96
+ if @azure_async
97
+ info "Deployment operation accepted. Use the Azure Portal to check progress"
98
+ else
99
+ follow_azure_deployment(stack, deployment_name)
100
+ end
101
+
102
+ end
103
+ end
104
+
105
+ # Track the progress of the deployment in Azure
106
+ #
107
+ # ===== Attributes
108
+ #
109
+ # * +rg_name+ - Name of the resource group being deployed to
110
+ # * +deployment_name+ - Name of the deployment that is currently being processed
111
+ def follow_azure_deployment(rg_name, deployment_name)
112
+
113
+ end_provisioning_states = 'Canceled,Failed,Deleted,Succeeded'
114
+ end_provisioning_state_reached = false
115
+
116
+ until end_provisioning_state_reached
117
+ list_outstanding_deployment_operations(rg_name, deployment_name)
118
+ info ""
119
+ sleep 10
120
+ deployment_provisioning_state = deployment_state(rg_name, deployment_name)
121
+ end_provisioning_state_reached = end_provisioning_states.split(',').include?(deployment_provisioning_state)
122
+ end
123
+ info format("Resource Template deployment reached end state of %s", deployment_provisioning_state)
124
+ end
125
+
126
+ # Get a list of the outstanding deployment operations
127
+ #
128
+ # ===== Attributes
129
+ #
130
+ # * +rg_name+ - Name of the resource group being deployed to
131
+ # * +deployment_name+ - Name of the deployment that is currently being processed
132
+ def list_outstanding_deployment_operations(rg_name, deployment_name)
133
+ end_operation_states = 'Failed,Succeeded'
134
+ deployment_operations = resource_management_client.deployment_operations.list(rg_name, deployment_name)
135
+ deployment_operations.each do |val|
136
+ resource_provisioning_state = val.properties.provisioning_state
137
+ unless val.properties.target_resource.nil?
138
+ resource_name = val.properties.target_resource.resource_name
139
+ resource_type = val.properties.target_resource.resource_type
140
+ end
141
+ end_operation_state_reached = end_operation_states.split(',').include?(resource_provisioning_state)
142
+ unless end_operation_state_reached
143
+ info format("resource %s '%s' provisioning status is %s", resource_type, resource_name, resource_provisioning_state)
144
+ end
145
+ end
146
+ end
147
+
148
+ # Get the state of the specified deployment
149
+ #
150
+ # ===== Attributes
151
+ #
152
+ # * +rg_name+ - Name of the resource group being deployed to
153
+ # * +deployment_name+ - Name of the deployment that is currently being processed
154
+ def deployment_state(rg_name, deployment_name)
155
+ deployments = resource_management_client.deployments.get(rg_name, deployment_name)
156
+ deployments.properties.provisioning_state
157
+ end
158
+
159
+ end
47
160
  end
data/lib/wombat/init.rb CHANGED
@@ -1,29 +1,31 @@
1
1
  require 'wombat/common'
2
2
 
3
- class InitRunner
4
- include Common
3
+ module Wombat
4
+ class InitRunner
5
+ include Wombat::Common
5
6
 
6
- attr_reader :path
7
+ attr_reader :path
7
8
 
8
- def initialize(opts)
9
- @path = opts.path.nil? ? Dir.pwd : opts.path
10
- end
9
+ def initialize(opts)
10
+ @path = opts.path.nil? ? Dir.pwd : opts.path
11
+ end
11
12
 
12
- def start
13
- copy_files(path)
14
- end
13
+ def start
14
+ copy_files(path)
15
+ end
15
16
 
16
- private
17
+ private
17
18
 
18
- def copy_files(path)
19
- p = path == Dir.pwd ? '.' : path
20
- gen_dir = "#{File.expand_path("../..", File.dirname(__FILE__))}/generator_files"
21
- Dir["#{gen_dir}/*"].each do |source|
22
- if !File.exist?("#{p}/#{File.basename(source)}")
23
- banner("create: #{p}/#{File.basename(source)}")
24
- FileUtils.cp_r source, path
25
- else
26
- warn("#{p}/#{File.basename(source)} already exists")
19
+ def copy_files(path)
20
+ p = path == Dir.pwd ? '.' : path
21
+ gen_dir = "#{File.expand_path("../..", File.dirname(__FILE__))}/generator_files"
22
+ Dir["#{gen_dir}/*"].each do |source|
23
+ if !File.exist?("#{p}/#{File.basename(source)}")
24
+ banner("create: #{p}/#{File.basename(source)}")
25
+ FileUtils.cp_r source, path
26
+ else
27
+ warn("#{p}/#{File.basename(source)} already exists")
28
+ end
27
29
  end
28
30
  end
29
31
  end
@@ -0,0 +1,27 @@
1
+ require "wombat/common"
2
+ require "wombat/aws"
3
+
4
+ # http://docs.aws.amazon.com/sdkforruby/api/Aws/EC2/Client.html#describe_images-instance_method
5
+ # https://github.com/test-kitchen/kitchen-ec2/blob/aa8e7f2cf9bfbb10fa4057f3297c2a20dc079f7b/lib/kitchen/driver/aws/standard_platform.rb
6
+ # https://github.com/test-kitchen/kitchen-ec2/blob/aa8e7f2cf9bfbb10fa4057f3297c2a20dc079f7b/lib/kitchen/driver/aws/standard_platform/ubuntu.rb
7
+
8
+ module Wombat
9
+ class LatestRunner
10
+ include Wombat::Common
11
+ include Wombat::Aws
12
+
13
+ attr_reader :stack, :cloud, :lock_opt, :template_opt
14
+
15
+ def initialize(opts)
16
+ @cloud = opts.cloud.nil? ? "aws" : opts.cloud
17
+ end
18
+
19
+ def start
20
+ if cloud =~ /aws/
21
+ find_latest_amis
22
+ else
23
+ puts "Unsupported for #{cloud}"
24
+ end
25
+ end
26
+ end
27
+ end
data/lib/wombat/output.rb CHANGED
@@ -1,45 +1,46 @@
1
1
  require 'wombat/common'
2
2
  require 'aws-sdk'
3
3
 
4
- class OutputRunner
4
+ module Wombat
5
+ class OutputRunner
6
+ include Wombat::Common
5
7
 
6
- include Common
8
+ attr_reader :stack
7
9
 
8
- attr_reader :stack
9
-
10
- def initialize(opts)
11
- @stack = opts.stack
12
- end
10
+ def initialize(opts)
11
+ @stack = opts.stack
12
+ end
13
13
 
14
- def start
15
- cfn_workstation_ips(stack)
16
- end
14
+ def start
15
+ cfn_workstation_ips(stack)
16
+ end
17
17
 
18
- private
18
+ private
19
19
 
20
- def cfn_workstation_ips(stack)
21
- ec2 = Aws::EC2::Resource.new
22
- instances = cfn_stack_instances(stack)
23
- instances.each do |name, id|
24
- instance = ec2.instance(id)
25
- if /Workstation/.match(name)
26
- puts "#{name} (#{id}) => #{instance.public_ip_address}"
20
+ def cfn_workstation_ips(stack)
21
+ ec2 = Aws::EC2::Resource.new
22
+ instances = cfn_stack_instances(stack)
23
+ instances.each do |name, id|
24
+ instance = ec2.instance(id)
25
+ if /Workstation/.match(name)
26
+ puts "#{name} (#{id}) => #{instance.public_ip_address}"
27
+ end
27
28
  end
28
29
  end
29
- end
30
-
31
- def cfn_stack_instances(stack)
32
- cfn = Aws::CloudFormation::Client.new
33
- resp = cfn.describe_stack_resources({
34
- stack_name: stack,
35
- })
36
30
 
37
- instances = {}
38
- resp.stack_resources.map do |resource|
39
- if resource.resource_type == 'AWS::EC2::Instance'
40
- instances[resource.logical_resource_id] = resource.physical_resource_id
31
+ def cfn_stack_instances(stack)
32
+ cfn = Aws::CloudFormation::Client.new
33
+ resp = cfn.describe_stack_resources({
34
+ stack_name: stack,
35
+ })
36
+
37
+ instances = {}
38
+ resp.stack_resources.map do |resource|
39
+ if resource.resource_type == 'AWS::EC2::Instance'
40
+ instances[resource.logical_resource_id] = resource.physical_resource_id
41
+ end
41
42
  end
43
+ instances
42
44
  end
43
- instances
44
45
  end
45
46
  end
data/lib/wombat/update.rb CHANGED
@@ -1,17 +1,20 @@
1
1
  require 'wombat/common'
2
2
 
3
- class UpdateRunner
4
- include Common
3
+ module Wombat
4
+ class UpdateRunner
5
+ include Wombat::Common
5
6
 
6
- attr_reader :cloud, :update_file
7
+ attr_reader :cloud, :update_file
7
8
 
8
- def initialize(opts)
9
- @cloud = opts.cloud.nil? ? "aws" : opts.cloud
10
- @update_file = opts.file.nil? ? "all" : opts.file
11
- end
9
+ def initialize(opts)
10
+ @cloud = opts.cloud.nil? ? "aws" : opts.cloud
11
+ @update_file = opts.file.nil? ? "all" : opts.file
12
+ @wombat_yml = opts.wombat_yml
13
+ end
12
14
 
13
- def start
14
- update_lock(cloud) if /(all|lock)/.match(update_file)
15
- update_template(cloud) if /(all|template)/.match(update_file)
15
+ def start
16
+ update_lock(cloud) if /(all|lock)/.match(update_file)
17
+ update_template(cloud) if /(all|template)/.match(update_file)
18
+ end
16
19
  end
17
20
  end
@@ -1,3 +1,3 @@
1
1
  module Wombat
2
- VERSION = "0.3.4"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -0,0 +1,26 @@
1
+ require "wombat/common"
2
+ require "yaml"
3
+
4
+ describe "Common" do
5
+ let(:common) do
6
+ klass = Class.new { include Wombat::Common }
7
+
8
+ return klass.new
9
+ end
10
+
11
+ describe "#wombat" do
12
+ it "reads a configuration file" do
13
+ wombat_yml = File.join(
14
+ File.expand_path("../..", File.dirname(__FILE__)),
15
+ "generator_files",
16
+ "wombat.yml"
17
+ )
18
+
19
+ stub_const("ENV", {"WOMBAT_YML" => wombat_yml})
20
+
21
+ content = common.wombat
22
+
23
+ expect(content["name"]).to eq("wombat")
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,103 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
6
+ #
7
+ # Given that it is always loaded, you are encouraged to keep this file as
8
+ # light-weight as possible. Requiring heavyweight dependencies from this file
9
+ # will add to the boot time of your test suite on EVERY test run, even for an
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
14
+ #
15
+ # The `.rspec` file also contains a few flags that are not defaults but that
16
+ # users commonly want.
17
+ #
18
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
19
+ RSpec.configure do |config|
20
+ # rspec-expectations config goes here. You can use an alternate
21
+ # assertion/expectation library such as wrong or the stdlib/minitest
22
+ # assertions if you prefer.
23
+ config.expect_with :rspec do |expectations|
24
+ # This option will default to `true` in RSpec 4. It makes the `description`
25
+ # and `failure_message` of custom matchers include text for helper methods
26
+ # defined using `chain`, e.g.:
27
+ # be_bigger_than(2).and_smaller_than(4).description
28
+ # # => "be bigger than 2 and smaller than 4"
29
+ # ...rather than:
30
+ # # => "be bigger than 2"
31
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
32
+ end
33
+
34
+ # rspec-mocks config goes here. You can use an alternate test double
35
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
36
+ config.mock_with :rspec do |mocks|
37
+ # Prevents you from mocking or stubbing a method that does not exist on
38
+ # a real object. This is generally recommended, and will default to
39
+ # `true` in RSpec 4.
40
+ mocks.verify_partial_doubles = true
41
+ end
42
+
43
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
44
+ # have no way to turn it off -- the option exists only for backwards
45
+ # compatibility in RSpec 3). It causes shared context metadata to be
46
+ # inherited by the metadata hash of host groups and examples, rather than
47
+ # triggering implicit auto-inclusion in groups with matching metadata.
48
+ config.shared_context_metadata_behavior = :apply_to_host_groups
49
+
50
+ # The settings below are suggested to provide a good initial experience
51
+ # with RSpec, but feel free to customize to your heart's content.
52
+ =begin
53
+ # This allows you to limit a spec run to individual examples or groups
54
+ # you care about by tagging them with `:focus` metadata. When nothing
55
+ # is tagged with `:focus`, all examples get run. RSpec also provides
56
+ # aliases for `it`, `describe`, and `context` that include `:focus`
57
+ # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
58
+ config.filter_run_when_matching :focus
59
+
60
+ # Allows RSpec to persist some state between runs in order to support
61
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
62
+ # you configure your source control system to ignore this file.
63
+ config.example_status_persistence_file_path = "spec/examples.txt"
64
+
65
+ # Limits the available syntax to the non-monkey patched syntax that is
66
+ # recommended. For more details, see:
67
+ # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
68
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
69
+ # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
70
+ config.disable_monkey_patching!
71
+
72
+ # This setting enables warnings. It's recommended, but in some cases may
73
+ # be too noisy due to issues in dependencies.
74
+ config.warnings = true
75
+
76
+ # Many RSpec users commonly either run the entire suite or an individual
77
+ # file, and it's useful to allow more verbose output when running an
78
+ # individual spec file.
79
+ if config.files_to_run.one?
80
+ # Use the documentation formatter for detailed output,
81
+ # unless a formatter has already been configured
82
+ # (e.g. via a command-line flag).
83
+ config.default_formatter = 'doc'
84
+ end
85
+
86
+ # Print the 10 slowest examples and example groups at the
87
+ # end of the spec run, to help surface which specs are running
88
+ # particularly slow.
89
+ config.profile_examples = 10
90
+
91
+ # Run specs in random order to surface order dependencies. If you find an
92
+ # order dependency and want to debug it, you can fix the order by providing
93
+ # the seed, which is printed after each run.
94
+ # --seed 1234
95
+ config.order = :random
96
+
97
+ # Seed global randomization in this process using the `--seed` CLI option.
98
+ # Setting this allows you to use `--seed` to deterministically reproduce
99
+ # test failures related to randomization by passing the same `--seed` value
100
+ # as the one that triggered the failure.
101
+ Kernel.srand config.seed
102
+ =end
103
+ end
@@ -0,0 +1,116 @@
1
+ require "wombat/common"
2
+ require "yaml"
3
+
4
+ describe "Wombat::Common" do
5
+ let(:common) do
6
+ klass = Class.new { include Wombat::Common }
7
+
8
+ return klass.new
9
+ end
10
+
11
+ describe "#conf" do
12
+ describe "when files_dir is set" do
13
+ it "sets files_dir to default value" do
14
+
15
+ content = common.conf
16
+
17
+ expect(content["files_dir"]).to eq("files")
18
+ end
19
+ end
20
+ end
21
+
22
+ describe "#lock" do
23
+ describe "when a lock does not exist" do
24
+ it "warn and return 1" do
25
+
26
+ expect(File).to receive(:exist?).
27
+ with("wombat.lock").
28
+ and_return(false)
29
+
30
+ expect(common).to receive(:warn)
31
+
32
+ lock = common.lock
33
+
34
+ expect(lock).to be(1)
35
+ end
36
+ end
37
+
38
+ describe "when a lock does exist" do
39
+ it "load lock file" do
40
+
41
+ expect(File).to receive(:exist?).
42
+ with("wombat.lock").
43
+ and_return(true)
44
+
45
+ expect(File).to receive(:read).
46
+ with("wombat.lock").
47
+ and_return("{")
48
+
49
+ expect(JSON).to receive(:parse).with("{")
50
+
51
+ common.lock
52
+ end
53
+ end
54
+ end
55
+
56
+ describe "#wombat" do
57
+ describe "when WOMBAT_YML is set" do
58
+ it "loads a configuration file from WOMBAT_YML" do
59
+ stub_const("ENV", {"WOMBAT_YML" => "aesthetics.yml"})
60
+
61
+ expect(File).to receive(:exist?).
62
+ with("aesthetics.yml").
63
+ and_return(true)
64
+
65
+ expect(File).to receive(:read).
66
+ with("aesthetics.yml").
67
+ and_return("---")
68
+
69
+ expect(YAML).to receive(:load).with("---")
70
+
71
+ common.wombat
72
+ end
73
+ end
74
+
75
+ describe "when WOMBAT_YML is not set" do
76
+ it "loads a configuration file from a default location" do
77
+ stub_const("ENV", {})
78
+
79
+ expect(File).to receive(:exist?).
80
+ with("wombat.yml").
81
+ and_return(true)
82
+
83
+ expect(File).to receive(:read).
84
+ with("wombat.yml").
85
+ and_return("---")
86
+
87
+ expect(YAML).to receive(:load).with("---")
88
+
89
+ common.wombat
90
+ end
91
+ end
92
+
93
+ describe "when a configuration file does not exist" do
94
+ it "copies example configuration file to default location" do
95
+ stub_const("ENV", {})
96
+
97
+ expect(File).to receive(:exist?).
98
+ with("wombat.yml").
99
+ and_return(false)
100
+
101
+ expect(common).to receive(:warn)
102
+
103
+ expect(FileUtils).to receive(:cp_r).
104
+ with(/generator_files\/wombat\.yml/, Dir.pwd)
105
+
106
+ expect(File).to receive(:read).
107
+ with("wombat.yml").
108
+ and_return("---")
109
+
110
+ expect(YAML).to receive(:load).with("---")
111
+
112
+ common.wombat
113
+ end
114
+ end
115
+ end
116
+ end
data/wombat-cli.gemspec CHANGED
@@ -28,5 +28,6 @@ Gem::Specification.new do |gem|
28
28
  gem.add_dependency 'net-ssh', '~> 3.2'
29
29
  gem.add_dependency 'parallel', '~> 1.9'
30
30
  gem.add_dependency 'aws-sdk', '~> 2.5'
31
- gem.add_dependency 'racker', '~> 0.2'
31
+ gem.add_dependency 'azure_mgmt_resources', '~> 0.8'
32
+ gem.add_dependency 'azure_mgmt_storage', '~> 0.8'
32
33
  end