opsworks-cli 0.2.4 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +14 -5
- data/lib/opsworks/cli/agent.rb +6 -10
- data/lib/opsworks/cli/subcommands/apps.rb +90 -0
- data/lib/opsworks/cli/subcommands/{allow.rb → iam.rb} +18 -3
- data/lib/opsworks/cli/subcommands/{exec.rb → recipes.rb} +21 -4
- data/lib/opsworks/cli/subcommands/update.rb +2 -1
- data/lib/opsworks/cli/version.rb +1 -1
- data/lib/opsworks/layer.rb +36 -0
- data/lib/opsworks/stack.rb +23 -1
- data/opsworks-cli.gemspec +1 -0
- data/spec/opsworks/cli/subcommands/apps_spec.rb +90 -0
- data/spec/opsworks/cli/subcommands/config_spec.rb +10 -0
- data/spec/opsworks/cli/subcommands/iam_spec.rb +68 -0
- data/spec/opsworks/cli/subcommands/recipes_spec.rb +39 -0
- metadata +26 -15
- data/lib/opsworks/cli/subcommands/deploy.rb +0 -38
- data/lib/opsworks/cli/subcommands/lockdown.rb +0 -30
- data/lib/opsworks/cli/subcommands/status.rb +0 -45
- data/spec/opsworks/cli/subcommands/allow_spec.rb +0 -42
- data/spec/opsworks/cli/subcommands/deploy_spec.rb +0 -53
- data/spec/opsworks/cli/subcommands/exec_spec.rb +0 -37
- data/spec/opsworks/cli/subcommands/lockdown_spec.rb +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2ab03c769a18190d2590e765e79e992e820685da
|
4
|
+
data.tar.gz: ce762573e029e65e1776bcfae265f7611762fc85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d347483d2342efc5d4cbbd882f310044bd327269868105eab6ea53e9f4a368cb2420c0cd2b9d4853a2cb409cfcb0e2eb0b6fddfa18932025f6bda958c0e9a8da
|
7
|
+
data.tar.gz: ca593191e47af2a66c8f072d1c1fca78f869c9cc0784c06e8b5c33819b63cba6afeabc66d0c677d98261c4f4b95ee4ef979ff9c6eb7879ddf13b0943e745948b
|
data/README.md
CHANGED
@@ -32,11 +32,20 @@ When you add credentials, make sure to name the account `default`.
|
|
32
32
|
```
|
33
33
|
$ opsworks help
|
34
34
|
Commands:
|
35
|
-
opsworks
|
36
|
-
opsworks
|
37
|
-
opsworks status [--stack STACK]
|
38
|
-
opsworks
|
39
|
-
opsworks
|
35
|
+
opsworks apps:create APP [--stack STACK] # Create a new OpsWorks app
|
36
|
+
opsworks apps:deploy APP [--stack STACK] # Deploy an OpsWorks app
|
37
|
+
opsworks apps:status APP [--stack STACK] # Display the most recent deployment of an app
|
38
|
+
opsworks config:get KEY [--stack STACK] # Get a single config value
|
39
|
+
opsworks config:set KEY VALUE [--stack STACK] # Set a config value
|
40
|
+
opsworks config:unset KEY [--stack STACK] # Unset a config value
|
41
|
+
opsworks help [COMMAND] # Describe available commands or one specific command
|
42
|
+
opsworks iam:allow USER [--stack STACK] # Allow an IAM user on a stack
|
43
|
+
opsworks iam:lockdown [--stack STACK] # Remove all stack permissions
|
44
|
+
opsworks recipes:add LAYER EVENT RECIPE [--stack STACK] # Add a recipe to a given layer and lifecycle event
|
45
|
+
opsworks recipes:run RECIPE [--stack STACK] # Execute a Chef recipe
|
46
|
+
opsworks update [--stack STACK] # Update OpsWorks custom cookbooks
|
47
|
+
opsworks upgrade-chef [--stack STACK] # Upgrade Chef version
|
48
|
+
opsworks version # Print OpsWorks CLI version
|
40
49
|
```
|
41
50
|
|
42
51
|
## Contributing
|
data/lib/opsworks/cli/agent.rb
CHANGED
@@ -5,12 +5,10 @@ require_relative 'helpers/keychain'
|
|
5
5
|
require_relative 'helpers/options'
|
6
6
|
|
7
7
|
require_relative 'subcommands/update'
|
8
|
-
require_relative 'subcommands/exec'
|
9
|
-
require_relative 'subcommands/deploy'
|
10
|
-
require_relative 'subcommands/status'
|
11
|
-
require_relative 'subcommands/allow'
|
12
|
-
require_relative 'subcommands/lockdown'
|
13
8
|
require_relative 'subcommands/upgrade_chef'
|
9
|
+
require_relative 'subcommands/recipes'
|
10
|
+
require_relative 'subcommands/apps'
|
11
|
+
require_relative 'subcommands/iam'
|
14
12
|
require_relative 'subcommands/config'
|
15
13
|
|
16
14
|
module OpsWorks
|
@@ -19,12 +17,10 @@ module OpsWorks
|
|
19
17
|
include Thor::Actions
|
20
18
|
|
21
19
|
include Subcommands::Update
|
22
|
-
include Subcommands::Exec
|
23
|
-
include Subcommands::Deploy
|
24
|
-
include Subcommands::Status
|
25
|
-
include Subcommands::Allow
|
26
|
-
include Subcommands::Lockdown
|
27
20
|
include Subcommands::UpgradeChef
|
21
|
+
include Subcommands::Recipes
|
22
|
+
include Subcommands::Apps
|
23
|
+
include Subcommands::IAM
|
28
24
|
include Subcommands::Config
|
29
25
|
|
30
26
|
desc 'version', 'Print OpsWorks CLI version'
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'opsworks/deployment'
|
2
|
+
|
3
|
+
module OpsWorks
|
4
|
+
module CLI
|
5
|
+
module Subcommands
|
6
|
+
module Apps
|
7
|
+
# rubocop:disable MethodLength
|
8
|
+
# rubocop:disable CyclomaticComplexity
|
9
|
+
# rubocop:disable PerceivedComplexity
|
10
|
+
def self.included(thor)
|
11
|
+
thor.class_eval do
|
12
|
+
desc 'apps:deploy APP [--stack STACK]', 'Deploy an OpsWorks app'
|
13
|
+
option :stack, type: :array
|
14
|
+
option :timeout, type: :numeric
|
15
|
+
define_method 'apps:deploy' do |name|
|
16
|
+
fetch_keychain_credentials unless env_credentials?
|
17
|
+
stacks = parse_stacks(options.merge(active: true))
|
18
|
+
deployments = stacks.map do |stack|
|
19
|
+
next unless (app = stack.find_app_by_name(name))
|
20
|
+
say "Deploying to #{stack.name}..."
|
21
|
+
stack.deploy_app(app)
|
22
|
+
end
|
23
|
+
deployments.compact!
|
24
|
+
OpsWorks::Deployment.wait(deployments, options[:timeout])
|
25
|
+
unless deployments.all?(&:success?)
|
26
|
+
failures = []
|
27
|
+
deployments.each_with_index do |deployment, i|
|
28
|
+
failures << stacks[i].name unless deployment.success?
|
29
|
+
end
|
30
|
+
fail "Deploy failed on #{failures.join(', ')}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'apps:status APP [--stack STACK]',
|
35
|
+
'Display the most recent deployment of an app'
|
36
|
+
option :stack, type: :array
|
37
|
+
define_method 'apps:status' do |name|
|
38
|
+
fetch_keychain_credentials unless env_credentials?
|
39
|
+
|
40
|
+
table = parse_stacks(options).map do |stack|
|
41
|
+
next unless (app = stack.find_app_by_name(name))
|
42
|
+
if (deployment = app.last_deployment)
|
43
|
+
deployed_at = formatted_time(deployment.created_at)
|
44
|
+
else
|
45
|
+
deployed_at = '-'
|
46
|
+
end
|
47
|
+
[stack.name, name, "(#{app.revision})", deployed_at]
|
48
|
+
end
|
49
|
+
# Sort output in descending date order
|
50
|
+
table.compact!
|
51
|
+
table.sort! { |x, y| y.last <=> x.last }
|
52
|
+
print_table table
|
53
|
+
end
|
54
|
+
|
55
|
+
desc 'apps:create APP [--stack STACK]', 'Create a new OpsWorks app'
|
56
|
+
option :stack, type: :array
|
57
|
+
option :type, default: 'other'
|
58
|
+
option :git_url
|
59
|
+
option :shortname
|
60
|
+
define_method 'apps:create' do |name|
|
61
|
+
unless %w(other).include?(options[:type])
|
62
|
+
fail "Unsupported type: #{options[:type]}"
|
63
|
+
end
|
64
|
+
|
65
|
+
fail 'Git URL not yet supported' if options[:git_url]
|
66
|
+
|
67
|
+
fetch_keychain_credentials unless env_credentials?
|
68
|
+
stacks = parse_stacks(options)
|
69
|
+
|
70
|
+
stacks.each do |stack|
|
71
|
+
next if stack.apps.map(&:name).include?(name)
|
72
|
+
say "Creating app on #{stack.name}."
|
73
|
+
stack.create_app(name, options)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def formatted_time(timestamp)
|
80
|
+
timestamp.strftime('%Y-%m-%d %H:%M:%S %Z')
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
# rubocop:enable PerceivedComplexity
|
85
|
+
# rubocop:enable CyclomaticComplexity
|
86
|
+
# rubocop:enable MethodLength
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -3,16 +3,17 @@ require 'opsworks/permission'
|
|
3
3
|
module OpsWorks
|
4
4
|
module CLI
|
5
5
|
module Subcommands
|
6
|
-
module
|
6
|
+
module IAM
|
7
7
|
# rubocop:disable MethodLength
|
8
8
|
# rubocop:disable CyclomaticComplexity
|
9
9
|
def self.included(thor)
|
10
10
|
thor.class_eval do
|
11
|
-
desc 'allow USER [--stack STACK]',
|
11
|
+
desc 'iam:allow USER [--stack STACK]',
|
12
|
+
'Allow an IAM user on a stack'
|
12
13
|
option :stack, type: :array
|
13
14
|
option :ssh, type: :boolean, default: true
|
14
15
|
option :sudo, type: :boolean, default: true
|
15
|
-
|
16
|
+
define_method 'iam:allow' do |user|
|
16
17
|
fetch_keychain_credentials unless env_credentials?
|
17
18
|
stacks = parse_stacks(options.merge(active: true))
|
18
19
|
stacks.each do |stack|
|
@@ -22,6 +23,20 @@ module OpsWorks
|
|
22
23
|
permission.update(ssh: options[:ssh], sudo: options[:sudo])
|
23
24
|
end
|
24
25
|
end
|
26
|
+
|
27
|
+
desc 'iam:lockdown [--stack STACK]', 'Remove all stack permissions'
|
28
|
+
option :stack, type: :array
|
29
|
+
define_method 'iam:lockdown' do
|
30
|
+
fetch_keychain_credentials unless env_credentials?
|
31
|
+
stacks = parse_stacks(options.merge(active: true))
|
32
|
+
stacks.each do |stack|
|
33
|
+
say "Locking down #{stack.name}..."
|
34
|
+
stack.permissions.each do |permission|
|
35
|
+
permission.update(ssh: false, sudo: false)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
25
40
|
end
|
26
41
|
end
|
27
42
|
# rubocop:enable CyclomaticComplexity
|
@@ -3,21 +3,22 @@ require 'opsworks/deployment'
|
|
3
3
|
module OpsWorks
|
4
4
|
module CLI
|
5
5
|
module Subcommands
|
6
|
-
module
|
6
|
+
module Recipes
|
7
7
|
# rubocop:disable MethodLength
|
8
8
|
# rubocop:disable CyclomaticComplexity
|
9
9
|
def self.included(thor)
|
10
10
|
thor.class_eval do
|
11
|
-
desc '
|
11
|
+
desc 'recipes:run RECIPE [--stack STACK]', 'Execute a Chef recipe'
|
12
12
|
option :stack, type: :array
|
13
|
-
|
13
|
+
option :timeout, type: :numeric
|
14
|
+
define_method 'recipes:run' do |recipe|
|
14
15
|
fetch_keychain_credentials unless env_credentials?
|
15
16
|
stacks = parse_stacks(options.merge(active: true))
|
16
17
|
deployments = stacks.map do |stack|
|
17
18
|
say "Executing recipe on #{stack.name}..."
|
18
19
|
stack.execute_recipe(recipe)
|
19
20
|
end
|
20
|
-
OpsWorks::Deployment.wait(deployments)
|
21
|
+
OpsWorks::Deployment.wait(deployments, options[:timeout])
|
21
22
|
unless deployments.all?(&:success?)
|
22
23
|
failures = []
|
23
24
|
deployments.each_with_index do |deployment, i|
|
@@ -26,6 +27,22 @@ module OpsWorks
|
|
26
27
|
fail "Command failed on #{failures.join(', ')}"
|
27
28
|
end
|
28
29
|
end
|
30
|
+
|
31
|
+
desc 'recipes:add LAYER EVENT RECIPE [--stack STACK]',
|
32
|
+
'Add a recipe to a given layer and lifecycle event'
|
33
|
+
option :stack, type: :array
|
34
|
+
define_method 'recipes:add' do |layername, event, recipe|
|
35
|
+
fetch_keychain_credentials unless env_credentials?
|
36
|
+
stacks = parse_stacks(options)
|
37
|
+
stacks.each do |stack|
|
38
|
+
layer = stack.layers.find { |l| l.shortname == layername }
|
39
|
+
next unless layer
|
40
|
+
next if layer.custom_recipes[event].include?(recipe)
|
41
|
+
|
42
|
+
say "Adding recipe to #{stack.name}."
|
43
|
+
layer.add_custom_recipe(event, recipe)
|
44
|
+
end
|
45
|
+
end
|
29
46
|
end
|
30
47
|
end
|
31
48
|
# rubocop:enable CyclomaticComplexity
|
@@ -14,6 +14,7 @@ module OpsWorks
|
|
14
14
|
|
15
15
|
desc 'update [--stack STACK]', 'Update OpsWorks custom cookbooks'
|
16
16
|
option :stack, type: :array
|
17
|
+
option :timeout, type: :numeric
|
17
18
|
def update
|
18
19
|
fetch_keychain_credentials unless env_credentials?
|
19
20
|
stacks = parse_stacks(options.merge(active: true))
|
@@ -21,7 +22,7 @@ module OpsWorks
|
|
21
22
|
say "Updating #{stack.name}..."
|
22
23
|
stack.update_custom_cookbooks
|
23
24
|
end
|
24
|
-
OpsWorks::Deployment.wait(deployments)
|
25
|
+
OpsWorks::Deployment.wait(deployments, options[:timeout])
|
25
26
|
unless deployments.all?(&:success?)
|
26
27
|
failures = []
|
27
28
|
deployments.each_with_index do |deployment, i|
|
data/lib/opsworks/cli/version.rb
CHANGED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'opsworks/resource'
|
2
|
+
require 'thor'
|
3
|
+
|
4
|
+
module OpsWorks
|
5
|
+
class Layer < Resource
|
6
|
+
attr_accessor :id, :name, :shortname, :custom_recipes
|
7
|
+
|
8
|
+
# rubocop:disable MethodLength
|
9
|
+
def self.from_collection_response(response)
|
10
|
+
response.data[:layers].map do |hash|
|
11
|
+
# Make custom_recipes accessible by string or symbol
|
12
|
+
custom_recipes = Thor::CoreExt::HashWithIndifferentAccess.new(
|
13
|
+
hash[:custom_recipes]
|
14
|
+
)
|
15
|
+
new(
|
16
|
+
id: hash[:layer_id],
|
17
|
+
name: hash[:name],
|
18
|
+
shortname: hash[:shortname],
|
19
|
+
custom_recipes: custom_recipes
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
# rubocop:enable MethodLength
|
24
|
+
|
25
|
+
def add_custom_recipe(event, recipe)
|
26
|
+
return if custom_recipes[event].include?(recipe)
|
27
|
+
|
28
|
+
custom_recipes[event] ||= []
|
29
|
+
custom_recipes[event].push recipe
|
30
|
+
self.class.client.update_layer(
|
31
|
+
layer_id: id,
|
32
|
+
custom_recipes: custom_recipes
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/opsworks/stack.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'jsonpath'
|
2
|
+
require 'active_support/core_ext/hash/slice'
|
2
3
|
|
3
4
|
require 'opsworks/resource'
|
4
5
|
require 'opsworks/app'
|
5
6
|
require 'opsworks/instance'
|
6
7
|
require 'opsworks/permission'
|
8
|
+
require 'opsworks/layer'
|
7
9
|
|
8
10
|
module OpsWorks
|
9
11
|
# rubocop:disable ClassLength
|
@@ -54,6 +56,10 @@ module OpsWorks
|
|
54
56
|
@instances ||= initialize_instances
|
55
57
|
end
|
56
58
|
|
59
|
+
def layers
|
60
|
+
@layers ||= initialize_layers
|
61
|
+
end
|
62
|
+
|
57
63
|
def upgrade_chef(version, options = {})
|
58
64
|
self.class.client.update_stack(
|
59
65
|
stack_id: id,
|
@@ -97,6 +103,12 @@ module OpsWorks
|
|
97
103
|
)
|
98
104
|
end
|
99
105
|
|
106
|
+
def create_app(name, options = {})
|
107
|
+
options = options.slice(:type, :shortname)
|
108
|
+
options.merge!(stack_id: id, name: name)
|
109
|
+
self.class.client.create_app(options)
|
110
|
+
end
|
111
|
+
|
100
112
|
private
|
101
113
|
|
102
114
|
def initialize_apps
|
@@ -106,12 +118,15 @@ module OpsWorks
|
|
106
118
|
end
|
107
119
|
|
108
120
|
# rubocop:disable Eval
|
121
|
+
# rubocop:disable MethodLength
|
109
122
|
def replace_hash_at_path(hash, key, value)
|
110
123
|
path = JsonPath.new(key).path
|
111
124
|
if value
|
112
125
|
# REVIEW: Is there a better way to parse the JSON Path and ensure
|
113
126
|
# a value at the location?
|
114
|
-
|
127
|
+
(0...(path.length - 1)).each do |i|
|
128
|
+
eval("hash#{path[0..i].join('')} ||= {}")
|
129
|
+
end
|
115
130
|
eval("hash#{path.join('')} = #{value.inspect}")
|
116
131
|
elsif JsonPath.new(key).on(hash).count > 0
|
117
132
|
# Path value is present, but we need to unset it
|
@@ -121,6 +136,7 @@ module OpsWorks
|
|
121
136
|
|
122
137
|
hash
|
123
138
|
end
|
139
|
+
# rubocop:enable MethodLength
|
124
140
|
# rubocop:enable Eval
|
125
141
|
|
126
142
|
def initialize_permissions
|
@@ -135,6 +151,12 @@ module OpsWorks
|
|
135
151
|
Instance.from_collection_response(response)
|
136
152
|
end
|
137
153
|
|
154
|
+
def initialize_layers
|
155
|
+
return [] unless id
|
156
|
+
response = self.class.client.describe_layers(stack_id: id)
|
157
|
+
Layer.from_collection_response(response)
|
158
|
+
end
|
159
|
+
|
138
160
|
def create_deployment(options = {})
|
139
161
|
response = self.class.client.create_deployment(
|
140
162
|
options.merge(stack_id: id)
|
data/opsworks-cli.gemspec
CHANGED
@@ -23,6 +23,7 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.add_dependency 'thor'
|
24
24
|
spec.add_dependency 'aws-sdk'
|
25
25
|
spec.add_dependency 'jsonpath'
|
26
|
+
spec.add_dependency 'activesupport'
|
26
27
|
|
27
28
|
spec.add_development_dependency 'aws-keychain-util'
|
28
29
|
spec.add_development_dependency 'bundler', '~> 1.5'
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OpsWorks::CLI::Agent do
|
4
|
+
context 'apps' do
|
5
|
+
let(:app_name) { 'aptible' }
|
6
|
+
let(:stacks) { 2.times.map { Fabricate(:stack) } }
|
7
|
+
|
8
|
+
before { allow(subject).to receive(:say) }
|
9
|
+
before { allow(OpsWorks::Deployment).to receive(:wait) }
|
10
|
+
before { allow(OpsWorks::Stack).to receive(:all) { stacks } }
|
11
|
+
before { allow(OpsWorks::Stack).to receive(:active) { stacks } }
|
12
|
+
|
13
|
+
describe 'apps:deploy' do
|
14
|
+
let(:app) { Fabricate(:app, name: app_name) }
|
15
|
+
let(:success) { Fabricate(:deployment, status: 'successful') }
|
16
|
+
let(:failure) { Fabricate(:deployment, status: 'failed') }
|
17
|
+
|
18
|
+
before do
|
19
|
+
stacks.each { |stack| allow(stack).to receive(:apps) { [app] } }
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should update custom cookbooks on all stacks' do
|
23
|
+
expect(stacks[0]).to receive(:deploy_app).with(app) { success }
|
24
|
+
expect(stacks[1]).to receive(:deploy_app).with(app) { success }
|
25
|
+
subject.send('apps:deploy', app_name)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should not fail if some stacks are inactive' do
|
29
|
+
allow(OpsWorks::Stack).to receive(:active) { [stacks[0]] }
|
30
|
+
expect(stacks[0]).to receive(:deploy_app).with(app) { success }
|
31
|
+
expect(stacks[1]).not_to receive(:deploy_app)
|
32
|
+
subject.send('apps:deploy', app_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should optionally run on a subset of stacks' do
|
36
|
+
expect(stacks[0]).to receive(:deploy_app).with(app) { success }
|
37
|
+
expect(stacks[1]).not_to receive(:deploy_app)
|
38
|
+
|
39
|
+
allow(subject).to receive(:options) { { stack: [stacks[0].name] } }
|
40
|
+
subject.send('apps:deploy', app_name)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should not fail if a stack does not have the app' do
|
44
|
+
allow(stacks[0]).to receive(:apps) { [] }
|
45
|
+
expect(stacks[1]).to receive(:deploy_app).with(app) { success }
|
46
|
+
expect { subject.send('apps:deploy', app_name) }.not_to raise_error
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should fail if any update fails' do
|
50
|
+
expect(stacks[0]).to receive(:deploy_app).with(app) { failure }
|
51
|
+
|
52
|
+
allow(subject).to receive(:options) { { stack: [stacks[0].name] } }
|
53
|
+
expect { subject.send('apps:deploy', app_name) }.to raise_error
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'apps:create' do
|
58
|
+
# TODO: Figure out why Thor doesn't populate options from defaults
|
59
|
+
# when methods are invoked directly
|
60
|
+
let(:options) { { type: 'other' } }
|
61
|
+
|
62
|
+
before do
|
63
|
+
stacks.each { |stack| allow(stack).to receive(:apps) { [] } }
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should fail with a helpful error on unsupported type' do
|
67
|
+
options.merge!(type: 'foobar')
|
68
|
+
allow(subject).to receive(:options) { options }
|
69
|
+
expect { subject.send('apps:create', app_name) }.to raise_error
|
70
|
+
end
|
71
|
+
|
72
|
+
xit 'should accept a Git URL'
|
73
|
+
|
74
|
+
it 'should create an app' do
|
75
|
+
allow(subject).to receive(:options) { options }
|
76
|
+
expect(stacks[0]).to receive(:create_app).with(app_name, options)
|
77
|
+
expect(stacks[1]).to receive(:create_app).with(app_name, options)
|
78
|
+
subject.send('apps:create', app_name)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should accept a different shortname' do
|
82
|
+
options.merge!(shortname: 'foobar')
|
83
|
+
allow(subject).to receive(:options) { options }
|
84
|
+
expect(stacks[0]).to receive(:create_app).with(app_name, options)
|
85
|
+
expect(stacks[1]).to receive(:create_app).with(app_name, options)
|
86
|
+
subject.send('apps:create', app_name)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -38,6 +38,16 @@ describe OpsWorks::CLI::Agent do
|
|
38
38
|
expect(stack.custom_json['env']['FOO']).to eq 'baz'
|
39
39
|
end
|
40
40
|
|
41
|
+
it 'should work with deep nested hashes' do
|
42
|
+
stack.custom_json = { 'app' => { 'var' => 'value' } }
|
43
|
+
expect(client).to receive(:update_stack) do |hash|
|
44
|
+
json = JSON.parse(hash[:custom_json])
|
45
|
+
expect(json['app']['env']['FOO']).to eq 'baz'
|
46
|
+
end
|
47
|
+
subject.send('config:set', 'app.env.FOO', 'baz')
|
48
|
+
expect(stack.custom_json['app']['env']['FOO']).to eq 'baz'
|
49
|
+
end
|
50
|
+
|
41
51
|
it 'should set the variable, if it is unset' do
|
42
52
|
stack.custom_json = {}
|
43
53
|
expect(client).to receive(:update_stack) do |hash|
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OpsWorks::CLI::Agent do
|
4
|
+
context 'iam' do
|
5
|
+
let(:permissions) { 2.times.map { Fabricate(:permission) } }
|
6
|
+
let(:user) { permissions[0].user }
|
7
|
+
|
8
|
+
before { allow(subject).to receive(:say) }
|
9
|
+
before { allow(OpsWorks::Stack).to receive(:all) { stacks } }
|
10
|
+
before { allow(OpsWorks::Stack).to receive(:active) { stacks } }
|
11
|
+
|
12
|
+
describe 'iam:allow' do
|
13
|
+
let(:stacks) do
|
14
|
+
2.times.map do |i|
|
15
|
+
Fabricate(:stack).tap do |stack|
|
16
|
+
allow(stack).to receive(:find_permission_by_user) { permissions[i] }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should update all matching permissions' do
|
22
|
+
expect(permissions[0]).to receive(:update)
|
23
|
+
expect(permissions[1]).to receive(:update)
|
24
|
+
subject.send('iam:allow', user)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should optionally run on a subset of stacks' do
|
28
|
+
expect(permissions[0]).to receive(:update)
|
29
|
+
expect(permissions[1]).not_to receive(:update)
|
30
|
+
|
31
|
+
allow(subject).to receive(:options) { { stack: [stacks[0].name] } }
|
32
|
+
subject.send('iam:allow', user)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should accept :ssh and :sudo options' do
|
36
|
+
expect(permissions[0]).to receive(:update).with(ssh: true, sudo: false)
|
37
|
+
|
38
|
+
allow(subject).to receive(:options) do
|
39
|
+
{ stack: [stacks[0].name], ssh: true, sudo: false }
|
40
|
+
end
|
41
|
+
subject.send('iam:allow', user)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'iam:lockdown' do
|
46
|
+
let(:stack) do
|
47
|
+
Fabricate(:stack).tap do |stack|
|
48
|
+
allow(stack).to receive(:permissions) { permissions }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
let(:stacks) { [stack] }
|
52
|
+
|
53
|
+
it 'should lock down all stacks' do
|
54
|
+
expect(permissions[0]).to receive(:update).with(ssh: false, sudo: false)
|
55
|
+
expect(permissions[1]).to receive(:update).with(ssh: false, sudo: false)
|
56
|
+
subject.send('iam:lockdown')
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should optionally run on a subset of stacks' do
|
60
|
+
expect(permissions[0]).to receive(:update).with(ssh: false, sudo: false)
|
61
|
+
expect(permissions[1]).to receive(:update).with(ssh: false, sudo: false)
|
62
|
+
|
63
|
+
allow(subject).to receive(:options) { { stacks: [stack.name] } }
|
64
|
+
subject.send('iam:lockdown')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OpsWorks::CLI::Agent do
|
4
|
+
context 'recipes' do
|
5
|
+
let(:recipe) { 'hotpockets::install' }
|
6
|
+
let(:stacks) { 2.times.map { Fabricate(:stack) } }
|
7
|
+
|
8
|
+
before { allow(subject).to receive(:say) }
|
9
|
+
before { allow(OpsWorks::Deployment).to receive(:wait) }
|
10
|
+
before { allow(OpsWorks::Stack).to receive(:all) { stacks } }
|
11
|
+
before { allow(OpsWorks::Stack).to receive(:active) { stacks } }
|
12
|
+
|
13
|
+
describe 'recipes:run' do
|
14
|
+
let(:success) { Fabricate(:deployment, status: 'successful') }
|
15
|
+
let(:failure) { Fabricate(:deployment, status: 'failed') }
|
16
|
+
|
17
|
+
it 'should update custom cookbooks on all stacks' do
|
18
|
+
expect(stacks[0]).to receive(:execute_recipe).with(recipe) { success }
|
19
|
+
expect(stacks[1]).to receive(:execute_recipe).with(recipe) { success }
|
20
|
+
subject.send('recipes:run', recipe)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should optionally run on a subset of stacks' do
|
24
|
+
expect(stacks[0]).to receive(:execute_recipe).with(recipe) { success }
|
25
|
+
expect(stacks[1]).not_to receive(:execute_recipe)
|
26
|
+
|
27
|
+
allow(subject).to receive(:options) { { stack: [stacks[0].name] } }
|
28
|
+
subject.send('recipes:run', recipe)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should fail if any update fails' do
|
32
|
+
expect(stacks[0]).to receive(:execute_recipe).with(recipe) { failure }
|
33
|
+
|
34
|
+
allow(subject).to receive(:options) { { stack: [stacks[0].name] } }
|
35
|
+
expect { subject.send('recipes:run', recipe) }.to raise_error
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opsworks-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Frank Macreery
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-12-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activesupport
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: aws-keychain-util
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -171,17 +185,16 @@ files:
|
|
171
185
|
- lib/opsworks/cli/agent.rb
|
172
186
|
- lib/opsworks/cli/helpers/keychain.rb
|
173
187
|
- lib/opsworks/cli/helpers/options.rb
|
174
|
-
- lib/opsworks/cli/subcommands/
|
188
|
+
- lib/opsworks/cli/subcommands/apps.rb
|
175
189
|
- lib/opsworks/cli/subcommands/config.rb
|
176
|
-
- lib/opsworks/cli/subcommands/
|
177
|
-
- lib/opsworks/cli/subcommands/
|
178
|
-
- lib/opsworks/cli/subcommands/lockdown.rb
|
179
|
-
- lib/opsworks/cli/subcommands/status.rb
|
190
|
+
- lib/opsworks/cli/subcommands/iam.rb
|
191
|
+
- lib/opsworks/cli/subcommands/recipes.rb
|
180
192
|
- lib/opsworks/cli/subcommands/update.rb
|
181
193
|
- lib/opsworks/cli/subcommands/upgrade_chef.rb
|
182
194
|
- lib/opsworks/cli/version.rb
|
183
195
|
- lib/opsworks/deployment.rb
|
184
196
|
- lib/opsworks/instance.rb
|
197
|
+
- lib/opsworks/layer.rb
|
185
198
|
- lib/opsworks/permission.rb
|
186
199
|
- lib/opsworks/resource.rb
|
187
200
|
- lib/opsworks/stack.rb
|
@@ -191,11 +204,10 @@ files:
|
|
191
204
|
- spec/fabricators/opsworks/permission_fabricator.rb
|
192
205
|
- spec/fabricators/opsworks/stack_fabricator.rb
|
193
206
|
- spec/opsworks/cli/agent_spec.rb
|
194
|
-
- spec/opsworks/cli/subcommands/
|
207
|
+
- spec/opsworks/cli/subcommands/apps_spec.rb
|
195
208
|
- spec/opsworks/cli/subcommands/config_spec.rb
|
196
|
-
- spec/opsworks/cli/subcommands/
|
197
|
-
- spec/opsworks/cli/subcommands/
|
198
|
-
- spec/opsworks/cli/subcommands/lockdown_spec.rb
|
209
|
+
- spec/opsworks/cli/subcommands/iam_spec.rb
|
210
|
+
- spec/opsworks/cli/subcommands/recipes_spec.rb
|
199
211
|
- spec/opsworks/cli/subcommands/update_spec.rb
|
200
212
|
- spec/spec_helper.rb
|
201
213
|
homepage: https://github.com/aptible/opsworks-cli
|
@@ -228,10 +240,9 @@ test_files:
|
|
228
240
|
- spec/fabricators/opsworks/permission_fabricator.rb
|
229
241
|
- spec/fabricators/opsworks/stack_fabricator.rb
|
230
242
|
- spec/opsworks/cli/agent_spec.rb
|
231
|
-
- spec/opsworks/cli/subcommands/
|
243
|
+
- spec/opsworks/cli/subcommands/apps_spec.rb
|
232
244
|
- spec/opsworks/cli/subcommands/config_spec.rb
|
233
|
-
- spec/opsworks/cli/subcommands/
|
234
|
-
- spec/opsworks/cli/subcommands/
|
235
|
-
- spec/opsworks/cli/subcommands/lockdown_spec.rb
|
245
|
+
- spec/opsworks/cli/subcommands/iam_spec.rb
|
246
|
+
- spec/opsworks/cli/subcommands/recipes_spec.rb
|
236
247
|
- spec/opsworks/cli/subcommands/update_spec.rb
|
237
248
|
- spec/spec_helper.rb
|
@@ -1,38 +0,0 @@
|
|
1
|
-
require 'opsworks/deployment'
|
2
|
-
|
3
|
-
module OpsWorks
|
4
|
-
module CLI
|
5
|
-
module Subcommands
|
6
|
-
module Deploy
|
7
|
-
# rubocop:disable MethodLength
|
8
|
-
# rubocop:disable CyclomaticComplexity
|
9
|
-
def self.included(thor)
|
10
|
-
thor.class_eval do
|
11
|
-
desc 'deploy APP [--stack STACK]', 'Deploy an OpsWorks app'
|
12
|
-
option :stack, type: :array
|
13
|
-
def deploy(name)
|
14
|
-
fetch_keychain_credentials unless env_credentials?
|
15
|
-
stacks = parse_stacks(options.merge(active: true))
|
16
|
-
deployments = stacks.map do |stack|
|
17
|
-
next unless (app = stack.find_app_by_name(name))
|
18
|
-
say "Deploying to #{stack.name}..."
|
19
|
-
stack.deploy_app(app)
|
20
|
-
end
|
21
|
-
deployments.compact!
|
22
|
-
OpsWorks::Deployment.wait(deployments)
|
23
|
-
unless deployments.all?(&:success?)
|
24
|
-
failures = []
|
25
|
-
deployments.each_with_index do |deployment, i|
|
26
|
-
failures << stacks[i].name unless deployment.success?
|
27
|
-
end
|
28
|
-
fail "Deploy failed on #{failures.join(', ')}"
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
# rubocop:enable CyclomaticComplexity
|
34
|
-
# rubocop:enable MethodLength
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
require 'opsworks/permission'
|
2
|
-
|
3
|
-
module OpsWorks
|
4
|
-
module CLI
|
5
|
-
module Subcommands
|
6
|
-
module Lockdown
|
7
|
-
# rubocop:disable MethodLength
|
8
|
-
# rubocop:disable CyclomaticComplexity
|
9
|
-
def self.included(thor)
|
10
|
-
thor.class_eval do
|
11
|
-
desc 'lockdown [--stack STACK]', 'Remove all stack permissions'
|
12
|
-
option :stack, type: :array
|
13
|
-
def lockdown
|
14
|
-
fetch_keychain_credentials unless env_credentials?
|
15
|
-
stacks = parse_stacks(options.merge(active: true))
|
16
|
-
stacks.each do |stack|
|
17
|
-
say "Locking down #{stack.name}..."
|
18
|
-
stack.permissions.each do |permission|
|
19
|
-
permission.update(ssh: false, sudo: false)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
# rubocop:enable CyclomaticComplexity
|
26
|
-
# rubocop:enable MethodLength
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,45 +0,0 @@
|
|
1
|
-
module OpsWorks
|
2
|
-
module CLI
|
3
|
-
module Subcommands
|
4
|
-
module Status
|
5
|
-
# rubocop:disable MethodLength
|
6
|
-
# rubocop:disable CyclomaticComplexity
|
7
|
-
def self.included(thor)
|
8
|
-
thor.class_eval do
|
9
|
-
include Helpers::Keychain
|
10
|
-
include Helpers::Options
|
11
|
-
|
12
|
-
desc 'status APP [--stack STACK]',
|
13
|
-
'Display the most recent deployment of an app'
|
14
|
-
option :stack, type: :array
|
15
|
-
def status(name)
|
16
|
-
fetch_keychain_credentials unless env_credentials?
|
17
|
-
|
18
|
-
table = parse_stacks(options).map do |stack|
|
19
|
-
next unless (app = stack.find_app_by_name(name))
|
20
|
-
if (deployment = app.last_deployment)
|
21
|
-
deployed_at = formatted_time(deployment.created_at)
|
22
|
-
else
|
23
|
-
deployed_at = '-'
|
24
|
-
end
|
25
|
-
[stack.name, name, "(#{app.revision})", deployed_at]
|
26
|
-
end
|
27
|
-
# Sort output in descending date order
|
28
|
-
table.compact!
|
29
|
-
table.sort! { |x, y| y.last <=> x.last }
|
30
|
-
print_table table
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def formatted_time(timestamp)
|
36
|
-
timestamp.strftime('%Y-%m-%d %H:%M:%S %Z')
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
# rubocop:enable CyclomaticComplexity
|
41
|
-
# rubocop:enable MethodLength
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe OpsWorks::CLI::Agent do
|
4
|
-
describe '#allow' do
|
5
|
-
let(:permissions) { 2.times.map { Fabricate(:permission) } }
|
6
|
-
let(:user) { permissions[0].user }
|
7
|
-
let(:stacks) do
|
8
|
-
2.times.map do |i|
|
9
|
-
Fabricate(:stack).tap do |stack|
|
10
|
-
allow(stack).to receive(:find_permission_by_user) { permissions[i] }
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
before { allow(subject).to receive(:say) }
|
16
|
-
before { allow(OpsWorks::Stack).to receive(:all) { stacks } }
|
17
|
-
before { allow(OpsWorks::Stack).to receive(:active) { stacks } }
|
18
|
-
|
19
|
-
it 'should update all matching permissions' do
|
20
|
-
expect(permissions[0]).to receive(:update)
|
21
|
-
expect(permissions[1]).to receive(:update)
|
22
|
-
subject.allow(user)
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'should optionally run on a subset of stacks' do
|
26
|
-
expect(permissions[0]).to receive(:update)
|
27
|
-
expect(permissions[1]).not_to receive(:update)
|
28
|
-
|
29
|
-
allow(subject).to receive(:options) { { stack: [stacks[0].name] } }
|
30
|
-
subject.allow(user)
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'should accept :ssh and :sudo options' do
|
34
|
-
expect(permissions[0]).to receive(:update).with(ssh: true, sudo: false)
|
35
|
-
|
36
|
-
allow(subject).to receive(:options) do
|
37
|
-
{ stack: [stacks[0].name], ssh: true, sudo: false }
|
38
|
-
end
|
39
|
-
subject.allow(user)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe OpsWorks::CLI::Agent do
|
4
|
-
describe '#deploy' do
|
5
|
-
let(:app_name) { 'aptible' }
|
6
|
-
let(:app) { Fabricate(:app, name: app_name) }
|
7
|
-
|
8
|
-
let(:stacks) { 2.times.map { Fabricate(:stack) } }
|
9
|
-
let(:deployment) { Fabricate(:deployment, status: 'successful') }
|
10
|
-
|
11
|
-
before { allow(subject).to receive(:say) }
|
12
|
-
before { allow(OpsWorks::Deployment).to receive(:wait) }
|
13
|
-
before { allow(OpsWorks::Stack).to receive(:all) { stacks } }
|
14
|
-
before { allow(OpsWorks::Stack).to receive(:active) { stacks } }
|
15
|
-
|
16
|
-
before { stacks.each { |stack| allow(stack).to receive(:apps) { [app] } } }
|
17
|
-
|
18
|
-
it 'should update custom cookbooks on all stacks' do
|
19
|
-
expect(stacks[0]).to receive(:deploy_app).with(app) { deployment }
|
20
|
-
expect(stacks[1]).to receive(:deploy_app).with(app) { deployment }
|
21
|
-
subject.deploy(app_name)
|
22
|
-
end
|
23
|
-
|
24
|
-
it 'should not fail if some stacks are inactive' do
|
25
|
-
allow(OpsWorks::Stack).to receive(:active) { [stacks[0]] }
|
26
|
-
expect(stacks[0]).to receive(:deploy_app).with(app) { deployment }
|
27
|
-
expect(stacks[1]).not_to receive(:deploy_app)
|
28
|
-
subject.deploy(app_name)
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'should optionally run on a subset of stacks' do
|
32
|
-
expect(stacks[0]).to receive(:deploy_app).with(app) { deployment }
|
33
|
-
expect(stacks[1]).not_to receive(:deploy_app)
|
34
|
-
|
35
|
-
allow(subject).to receive(:options) { { stack: [stacks[0].name] } }
|
36
|
-
subject.deploy(app_name)
|
37
|
-
end
|
38
|
-
|
39
|
-
it 'should not fail if a stack does not have the app' do
|
40
|
-
allow(stacks[0]).to receive(:apps) { [] }
|
41
|
-
expect(stacks[1]).to receive(:deploy_app).with(app) { deployment }
|
42
|
-
expect { subject.deploy(app_name) }.not_to raise_error
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'should fail if any update fails' do
|
46
|
-
failure = Fabricate(:deployment, status: 'failed')
|
47
|
-
expect(stacks[0]).to receive(:deploy_app).with(app) { failure }
|
48
|
-
|
49
|
-
allow(subject).to receive(:options) { { stack: [stacks[0].name] } }
|
50
|
-
expect { subject.deploy(app_name) }.to raise_error
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe OpsWorks::CLI::Agent do
|
4
|
-
describe '#exec' do
|
5
|
-
let(:recipe) { 'hotpockets::install' }
|
6
|
-
|
7
|
-
let(:stacks) { 2.times.map { Fabricate(:stack) } }
|
8
|
-
let(:deployment) { Fabricate(:deployment, status: 'successful') }
|
9
|
-
|
10
|
-
before { allow(subject).to receive(:say) }
|
11
|
-
before { allow(OpsWorks::Deployment).to receive(:wait) }
|
12
|
-
before { allow(OpsWorks::Stack).to receive(:all) { stacks } }
|
13
|
-
before { allow(OpsWorks::Stack).to receive(:active) { stacks } }
|
14
|
-
|
15
|
-
it 'should update custom cookbooks on all stacks' do
|
16
|
-
expect(stacks[0]).to receive(:execute_recipe).with(recipe) { deployment }
|
17
|
-
expect(stacks[1]).to receive(:execute_recipe).with(recipe) { deployment }
|
18
|
-
subject.exec(recipe)
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'should optionally run on a subset of stacks' do
|
22
|
-
expect(stacks[0]).to receive(:execute_recipe).with(recipe) { deployment }
|
23
|
-
expect(stacks[1]).not_to receive(:execute_recipe)
|
24
|
-
|
25
|
-
allow(subject).to receive(:options) { { stack: [stacks[0].name] } }
|
26
|
-
subject.exec(recipe)
|
27
|
-
end
|
28
|
-
|
29
|
-
it 'should fail if any update fails' do
|
30
|
-
failure = Fabricate(:deployment, status: 'failed')
|
31
|
-
expect(stacks[0]).to receive(:execute_recipe).with(recipe) { failure }
|
32
|
-
|
33
|
-
allow(subject).to receive(:options) { { stack: [stacks[0].name] } }
|
34
|
-
expect { subject.exec(recipe) }.to raise_error
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe OpsWorks::CLI::Agent do
|
4
|
-
describe '#lockdown' do
|
5
|
-
let(:permissions) { 2.times.map { Fabricate(:permission) } }
|
6
|
-
let(:user) { permissions[0].user }
|
7
|
-
let(:stack) do
|
8
|
-
Fabricate(:stack).tap do |stack|
|
9
|
-
allow(stack).to receive(:permissions) { permissions }
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
before { allow(subject).to receive(:say) }
|
14
|
-
before { allow(OpsWorks::Stack).to receive(:all) { [stack] } }
|
15
|
-
before { allow(OpsWorks::Stack).to receive(:active) { [stack] } }
|
16
|
-
|
17
|
-
it 'should lock down all stacks' do
|
18
|
-
expect(permissions[0]).to receive(:update).with(ssh: false, sudo: false)
|
19
|
-
expect(permissions[1]).to receive(:update).with(ssh: false, sudo: false)
|
20
|
-
subject.lockdown
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'should optionally run on a subset of stacks' do
|
24
|
-
expect(permissions[0]).to receive(:update).with(ssh: false, sudo: false)
|
25
|
-
expect(permissions[1]).to receive(:update).with(ssh: false, sudo: false)
|
26
|
-
|
27
|
-
allow(subject).to receive(:options) { { stacks: [stack.name] } }
|
28
|
-
subject.lockdown
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|