opsworks-cli 0.4.5 → 0.5.0

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: 00979b100ad560ac20e1198ebde33d3b81d3f46f
4
- data.tar.gz: 3f66d10db7a20b4aff6db3b1d1b5ab214efba24e
3
+ metadata.gz: 1f9314cdf3a4f3ca9c386e2dc2f0e537cfa498f9
4
+ data.tar.gz: cfb55e0c88b9efb9188c0fece32de6edbf83494e
5
5
  SHA512:
6
- metadata.gz: 79ada7860aa57e55e00edc85d5b52162a416ceed7b23d3c966ba4441a0ac140824aeb4c236d35172098567c40ff330dc5d109d2bce5f376326afdf55f52dff8b
7
- data.tar.gz: 1bfc5d83a69d437590abfa1ff791ba719bc829a410e624fda20201c6855d93269864a8b0a711711c8f78dd115d37120c8e53563349015d54e6d98c796736d7ac
6
+ metadata.gz: c63763cc141a5bc68fc7a32632cf6b73da5ba4e316dfbc2b96c51b7b29aded201b45eb857b5cc79fc42bacbb31a9b630081ad93c8755577329297963d47f4ab9
7
+ data.tar.gz: 221fe534dcb3affcc3de82ec1038fe5524c48af2a9a5ea79f69a446e9ec6b418b893cf80538de625f3dc7d91cb29f71fd5f277d1656081e018eb398ba39746ab
@@ -1,3 +1,6 @@
1
+ sudo: false
2
+
1
3
  rvm:
2
- - 2.0.0
3
- - jruby
4
+ - 2.1.0
5
+ - 2.2.0
6
+ - 2.3.0
data/Gemfile CHANGED
@@ -1,4 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ # Support older Rubies in specs
4
+ gem 'activesupport', '~> 4.0'
5
+
3
6
  # Specify your gem's dependencies in opsworks-cli.gemspec
4
7
  gemspec
data/README.md CHANGED
@@ -14,12 +14,14 @@ Install the gem:
14
14
 
15
15
  ## Configuration
16
16
 
17
- The gem expects to have access to your AWS access key ID and secret access key. You can configure this in either of two ways. First, you may set the following environment variables:
17
+ The gem expects to have access to your AWS access key ID and secret access key. You may set the following environment variables:
18
18
 
19
19
  export AWS_ACCESS_KEY_ID=...
20
20
  export AWS_SECRET_ACCESS_KEY=...
21
21
 
22
- Second (the preferred option), you may use the [Omnivault](https://github.com/aptible/omnivault) gem to store and configure your credentials automatically, via your choice of password-protected vault (Apple Keychain or pws).
22
+ To avoid manually setting these ENV variables each time, you may use the [Omnivault](https://github.com/aptible/omnivault) gem to store and configure your credentials once, then access them later by running e.g.
23
+
24
+ omnivault exec opsworks [...]
23
25
 
24
26
  ## Usage
25
27
 
@@ -5,10 +5,11 @@ module OpsWorks
5
5
  class App < Resource
6
6
  attr_accessor :id, :name, :revision
7
7
 
8
- def self.from_collection_response(response)
9
- response.data[:apps].map do |hash|
8
+ def self.from_collection_response(client, response)
9
+ response.data[:apps].map do |app|
10
+ hash = app.to_h
10
11
  revision = hash[:app_source][:revision] if hash[:app_source]
11
- new(id: hash[:app_id], name: hash[:name], revision: revision)
12
+ new(client, id: hash[:app_id], name: hash[:name], revision: revision)
12
13
  end
13
14
  end
14
15
 
@@ -20,12 +21,21 @@ module OpsWorks
20
21
  deployments.find(&:success?)
21
22
  end
22
23
 
24
+ def update_revision(revision)
25
+ client.update_app(
26
+ app_id: id,
27
+ app_source: { revision: revision }
28
+ )
29
+
30
+ self.revision = revision
31
+ end
32
+
23
33
  private
24
34
 
25
35
  def initialize_deployments
26
36
  return [] unless id
27
- response = self.class.client.describe_deployments(app_id: id)
28
- Deployment.from_collection_response(response)
37
+ response = client.describe_deployments(app_id: id)
38
+ Deployment.from_collection_response(client, response)
29
39
  end
30
40
  end
31
41
  end
@@ -1,7 +1,5 @@
1
1
  require 'thor'
2
- require 'aws'
3
2
 
4
- require_relative 'helpers/credentials'
5
3
  require_relative 'helpers/options'
6
4
  require_relative 'helpers/typecasts'
7
5
 
@@ -10,13 +8,13 @@ require_relative 'subcommands/recipes'
10
8
  require_relative 'subcommands/apps'
11
9
  require_relative 'subcommands/iam'
12
10
  require_relative 'subcommands/config'
11
+ require_relative 'subcommands/deployments'
13
12
 
14
13
  module OpsWorks
15
14
  module CLI
16
15
  class Agent < Thor
17
16
  include Thor::Actions
18
17
 
19
- include Helpers::Credentials
20
18
  include Helpers::Options
21
19
  include Helpers::Typecasts
22
20
 
@@ -25,6 +23,7 @@ module OpsWorks
25
23
  include Subcommands::Apps
26
24
  include Subcommands::IAM
27
25
  include Subcommands::Config
26
+ include Subcommands::Deployments
28
27
 
29
28
  desc 'version', 'Print OpsWorks CLI version'
30
29
  def version
@@ -4,31 +4,37 @@ module OpsWorks
4
4
  module CLI
5
5
  module Subcommands
6
6
  module Apps
7
- # rubocop:disable MethodLength
8
- # rubocop:disable CyclomaticComplexity
9
- # rubocop:disable PerceivedComplexity
10
7
  def self.included(thor)
11
8
  thor.class_eval do
12
9
  desc 'apps:deploy APP [--stack STACK]', 'Deploy an OpsWorks app'
13
10
  option :stack, type: :array
14
11
  option :timeout, type: :numeric, default: 300
15
12
  option :migrate, type: :boolean, default: false
13
+ option :layer, type: :string
16
14
  define_method 'apps:deploy' do |name|
17
- fetch_credentials unless env_credentials?
18
15
  stacks = parse_stacks(options.merge(active: true))
19
16
  deployments = stacks.map do |stack|
20
17
  next unless (app = stack.find_app_by_name(name))
21
18
  say "Deploying to #{stack.name}..."
22
- stack.deploy_app(app, 'migrate' => [options[:migrate].to_s])
23
- end
24
- deployments.compact!
25
- OpsWorks::Deployment.wait(deployments, options[:timeout])
26
- unless deployments.all?(&:success?)
27
- failures = []
28
- deployments.each_with_index do |deployment, i|
29
- failures << stacks[i].name unless deployment.success?
30
- end
31
- fail "Deploy failed on #{failures.join(', ')}"
19
+ dpl = stack.deploy_app(
20
+ app,
21
+ layer: options[:layer],
22
+ args: { 'migrate' => [options[:migrate].to_s] }
23
+ )
24
+ next unless dpl
25
+ [stack, dpl]
26
+ end.compact
27
+
28
+ OpsWorks::Deployment.wait(deployments.map(&:last),
29
+ options[:timeout])
30
+
31
+ failures = deployments.map do |stack, deployment|
32
+ next if deployment.success?
33
+ stack
34
+ end.compact
35
+
36
+ unless failures.empty?
37
+ raise "Deploy failed on #{failures.map(&:name).join(' ')}"
32
38
  end
33
39
  end
34
40
 
@@ -36,8 +42,6 @@ module OpsWorks
36
42
  'Display the most recent deployment of an app'
37
43
  option :stack, type: :array
38
44
  define_method 'apps:status' do |name|
39
- fetch_credentials unless env_credentials?
40
-
41
45
  table = parse_stacks(options).map do |stack|
42
46
  next unless (app = stack.find_app_by_name(name))
43
47
  if (deployment = app.last_deployment)
@@ -60,12 +64,11 @@ module OpsWorks
60
64
  option :shortname
61
65
  define_method 'apps:create' do |name|
62
66
  unless %w(other).include?(options[:type])
63
- fail "Unsupported type: #{options[:type]}"
67
+ raise "Unsupported type: #{options[:type]}"
64
68
  end
65
69
 
66
- fail 'Git URL not yet supported' if options[:git_url]
70
+ raise 'Git URL not yet supported' if options[:git_url]
67
71
 
68
- fetch_credentials unless env_credentials?
69
72
  stacks = parse_stacks(options)
70
73
 
71
74
  stacks.each do |stack|
@@ -75,6 +78,18 @@ module OpsWorks
75
78
  end
76
79
  end
77
80
 
81
+ desc 'apps:revision:update APP REVISION [--stack STACK]',
82
+ 'Set the revision for an app'
83
+ option :stack, type: :array
84
+ define_method 'apps:revision:update' do |app_name, revision|
85
+ stacks = parse_stacks(options.merge(active: true))
86
+ stacks.each do |stack|
87
+ next unless (app = stack.find_app_by_name(app_name))
88
+ say "Updating #{stack.name} (from: #{app.revision})..."
89
+ app.update_revision(revision)
90
+ end
91
+ end
92
+
78
93
  private
79
94
 
80
95
  def formatted_time(timestamp)
@@ -82,9 +97,6 @@ module OpsWorks
82
97
  end
83
98
  end
84
99
  end
85
- # rubocop:enable PerceivedComplexity
86
- # rubocop:enable CyclomaticComplexity
87
- # rubocop:enable MethodLength
88
100
  end
89
101
  end
90
102
  end
@@ -1,12 +1,9 @@
1
- require 'aws'
2
1
  require 'opsworks/stack'
3
2
 
4
3
  module OpsWorks
5
4
  module CLI
6
5
  module Subcommands
7
6
  module Chef
8
- # rubocop:disable MethodLength
9
- # rubocop:disable CyclomaticComplexity
10
7
  def self.included(thor)
11
8
  thor.class_eval do
12
9
  desc 'chef:configure [--stack STACK]', 'Configure Chef/Berkshelf'
@@ -20,7 +17,6 @@ module OpsWorks
20
17
  option :cookbook_username
21
18
  option :cookbook_password
22
19
  define_method 'chef:configure' do
23
- fetch_credentials unless env_credentials?
24
20
  stacks = parse_stacks(options.merge(active: true))
25
21
  stacks.each do |stack|
26
22
  say "Configuring Chef #{options[:version]} on #{stack.name}..."
@@ -32,25 +28,28 @@ module OpsWorks
32
28
  option :stack, type: :array
33
29
  option :timeout, type: :numeric, default: 300
34
30
  define_method 'chef:sync' do
35
- fetch_credentials unless env_credentials?
36
31
  stacks = parse_stacks(options.merge(active: true))
37
32
  deployments = stacks.map do |stack|
38
33
  say "Syncing #{stack.name}..."
39
- stack.update_custom_cookbooks
40
- end
41
- OpsWorks::Deployment.wait(deployments, options[:timeout])
42
- unless deployments.all?(&:success?)
43
- failures = []
44
- deployments.each_with_index do |deployment, i|
45
- failures << stacks[i].name unless deployment.success?
46
- end
47
- fail "Update failed on #{failures.join(', ')}"
34
+ dpl = stack.update_custom_cookbooks
35
+ next unless dpl
36
+ [stack, dpl]
37
+ end.compact
38
+
39
+ OpsWorks::Deployment.wait(deployments.map(&:last),
40
+ options[:timeout])
41
+
42
+ failures = deployments.map do |stack, deployment|
43
+ next if deployment.success?
44
+ stack
45
+ end.compact
46
+
47
+ unless failures.empty?
48
+ raise "Deploy failed on #{failures.map(&:name).join(', ')}"
48
49
  end
49
50
  end
50
51
  end
51
52
  end
52
- # rubocop:enable CyclomaticComplexity
53
- # rubocop:enable MethodLength
54
53
  end
55
54
  end
56
55
  end
@@ -1,21 +1,17 @@
1
- require 'aws'
2
1
  require 'opsworks/stack'
3
2
 
4
3
  module OpsWorks
5
4
  module CLI
6
5
  module Subcommands
7
6
  module Config
8
- # rubocop:disable MethodLength
9
- # rubocop:disable CyclomaticComplexity
10
7
  def self.included(thor)
11
8
  thor.class_eval do
12
9
  desc 'config:get KEY [--stack STACK]', 'Get a single config value'
13
10
  option :stack, type: :array
14
11
  define_method 'config:get' do |key|
15
- fetch_credentials unless env_credentials?
16
12
  table = parse_stacks(options).map do |stack|
17
13
  value = stack.custom_json_at(key)
18
- [stack.name, value || '(null)']
14
+ [stack.name, value.nil? ? '(null)' : value]
19
15
  end
20
16
  table.compact!
21
17
  table.sort! { |x, y| x.first <=> y.first }
@@ -25,7 +21,6 @@ module OpsWorks
25
21
  desc 'config:set KEY VALUE [--stack STACK]', 'Set a config value'
26
22
  option :stack, type: :array
27
23
  define_method 'config:set' do |key, value|
28
- fetch_credentials unless env_credentials?
29
24
  parse_stacks(options).each do |stack|
30
25
  say "Updating #{stack.name}..."
31
26
  stack.set_custom_json_at(key, typecast_string_argument(value))
@@ -35,7 +30,6 @@ module OpsWorks
35
30
  desc 'config:unset KEY [--stack STACK]', 'Unset a config value'
36
31
  option :stack, type: :array
37
32
  define_method 'config:unset' do |key|
38
- fetch_credentials unless env_credentials?
39
33
  parse_stacks(options).map do |stack|
40
34
  say "Updating #{stack.name}..."
41
35
  stack.set_custom_json_at(key, nil)
@@ -43,8 +37,6 @@ module OpsWorks
43
37
  end
44
38
  end
45
39
  end
46
- # rubocop:enable CyclomaticComplexity
47
- # rubocop:enable MethodLength
48
40
  end
49
41
  end
50
42
  end
@@ -0,0 +1,82 @@
1
+ module OpsWorks
2
+ module CLI
3
+ module Subcommands
4
+ module Deployments
5
+ def self.included(thor)
6
+ thor.class_eval do
7
+ desc 'deployments:retry [--stack STACK]', 'Retry last deployment'
8
+ option :stack, type: :array
9
+ option :timeout, type: :numeric, default: 300
10
+ define_method 'deployments:retry' do
11
+ stacks = parse_stacks(options.merge(active: true))
12
+
13
+ last_deployment = stacks.map do |stack|
14
+ say "Loading last deployment for #{stack.name}"
15
+ [stack, stack.deployments.max_by(&:created_at)]
16
+ end
17
+
18
+ will_retry = last_deployment.map do |stack, deployment|
19
+ if deployment.status == 'successful'
20
+ say "Skipping stack #{stack.name}: last deployment is " \
21
+ "#{deployment.status}"
22
+ next
23
+ end
24
+
25
+ say "Loading instance status for #{stack.name}"
26
+
27
+ res = deployment.client.describe_commands(
28
+ deployment_id: deployment.id
29
+ )
30
+
31
+ retry_commands = res.commands.select do |command|
32
+ command.status != 'successful'
33
+ end
34
+
35
+ instance_ids = retry_commands.map(&:instance_id).to_a
36
+
37
+ if instance_ids.empty?
38
+ say "Skipping #{stack.name}: no instances failed"
39
+ next
40
+ end
41
+
42
+ [stack, deployment, instance_ids]
43
+ end.compact
44
+
45
+ deployments = will_retry.map do |stack, dep, instance_ids|
46
+ say "Retrying #{dep.command[:name]} in #{stack.name} " \
47
+ "on #{instance_ids.join ' '}"
48
+
49
+ opts = {
50
+ command: dep.command,
51
+ instance_ids: instance_ids
52
+ }
53
+ opts[:app_id] = dep.app_id if dep.app_id
54
+ opts[:custom_json] = dep.custom_json if dep.custom_json
55
+
56
+ new_deployment = stack.send(:create_deployment, opts)
57
+
58
+ [stack, new_deployment]
59
+ end
60
+
61
+ say "Waiting #{options[:timeout]}s for deployments to finish"
62
+
63
+ OpsWorks::Deployment.wait(deployments.map(&:last),
64
+ options[:timeout])
65
+
66
+ failures = deployments.map do |stack, deployment|
67
+ next if deployment.success?
68
+ stack
69
+ end.compact
70
+
71
+ unless failures.empty?
72
+ raise "Deploy failed on #{failures.map(&:name).join(' ')}"
73
+ end
74
+
75
+ say "All #{deployments.size} deployments suceeded"
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -4,8 +4,6 @@ module OpsWorks
4
4
  module CLI
5
5
  module Subcommands
6
6
  module IAM
7
- # rubocop:disable MethodLength
8
- # rubocop:disable CyclomaticComplexity
9
7
  def self.included(thor)
10
8
  thor.class_eval do
11
9
  desc 'iam:allow USER [--stack STACK]',
@@ -14,7 +12,6 @@ module OpsWorks
14
12
  option :ssh, type: :boolean, default: true
15
13
  option :sudo, type: :boolean, default: true
16
14
  define_method 'iam:allow' do |user|
17
- fetch_credentials unless env_credentials?
18
15
  stacks = parse_stacks(options.merge(active: true))
19
16
  stacks.each do |stack|
20
17
  permission = stack.find_permission_by_user(user)
@@ -27,7 +24,6 @@ module OpsWorks
27
24
  desc 'iam:lockdown [--stack STACK]', 'Remove all stack permissions'
28
25
  option :stack, type: :array
29
26
  define_method 'iam:lockdown' do
30
- fetch_credentials unless env_credentials?
31
27
  stacks = parse_stacks(options.merge(active: true))
32
28
  stacks.each do |stack|
33
29
  say "Locking down #{stack.name}..."
@@ -38,8 +34,6 @@ module OpsWorks
38
34
  end
39
35
  end
40
36
  end
41
- # rubocop:enable CyclomaticComplexity
42
- # rubocop:enable MethodLength
43
37
  end
44
38
  end
45
39
  end
@@ -4,15 +4,12 @@ module OpsWorks
4
4
  module CLI
5
5
  module Subcommands
6
6
  module Recipes
7
- # rubocop:disable MethodLength
8
- # rubocop:disable CyclomaticComplexity
9
7
  def self.included(thor)
10
8
  thor.class_eval do
11
9
  desc 'recipes:run RECIPE [--stack STACK]', 'Execute a Chef recipe'
12
10
  option :stack, type: :array
13
11
  option :timeout, type: :numeric, default: 300
14
12
  define_method 'recipes:run' do |recipe|
15
- fetch_credentials unless env_credentials?
16
13
  stacks = parse_stacks(options.merge(active: true))
17
14
  deployments = stacks.map do |stack|
18
15
  say "Executing recipe on #{stack.name}..."
@@ -24,7 +21,7 @@ module OpsWorks
24
21
  deployments.each_with_index do |deployment, i|
25
22
  failures << stacks[i].name unless deployment.success?
26
23
  end
27
- fail "Command failed on #{failures.join(', ')}"
24
+ raise "Command failed on #{failures.join(', ')}"
28
25
  end
29
26
  end
30
27
 
@@ -32,7 +29,6 @@ module OpsWorks
32
29
  'Add a recipe to a given layer and lifecycle event'
33
30
  option :stack, type: :array
34
31
  define_method 'recipes:add' do |layername, event, recipe|
35
- fetch_credentials unless env_credentials?
36
32
  stacks = parse_stacks(options)
37
33
  stacks.each do |stack|
38
34
  layer = stack.layers.find { |l| l.shortname == layername }
@@ -48,7 +44,6 @@ module OpsWorks
48
44
  'Remove a recipe from a given layer and lifecycle event'
49
45
  option :stack, type: :array
50
46
  define_method 'recipes:rm' do |layername, event, recipe|
51
- fetch_credentials unless env_credentials?
52
47
  stacks = parse_stacks(options)
53
48
  stacks.each do |stack|
54
49
  layer = stack.layers.find { |l| l.shortname == layername }
@@ -61,8 +56,6 @@ module OpsWorks
61
56
  end
62
57
  end
63
58
  end
64
- # rubocop:enable CyclomaticComplexity
65
- # rubocop:enable MethodLength
66
59
  end
67
60
  end
68
61
  end
@@ -1,5 +1,5 @@
1
1
  module OpsWorks
2
2
  module CLI
3
- VERSION = '0.4.5'
3
+ VERSION = '0.5.0'.freeze
4
4
  end
5
5
  end
@@ -2,13 +2,12 @@ require 'opsworks/resource'
2
2
 
3
3
  module OpsWorks
4
4
  class Deployment < Resource
5
- attr_accessor :id, :status, :created_at
5
+ attr_accessor :id, :command, :status, :created_at, :custom_json, :app_id
6
6
 
7
7
  TIMEOUT = 300
8
8
  POLL_INTERVAL = 5
9
9
  API_LIMIT = 25
10
10
 
11
- # rubocop:disable MethodLength
12
11
  def self.wait(deployments, timeout = TIMEOUT)
13
12
  start_time = Time.now
14
13
  timeout ||= TIMEOUT
@@ -17,41 +16,40 @@ module OpsWorks
17
16
  sleep POLL_INTERVAL
18
17
  updates = []
19
18
  running_deployments = deployments.select(&:running?)
20
- running_deployments.map(&:id).each_slice(API_LIMIT) do |slice|
21
- response = client.describe_deployments(
22
- deployment_ids: slice
23
- )
24
- updates += from_collection_response(response)
19
+ groups = running_deployments.group_by { |d| d.client.config.region }
20
+
21
+ groups.each do |region, region_deployments|
22
+ client = Aws::OpsWorks::Client.new(region: region)
23
+ region_deployments.map(&:id).each_slice(API_LIMIT) do |slice|
24
+ response = client.describe_deployments(deployment_ids: slice)
25
+ updates += from_collection_response(client, response)
26
+ end
25
27
  end
28
+
26
29
  running_deployments.each do |deployment|
27
30
  update = updates.find { |u| u.id == deployment.id }
28
31
  deployment.status = update.status
29
32
  end
30
33
  end
31
34
  end
32
- # rubocop:enble MethodLength
33
35
 
34
- def self.from_collection_response(response)
35
- response.data[:deployments].map do |hash|
36
+ def self.from_collection_response(client, response)
37
+ response.data[:deployments].map do |deployment|
38
+ hash = deployment.to_h
36
39
  new(
40
+ client,
37
41
  id: hash[:deployment_id],
42
+ command: hash[:command],
38
43
  created_at: hash[:created_at],
39
- status: hash[:status]
44
+ status: hash[:status],
45
+ custom_json: hash[:custom_json],
46
+ app_id: hash[:app_id]
40
47
  )
41
48
  end
42
49
  end
43
50
 
44
- def self.from_response(response)
45
- new(id: response[:deployment_id])
46
- end
47
-
48
- def wait
49
- while deployment.running?
50
- sleep POLL_INTERVAL
51
- response = client.describe_deployments(deployment_ids: [id])
52
- update = from_collection_response(response).first
53
- deployment.status = update.status
54
- end
51
+ def self.from_response(client, response)
52
+ new(client, id: response[:deployment_id])
55
53
  end
56
54
 
57
55
  def running?
@@ -4,9 +4,10 @@ module OpsWorks
4
4
  class Instance < Resource
5
5
  attr_accessor :id, :hostname, :ec2_instance_id, :instance_type, :status
6
6
 
7
- def self.from_collection_response(response)
7
+ def self.from_collection_response(client, response)
8
8
  response.data[:instances].map do |hash|
9
9
  new(
10
+ client,
10
11
  id: hash[:instance_id],
11
12
  hostname: hash[:hostname],
12
13
  ec2_instance_id: hash[:ec2_instance_id],
@@ -5,14 +5,15 @@ module OpsWorks
5
5
  class Layer < Resource
6
6
  attr_accessor :id, :name, :shortname, :custom_recipes
7
7
 
8
- # rubocop:disable MethodLength
9
- def self.from_collection_response(response)
10
- response.data[:layers].map do |hash|
8
+ def self.from_collection_response(client, response)
9
+ response.data[:layers].map do |layer|
10
+ hash = layer.to_h
11
11
  # Make custom_recipes accessible by string or symbol
12
12
  custom_recipes = Thor::CoreExt::HashWithIndifferentAccess.new(
13
13
  hash[:custom_recipes]
14
14
  )
15
15
  new(
16
+ client,
16
17
  id: hash[:layer_id],
17
18
  name: hash[:name],
18
19
  shortname: hash[:shortname],
@@ -20,14 +21,13 @@ module OpsWorks
20
21
  )
21
22
  end
22
23
  end
23
- # rubocop:enable MethodLength
24
24
 
25
25
  def add_custom_recipe(event, recipe)
26
26
  return if custom_recipes[event].include?(recipe)
27
27
 
28
28
  custom_recipes[event] ||= []
29
29
  custom_recipes[event].push recipe
30
- self.class.client.update_layer(
30
+ client.update_layer(
31
31
  layer_id: id,
32
32
  custom_recipes: custom_recipes
33
33
  )
@@ -37,7 +37,7 @@ module OpsWorks
37
37
  return unless custom_recipes[event].include?(recipe)
38
38
 
39
39
  custom_recipes[event].delete recipe
40
- self.class.client.update_layer(
40
+ client.update_layer(
41
41
  layer_id: id,
42
42
  custom_recipes: custom_recipes
43
43
  )
@@ -4,9 +4,11 @@ module OpsWorks
4
4
  class Permission < Resource
5
5
  attr_accessor :id, :stack_id, :iam_user_arn, :ssh, :sudo
6
6
 
7
- def self.from_collection_response(response)
8
- response.data[:permissions].map do |hash|
7
+ def self.from_collection_response(client, response)
8
+ response.data[:permissions].map do |permission|
9
+ hash = permission.to_h
9
10
  new(
11
+ client,
10
12
  id: hash[:permission_id],
11
13
  stack_id: hash[:stack_id],
12
14
  iam_user_arn: hash[:iam_user_arn],
@@ -32,7 +34,7 @@ module OpsWorks
32
34
  options[:ssh] = ssh if options[:ssh].nil?
33
35
  options[:sudo] = sudo if options[:sudo].nil?
34
36
 
35
- self.class.client.set_permission(
37
+ client.set_permission(
36
38
  stack_id: stack_id,
37
39
  iam_user_arn: iam_user_arn,
38
40
  allow_ssh: options[:ssh],
@@ -1,17 +1,15 @@
1
- require 'aws'
2
-
3
1
  module OpsWorks
4
2
  class Resource
5
- def initialize(options = {})
3
+ attr_reader :client
4
+
5
+ def initialize(client, options = {})
6
+ @client = client
7
+
6
8
  options.each do |key, value|
7
9
  send("#{key}=", value) if respond_to?("#{key}=")
8
10
  end
9
11
  end
10
12
 
11
- def self.client
12
- @client ||= AWS::OpsWorks::Client.new
13
- end
14
-
15
13
  def self.account
16
14
  ENV['ACCOUNT'] || @account || 'opsworks-cli'
17
15
  end
@@ -1,4 +1,5 @@
1
1
  require 'jsonpath'
2
+ require 'aws-sdk'
2
3
  require 'active_support/core_ext/hash/slice'
3
4
 
4
5
  require 'opsworks/resource'
@@ -8,20 +9,38 @@ require 'opsworks/permission'
8
9
  require 'opsworks/layer'
9
10
 
10
11
  module OpsWorks
11
- # rubocop:disable ClassLength
12
12
  class Stack < Resource
13
13
  attr_accessor :id, :name, :custom_json
14
14
 
15
- AVAILABLE_CHEF_VERSIONS = %w(0.9 11.4 11.10)
15
+ AVAILABLE_CHEF_VERSIONS = %w(0.9 11.4 11.10).freeze
16
+ DEPLOY_NO_INSTANCES_ERROR = 'Please provide at least an instance ID of ' \
17
+ 'one running instance'.freeze
16
18
 
17
19
  def self.all
18
- client.describe_stacks.data[:stacks].map do |hash|
19
- new(
20
- id: hash[:stack_id],
21
- name: hash[:name],
22
- custom_json: JSON.parse(hash.fetch(:custom_json, '{}'))
23
- )
20
+ regions = Aws.partition('aws').regions.select do |region|
21
+ region.services.include?('OpsWorks')
22
+ end.map(&:name)
23
+
24
+ stack_queue = Queue.new
25
+
26
+ threads = regions.map do |region|
27
+ Thread.new do
28
+ client = Aws::OpsWorks::Client.new(region: region)
29
+ client.describe_stacks.stacks.each do |stack|
30
+ stack_queue << new(
31
+ client,
32
+ id: stack.stack_id,
33
+ name: stack.name,
34
+ custom_json: JSON.parse(stack.custom_json || '{}')
35
+ )
36
+ end
37
+ end
24
38
  end
39
+ threads.each(&:join)
40
+
41
+ stacks = []
42
+ stacks << stack_queue.pop until stack_queue.empty?
43
+ stacks
25
44
  end
26
45
 
27
46
  def self.active
@@ -60,6 +79,10 @@ module OpsWorks
60
79
  @layers ||= initialize_layers
61
80
  end
62
81
 
82
+ def deployments
83
+ @deployments ||= initialize_deployments
84
+ end
85
+
63
86
  def update_chef(options)
64
87
  params = {
65
88
  stack_id: id,
@@ -83,7 +106,7 @@ module OpsWorks
83
106
  password: options[:cookbook_password]
84
107
  }
85
108
  end
86
- self.class.client.update_stack(params)
109
+ client.update_stack(params)
87
110
  end
88
111
 
89
112
  def update_custom_cookbooks
@@ -99,15 +122,24 @@ module OpsWorks
99
122
  )
100
123
  end
101
124
 
102
- def deploy_app(app, args = {})
103
- fail 'App not found' unless app && app.id
104
- create_deployment(
125
+ def deploy_app(app, layer: nil, args: {})
126
+ raise 'App not found' unless app && app.id
127
+
128
+ deploy_args = {
105
129
  app_id: app.id,
106
130
  command: {
107
131
  name: 'deploy',
108
132
  args: args
109
133
  }
110
- )
134
+ }
135
+
136
+ if layer
137
+ layer = layers.find { |l| l.shortname == layer }
138
+ raise "Layer #{layer} not found" unless layer
139
+ deploy_args[:layer_ids] = [layer.id]
140
+ end
141
+
142
+ create_deployment(**deploy_args)
111
143
  end
112
144
 
113
145
  def active?
@@ -121,31 +153,23 @@ module OpsWorks
121
153
  def set_custom_json_at(key, value)
122
154
  self.custom_json = replace_hash_at_path(custom_json, key, value)
123
155
 
124
- self.class.client.update_stack(
156
+ client.update_stack(
125
157
  stack_id: id,
126
158
  custom_json: JSON.pretty_generate(custom_json)
127
159
  )
128
160
  end
129
161
 
130
162
  def create_app(name, options = {})
131
- options = options.slice(:type, :shortname)
132
- options.merge!(stack_id: id, name: name)
133
- self.class.client.create_app(options)
163
+ options = options.slice(:type, :shortname).merge(stack_id: id, name: name)
164
+ client.create_app(options)
134
165
  end
135
166
 
136
167
  private
137
168
 
138
- def initialize_apps
139
- return [] unless id
140
- response = self.class.client.describe_apps(stack_id: id)
141
- App.from_collection_response(response)
142
- end
143
-
144
169
  # rubocop:disable Eval
145
- # rubocop:disable MethodLength
146
170
  def replace_hash_at_path(hash, key, value)
147
171
  path = JsonPath.new(key).path
148
- if value
172
+ if !value.nil?
149
173
  # REVIEW: Is there a better way to parse the JSON Path and ensure
150
174
  # a value at the location?
151
175
  (0...(path.length - 1)).each do |i|
@@ -160,33 +184,46 @@ module OpsWorks
160
184
 
161
185
  hash
162
186
  end
163
- # rubocop:enable MethodLength
164
187
  # rubocop:enable Eval
165
188
 
189
+ def initialize_apps
190
+ return [] unless id
191
+ response = client.describe_apps(stack_id: id)
192
+ App.from_collection_response(client, response)
193
+ end
194
+
166
195
  def initialize_permissions
167
196
  return [] unless id
168
- response = self.class.client.describe_permissions(stack_id: id)
169
- Permission.from_collection_response(response)
197
+ response = client.describe_permissions(stack_id: id)
198
+ Permission.from_collection_response(client, response)
170
199
  end
171
200
 
172
201
  def initialize_instances
173
202
  return [] unless id
174
- response = self.class.client.describe_instances(stack_id: id)
175
- Instance.from_collection_response(response)
203
+ response = client.describe_instances(stack_id: id)
204
+ Instance.from_collection_response(client, response)
176
205
  end
177
206
 
178
207
  def initialize_layers
179
208
  return [] unless id
180
- response = self.class.client.describe_layers(stack_id: id)
181
- Layer.from_collection_response(response)
209
+ response = client.describe_layers(stack_id: id)
210
+ Layer.from_collection_response(client, response)
211
+ end
212
+
213
+ def initialize_deployments
214
+ return [] unless id
215
+ response = client.describe_deployments(stack_id: id)
216
+ Deployment.from_collection_response(client, response)
182
217
  end
183
218
 
184
219
  def create_deployment(options = {})
185
- response = self.class.client.create_deployment(
220
+ response = client.create_deployment(
186
221
  options.merge(stack_id: id)
187
222
  )
188
- Deployment.from_response(response)
223
+ rescue Aws::OpsWorks::Errors::ValidationException => e
224
+ raise unless e.message == DEPLOY_NO_INSTANCES_ERROR
225
+ else
226
+ Deployment.from_response(client, response)
189
227
  end
190
228
  end
191
- # rubocop:enable ClassLength
192
229
  end
@@ -21,15 +21,14 @@ Gem::Specification.new do |spec|
21
21
  spec.require_paths = ['lib']
22
22
 
23
23
  spec.add_dependency 'thor'
24
- spec.add_dependency 'aws-sdk-v1', '~> 1.64'
24
+ spec.add_dependency 'aws-sdk', '~> 2.9.6'
25
25
  spec.add_dependency 'jsonpath'
26
26
  spec.add_dependency 'activesupport'
27
- spec.add_dependency 'omnivault'
28
27
 
29
28
  spec.add_development_dependency 'bundler', '~> 1.5'
30
29
  spec.add_development_dependency 'aptible-tasks'
31
30
  spec.add_development_dependency 'rake'
32
31
  spec.add_development_dependency 'rspec', '~> 3.0'
33
- spec.add_development_dependency 'fabrication'
32
+ spec.add_development_dependency 'fabrication', '~> 2.16.0'
34
33
  spec.add_development_dependency 'pry'
35
34
  end
@@ -1,4 +1,5 @@
1
1
  Fabricator(:app, from: OpsWorks::App) do
2
+ initialize_with { @_klass.new(opsworks_stub) }
2
3
  id { SecureRandom.uuid }
3
4
  name { Fabricate.sequence(:name) { |i| "app#{i}" } }
4
5
  revision 'master'
@@ -1,4 +1,5 @@
1
1
  Fabricator(:deployment, from: OpsWorks::Deployment) do
2
+ initialize_with { @_klass.new(opsworks_stub) }
2
3
  id { SecureRandom.uuid }
3
4
  status 'running'
4
5
  created_at { Time.now }
@@ -1,4 +1,5 @@
1
1
  Fabricator(:permission, from: OpsWorks::Permission) do
2
+ initialize_with { @_klass.new(opsworks_stub) }
2
3
  id { SecureRandom.uuid }
3
4
  stack_id { SecureRandom.uuid }
4
5
  iam_user_arn { Fabricate.sequence(:iam) { |i| "iam::#{i}:user/bob" } }
@@ -1,4 +1,5 @@
1
1
  Fabricator(:stack, from: OpsWorks::Stack) do
2
+ initialize_with { @_klass.new(opsworks_stub) }
2
3
  id { SecureRandom.uuid }
3
4
  name { Fabricate.sequence(:name) { |i| "test-stack#{i}" } }
4
5
  custom_json { { 'env' => { 'FOO' => 'bar' } } }
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe OpsWorks::CLI::Agent do
4
4
  context 'apps' do
5
5
  let(:app_name) { 'aptible' }
6
- let(:stacks) { 2.times.map { Fabricate(:stack) } }
6
+ let(:stacks) { Array.new(2) { Fabricate(:stack) } }
7
7
 
8
8
  before { allow(subject).to receive(:say) }
9
9
  before { allow(OpsWorks::Deployment).to receive(:wait) }
@@ -46,9 +46,9 @@ describe OpsWorks::CLI::Agent do
46
46
 
47
47
  it 'should optionally run migrations' do
48
48
  expect(stacks[0]).to receive(:deploy_app)
49
- .with(app, 'migrate' => ['true']) { success }
49
+ .with(app, args: { 'migrate' => ['true'] }, layer: nil) { success }
50
50
  expect(stacks[1]).to receive(:deploy_app)
51
- .with(app, 'migrate' => ['true']) { success }
51
+ .with(app, args: { 'migrate' => ['true'] }, layer: nil) { success }
52
52
 
53
53
  allow(subject).to receive(:options) { { migrate: true } }
54
54
  subject.send('apps:deploy', app_name)
@@ -80,7 +80,7 @@ describe OpsWorks::CLI::Agent do
80
80
  end
81
81
 
82
82
  it 'should fail with a helpful error on unsupported type' do
83
- options.merge!(type: 'foobar')
83
+ options[:type] = 'foobar'
84
84
  allow(subject).to receive(:options) { options }
85
85
  expect { subject.send('apps:create', app_name) }.to raise_error
86
86
  end
@@ -95,12 +95,50 @@ describe OpsWorks::CLI::Agent do
95
95
  end
96
96
 
97
97
  it 'should accept a different shortname' do
98
- options.merge!(shortname: 'foobar')
98
+ options[:shortname] = 'foobar'
99
99
  allow(subject).to receive(:options) { options }
100
100
  expect(stacks[0]).to receive(:create_app).with(app_name, options)
101
101
  expect(stacks[1]).to receive(:create_app).with(app_name, options)
102
102
  subject.send('apps:create', app_name)
103
103
  end
104
104
  end
105
+
106
+ describe 'apps:revision:update' do
107
+ let(:revision) { '123' }
108
+
109
+ before do
110
+ stacks.each do |stack|
111
+ app = Fabricate(:app, name: app_name)
112
+ allow(stack).to receive(:apps) { [app] }
113
+ end
114
+ end
115
+
116
+ it 'should update the app revision on all stacks' do
117
+ expect(stacks[0].apps[0]).to receive(:update_revision).with(revision)
118
+ expect(stacks[1].apps[0]).to receive(:update_revision).with(revision)
119
+ subject.send('apps:revision:update', app_name, revision)
120
+ end
121
+
122
+ it 'should only run on active stacks' do
123
+ allow(OpsWorks::Stack).to receive(:active) { [stacks[0]] }
124
+ expect(stacks[0].apps[0]).to receive(:update_revision).with(revision)
125
+ expect(stacks[1].apps[0]).not_to receive(:update_revision)
126
+ subject.send('apps:revision:update', app_name, revision)
127
+ end
128
+
129
+ it 'should optionally run on a subset of stacks' do
130
+ expect(stacks[0].apps[0]).to receive(:update_revision).with(revision)
131
+ expect(stacks[1].apps[0]).not_to receive(:update_revision)
132
+
133
+ allow(subject).to receive(:options) { { stack: [stacks[0].name] } }
134
+ subject.send('apps:revision:update', app_name, revision)
135
+ end
136
+
137
+ it 'should not fail if a stack does not have the app' do
138
+ allow(stacks[0]).to receive(:apps) { [] }
139
+ expect(stacks[1].apps[0]).to receive(:update_revision).with(revision)
140
+ subject.send('apps:revision:update', app_name, revision)
141
+ end
142
+ end
105
143
  end
106
144
  end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe OpsWorks::CLI::Agent do
4
4
  describe 'chef:sync' do
5
- let(:stacks) { 2.times.map { Fabricate(:stack) } }
5
+ let(:stacks) { Array.new(2) { Fabricate(:stack) } }
6
6
  let(:deployment) { Fabricate(:deployment, status: 'successful') }
7
7
 
8
8
  before { allow(subject).to receive(:say) }
@@ -5,11 +5,9 @@ describe OpsWorks::CLI::Agent do
5
5
  let(:custom_json) { { 'env' => { 'FOO' => 'bar' } } }
6
6
  let(:json_path) { 'env.FOO' }
7
7
  let(:stack) { Fabricate(:stack, custom_json: custom_json) }
8
- let(:client) { double.as_null_object }
9
8
 
10
9
  before { allow(subject).to receive(:say) }
11
- before { allow(OpsWorks::Stack).to receive(:all) { [stack] } }
12
- before { allow(OpsWorks::Stack).to receive(:client) { client } }
10
+ before { allow(subject).to receive(:parse_stacks).and_return([stack]) }
13
11
 
14
12
  describe 'config:get' do
15
13
  it 'should print the variable from the stack custom JSON' do
@@ -30,7 +28,7 @@ describe OpsWorks::CLI::Agent do
30
28
 
31
29
  describe 'config:set' do
32
30
  it 'should reset the variable, if it is already set' do
33
- expect(client).to receive(:update_stack) do |hash|
31
+ expect(stack.client).to receive(:update_stack) do |hash|
34
32
  json = JSON.parse(hash[:custom_json])
35
33
  expect(json['env']['FOO']).to eq 'baz'
36
34
  end
@@ -40,7 +38,7 @@ describe OpsWorks::CLI::Agent do
40
38
 
41
39
  it 'should work with deep nested hashes' do
42
40
  stack.custom_json = { 'app' => { 'var' => 'value' } }
43
- expect(client).to receive(:update_stack) do |hash|
41
+ expect(stack.client).to receive(:update_stack) do |hash|
44
42
  json = JSON.parse(hash[:custom_json])
45
43
  expect(json['app']['env']['FOO']).to eq 'baz'
46
44
  end
@@ -50,7 +48,7 @@ describe OpsWorks::CLI::Agent do
50
48
 
51
49
  it 'should set the variable, if it is unset' do
52
50
  stack.custom_json = {}
53
- expect(client).to receive(:update_stack) do |hash|
51
+ expect(stack.client).to receive(:update_stack) do |hash|
54
52
  json = JSON.parse(hash[:custom_json])
55
53
  expect(json['env']['FOO']).to eq 'baz'
56
54
  end
@@ -59,8 +57,8 @@ describe OpsWorks::CLI::Agent do
59
57
  end
60
58
 
61
59
  it 'should leave other variables alone' do
62
- stack.custom_json.merge!('other' => 'something')
63
- expect(client).to receive(:update_stack) do |hash|
60
+ stack.custom_json['other'] = 'something'
61
+ expect(stack.client).to receive(:update_stack) do |hash|
64
62
  json = JSON.parse(hash[:custom_json])
65
63
  expect(json['env']['FOO']).to eq 'baz'
66
64
  expect(json['other']).to eq 'something'
@@ -72,7 +70,7 @@ describe OpsWorks::CLI::Agent do
72
70
 
73
71
  it 'should typecast Boolean values' do
74
72
  stack.custom_json = {}
75
- expect(client).to receive(:update_stack) do |hash|
73
+ expect(stack.client).to receive(:update_stack) do |hash|
76
74
  json = JSON.parse(hash[:custom_json])
77
75
  expect(json['env']['FOO']).to eq true
78
76
  end
@@ -83,7 +81,7 @@ describe OpsWorks::CLI::Agent do
83
81
 
84
82
  describe 'config:unset' do
85
83
  it 'should unset the variable' do
86
- expect(client).to receive(:update_stack) do |hash|
84
+ expect(stack.client).to receive(:update_stack) do |hash|
87
85
  json = JSON.parse(hash[:custom_json])
88
86
  expect(json['env'].keys).not_to include('FOO')
89
87
  end
@@ -92,8 +90,8 @@ describe OpsWorks::CLI::Agent do
92
90
  end
93
91
 
94
92
  it 'should leave other variables alone' do
95
- stack.custom_json['env'].merge!('OTHER' => 'something')
96
- expect(client).to receive(:update_stack) do |hash|
93
+ stack.custom_json['env']['OTHER'] = 'something'
94
+ expect(stack.client).to receive(:update_stack) do |hash|
97
95
  json = JSON.parse(hash[:custom_json])
98
96
  expect(json['env'].keys).not_to include('FOO')
99
97
  end
@@ -103,7 +101,7 @@ describe OpsWorks::CLI::Agent do
103
101
 
104
102
  it 'should work even with nil values' do
105
103
  stack.custom_json['env'] = { 'FOO' => nil }
106
- expect(client).to receive(:update_stack) do |hash|
104
+ expect(stack.client).to receive(:update_stack) do |hash|
107
105
  json = JSON.parse(hash[:custom_json])
108
106
  expect(json['env'].keys).not_to include('FOO')
109
107
  end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe OpsWorks::CLI::Agent do
4
4
  context 'iam' do
5
- let(:permissions) { 2.times.map { Fabricate(:permission) } }
5
+ let(:permissions) { Array.new(2) { Fabricate(:permission) } }
6
6
  let(:user) { permissions[0].user }
7
7
 
8
8
  before { allow(subject).to receive(:say) }
@@ -11,7 +11,7 @@ describe OpsWorks::CLI::Agent do
11
11
 
12
12
  describe 'iam:allow' do
13
13
  let(:stacks) do
14
- 2.times.map do |i|
14
+ Array.new(2) do |i|
15
15
  Fabricate(:stack).tap do |stack|
16
16
  allow(stack).to receive(:find_permission_by_user) { permissions[i] }
17
17
  end
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe OpsWorks::CLI::Agent do
4
4
  context 'recipes' do
5
5
  let(:recipe) { 'hotpockets::install' }
6
- let(:stacks) { 2.times.map { Fabricate(:stack) } }
6
+ let(:stacks) { Array.new(2) { Fabricate(:stack) } }
7
7
 
8
8
  before { allow(subject).to receive(:say) }
9
9
  before { allow(OpsWorks::Deployment).to receive(:wait) }
@@ -4,14 +4,12 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
4
  # Require library up front
5
5
  require 'opsworks/cli'
6
6
 
7
+ require 'securerandom'
7
8
  require 'fabrication'
8
- require 'omnivault'
9
9
 
10
- RSpec.configure do |config|
11
- config.before do
12
- allow(AWS::OpsWorks::Client).to receive(:new) { double.as_null_object }
13
- allow(AWS).to receive(:config)
10
+ def opsworks_stub
11
+ Aws::OpsWorks::Client.new(stub_responses: true)
12
+ end
14
13
 
15
- allow(Omnivault).to receive(:autodetect) { double.as_null_object }
16
- end
14
+ RSpec.configure do |_config|
17
15
  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.5
4
+ version: 0.5.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: 2015-10-20 00:00:00.000000000 Z
11
+ date: 2017-06-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -25,19 +25,19 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: aws-sdk-v1
28
+ name: aws-sdk
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.64'
33
+ version: 2.9.6
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.64'
40
+ version: 2.9.6
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: jsonpath
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -66,20 +66,6 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: omnivault
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
69
  - !ruby/object:Gem::Dependency
84
70
  name: bundler
85
71
  requirement: !ruby/object:Gem::Requirement
@@ -140,16 +126,16 @@ dependencies:
140
126
  name: fabrication
141
127
  requirement: !ruby/object:Gem::Requirement
142
128
  requirements:
143
- - - ">="
129
+ - - "~>"
144
130
  - !ruby/object:Gem::Version
145
- version: '0'
131
+ version: 2.16.0
146
132
  type: :development
147
133
  prerelease: false
148
134
  version_requirements: !ruby/object:Gem::Requirement
149
135
  requirements:
150
- - - ">="
136
+ - - "~>"
151
137
  - !ruby/object:Gem::Version
152
- version: '0'
138
+ version: 2.16.0
153
139
  - !ruby/object:Gem::Dependency
154
140
  name: pry
155
141
  requirement: !ruby/object:Gem::Requirement
@@ -183,12 +169,12 @@ files:
183
169
  - lib/opsworks/app.rb
184
170
  - lib/opsworks/cli.rb
185
171
  - lib/opsworks/cli/agent.rb
186
- - lib/opsworks/cli/helpers/credentials.rb
187
172
  - lib/opsworks/cli/helpers/options.rb
188
173
  - lib/opsworks/cli/helpers/typecasts.rb
189
174
  - lib/opsworks/cli/subcommands/apps.rb
190
175
  - lib/opsworks/cli/subcommands/chef.rb
191
176
  - lib/opsworks/cli/subcommands/config.rb
177
+ - lib/opsworks/cli/subcommands/deployments.rb
192
178
  - lib/opsworks/cli/subcommands/iam.rb
193
179
  - lib/opsworks/cli/subcommands/recipes.rb
194
180
  - lib/opsworks/cli/version.rb
@@ -230,7 +216,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
230
216
  version: '0'
231
217
  requirements: []
232
218
  rubyforge_project:
233
- rubygems_version: 2.4.5
219
+ rubygems_version: 2.4.5.1
234
220
  signing_key:
235
221
  specification_version: 4
236
222
  summary: Alternative CLI for Amazon OpsWorks
@@ -1,18 +0,0 @@
1
- require 'omnivault'
2
-
3
- module OpsWorks
4
- module CLI
5
- module Helpers
6
- module Credentials
7
- def fetch_credentials
8
- vault = Omnivault.autodetect
9
- vault.configure_aws!
10
- end
11
-
12
- def env_credentials?
13
- !!(ENV['AWS_ACCESS_KEY_ID'] && ENV['AWS_SECRET_ACCESS_KEY'])
14
- end
15
- end
16
- end
17
- end
18
- end