opsworks-cli 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bcc4c1284a99afeda1c929e55f80643e5a2aad36
4
- data.tar.gz: 028437c8044bb146a5ecbfe8febbd125682cdc82
3
+ metadata.gz: b5ab9727e55ff6564f7d6f82d6cbeee4d55005f9
4
+ data.tar.gz: 02ce23e2d07cdccc433660baaf45a9ea031d741a
5
5
  SHA512:
6
- metadata.gz: 16b3ff5a4ceb89c025944ec12ad119e6303bdd763d8a3634ed9d1aca51ff259382f1e61aa062eeba70f01f82c5bec6dcfd2f819a144b1f57646ef9edc032db7f
7
- data.tar.gz: cb5f4440c3428f3773747386fdbed8ca4049a38816e895ac0d78816542299f2ea711441f9f3f4fd798065d72eede476ffbb9045859bf573914fad1cb4f80b387
6
+ metadata.gz: 97b2db684720a9bfc43df78b9ce0ea49afe4687ab40833ac23aeb0f41708807062b5de207736bdb44fe94061c2a945ff42f1d09054b40ac8b28d29650b425595
7
+ data.tar.gz: 1e2201866045a971e81bc2bc10db039576565ad5d94bd910fe852361542b61065fc1d453c2dea310c8821f2598f55d0b71dbe50d418b612036931420c8b24139
@@ -11,6 +11,7 @@ require_relative 'subcommands/status'
11
11
  require_relative 'subcommands/allow'
12
12
  require_relative 'subcommands/lockdown'
13
13
  require_relative 'subcommands/upgrade_chef'
14
+ require_relative 'subcommands/config'
14
15
 
15
16
  module OpsWorks
16
17
  module CLI
@@ -24,6 +25,7 @@ module OpsWorks
24
25
  include Subcommands::Allow
25
26
  include Subcommands::Lockdown
26
27
  include Subcommands::UpgradeChef
28
+ include Subcommands::Config
27
29
 
28
30
  desc 'version', 'Print OpsWorks CLI version'
29
31
  def version
@@ -0,0 +1,54 @@
1
+ require 'aws'
2
+ require 'opsworks/stack'
3
+
4
+ module OpsWorks
5
+ module CLI
6
+ module Subcommands
7
+ module Config
8
+ # rubocop:disable MethodLength
9
+ # rubocop:disable CyclomaticComplexity
10
+ def self.included(thor)
11
+ thor.class_eval do
12
+ include Helpers::Keychain
13
+ include Helpers::Options
14
+
15
+ desc 'config:get KEY [--stack STACK]', 'Get a single config value'
16
+ option :stack, type: :array
17
+ define_method 'config:get' do |key|
18
+ fetch_keychain_credentials unless env_credentials?
19
+ table = parse_stacks(options).map do |stack|
20
+ value = stack.custom_json_at(key)
21
+ [stack.name, value || '(null)']
22
+ end
23
+ table.compact!
24
+ table.sort! { |x, y| x.first <=> y.first }
25
+ print_table table
26
+ end
27
+
28
+ desc 'config:set KEY VALUE [--stack STACK]', 'Set a config value'
29
+ option :stack, type: :array
30
+ define_method 'config:set' do |key, value|
31
+ fetch_keychain_credentials unless env_credentials?
32
+ parse_stacks(options).each do |stack|
33
+ say "Updating #{stack.name}..."
34
+ stack.set_custom_json_at(key, value)
35
+ end
36
+ end
37
+
38
+ desc 'config:unset KEY [--stack STACK]', 'Unset a config value'
39
+ option :stack, type: :array
40
+ define_method 'config:unset' do |key|
41
+ fetch_keychain_credentials unless env_credentials?
42
+ parse_stacks(options).map do |stack|
43
+ say "Updating #{stack.name}..."
44
+ stack.set_custom_json_at(key, nil)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ # rubocop:enable CyclomaticComplexity
50
+ # rubocop:enable MethodLength
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,5 +1,5 @@
1
1
  require 'aws'
2
- require 'opsworks/deployment'
2
+ require 'opsworks/stack'
3
3
 
4
4
  module OpsWorks
5
5
  module CLI
@@ -1,5 +1,5 @@
1
1
  module OpsWorks
2
2
  module CLI
3
- VERSION = '0.2.3'
3
+ VERSION = '0.2.4'
4
4
  end
5
5
  end
@@ -1,17 +1,24 @@
1
+ require 'jsonpath'
2
+
1
3
  require 'opsworks/resource'
2
4
  require 'opsworks/app'
3
5
  require 'opsworks/instance'
4
6
  require 'opsworks/permission'
5
7
 
6
8
  module OpsWorks
9
+ # rubocop:disable ClassLength
7
10
  class Stack < Resource
8
- attr_accessor :id, :name
11
+ attr_accessor :id, :name, :custom_json
9
12
 
10
13
  AVAILABLE_CHEF_VERSIONS = %w(0.9 11.4 11.10)
11
14
 
12
15
  def self.all
13
16
  client.describe_stacks.data[:stacks].map do |hash|
14
- new(id: hash[:stack_id], name: hash[:name])
17
+ new(
18
+ id: hash[:stack_id],
19
+ name: hash[:name],
20
+ custom_json: JSON.parse(hash[:custom_json])
21
+ )
15
22
  end
16
23
  end
17
24
 
@@ -77,6 +84,19 @@ module OpsWorks
77
84
  instances.any?(&:online?)
78
85
  end
79
86
 
87
+ def custom_json_at(key)
88
+ JsonPath.new(key).first(custom_json)
89
+ end
90
+
91
+ def set_custom_json_at(key, value)
92
+ self.custom_json = replace_hash_at_path(custom_json, key, value)
93
+
94
+ self.class.client.update_stack(
95
+ stack_id: id,
96
+ custom_json: custom_json.to_json
97
+ )
98
+ end
99
+
80
100
  private
81
101
 
82
102
  def initialize_apps
@@ -85,6 +105,24 @@ module OpsWorks
85
105
  App.from_collection_response(response)
86
106
  end
87
107
 
108
+ # rubocop:disable Eval
109
+ def replace_hash_at_path(hash, key, value)
110
+ path = JsonPath.new(key).path
111
+ if value
112
+ # REVIEW: Is there a better way to parse the JSON Path and ensure
113
+ # a value at the location?
114
+ hash.default_proc = ->(h, k) { h[k] = Hash.new(&h.default_proc) }
115
+ eval("hash#{path.join('')} = #{value.inspect}")
116
+ elsif JsonPath.new(key).on(hash).count > 0
117
+ # Path value is present, but we need to unset it
118
+ leaf_key = eval(path[-1]).first
119
+ eval("hash#{path[0...-1].join('')}.delete(#{leaf_key.inspect})")
120
+ end
121
+
122
+ hash
123
+ end
124
+ # rubocop:enable Eval
125
+
88
126
  def initialize_permissions
89
127
  return [] unless id
90
128
  response = self.class.client.describe_permissions(stack_id: id)
@@ -104,4 +142,5 @@ module OpsWorks
104
142
  Deployment.from_response(response)
105
143
  end
106
144
  end
145
+ # rubocop:enable ClassLength
107
146
  end
data/opsworks-cli.gemspec CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
22
22
 
23
23
  spec.add_dependency 'thor'
24
24
  spec.add_dependency 'aws-sdk'
25
+ spec.add_dependency 'jsonpath'
25
26
 
26
27
  spec.add_development_dependency 'aws-keychain-util'
27
28
  spec.add_development_dependency 'bundler', '~> 1.5'
@@ -1,4 +1,5 @@
1
1
  Fabricator(:stack, from: OpsWorks::Stack) do
2
2
  id { SecureRandom.uuid }
3
3
  name { Fabricate.sequence(:name) { |i| "test-stack#{i}" } }
4
+ custom_json { { 'env' => { 'FOO' => 'bar' } } }
4
5
  end
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+
3
+ describe OpsWorks::CLI::Agent do
4
+ context 'config' do
5
+ let(:custom_json) { { 'env' => { 'FOO' => 'bar' } } }
6
+ let(:json_path) { 'env.FOO' }
7
+ let(:stack) { Fabricate(:stack, custom_json: custom_json) }
8
+ let(:client) { double.as_null_object }
9
+
10
+ before { allow(subject).to receive(:say) }
11
+ before { allow(OpsWorks::Stack).to receive(:all) { [stack] } }
12
+ before { allow(OpsWorks::Stack).to receive(:client) { client } }
13
+
14
+ describe 'config:get' do
15
+ it 'should print the variable from the stack custom JSON' do
16
+ expect(subject).to receive(:print_table) do |table|
17
+ expect(table).to eq [[stack.name, 'bar']]
18
+ end
19
+ subject.send('config:get', json_path)
20
+ end
21
+
22
+ it 'should print (null) if the variable is unset' do
23
+ stack.custom_json = {}
24
+ expect(subject).to receive(:print_table) do |table|
25
+ expect(table).to eq [[stack.name, '(null)']]
26
+ end
27
+ subject.send('config:get', json_path)
28
+ end
29
+ end
30
+
31
+ describe 'config:set' do
32
+ it 'should reset the variable, if it is already set' do
33
+ expect(client).to receive(:update_stack) do |hash|
34
+ json = JSON.parse(hash[:custom_json])
35
+ expect(json['env']['FOO']).to eq 'baz'
36
+ end
37
+ subject.send('config:set', json_path, 'baz')
38
+ expect(stack.custom_json['env']['FOO']).to eq 'baz'
39
+ end
40
+
41
+ it 'should set the variable, if it is unset' do
42
+ stack.custom_json = {}
43
+ expect(client).to receive(:update_stack) do |hash|
44
+ json = JSON.parse(hash[:custom_json])
45
+ expect(json['env']['FOO']).to eq 'baz'
46
+ end
47
+ subject.send('config:set', json_path, 'baz')
48
+ expect(stack.custom_json['env']['FOO']).to eq 'baz'
49
+ end
50
+
51
+ it 'should leave other variables alone' do
52
+ stack.custom_json.merge!('other' => 'something')
53
+ expect(client).to receive(:update_stack) do |hash|
54
+ json = JSON.parse(hash[:custom_json])
55
+ expect(json['env']['FOO']).to eq 'baz'
56
+ expect(json['other']).to eq 'something'
57
+ end
58
+ subject.send('config:set', json_path, 'baz')
59
+ expect(stack.custom_json['env']['FOO']).to eq 'baz'
60
+ expect(stack.custom_json['other']).to eq 'something'
61
+ end
62
+ end
63
+
64
+ describe 'config:unset' do
65
+ it 'should unset the variable' do
66
+ expect(client).to receive(:update_stack) do |hash|
67
+ json = JSON.parse(hash[:custom_json])
68
+ expect(json['env'].keys).not_to include('FOO')
69
+ end
70
+ subject.send('config:unset', json_path)
71
+ expect(stack.custom_json['env'].keys).not_to include('FOO')
72
+ end
73
+
74
+ it 'should leave other variables alone' do
75
+ stack.custom_json['env'].merge!('OTHER' => 'something')
76
+ expect(client).to receive(:update_stack) do |hash|
77
+ json = JSON.parse(hash[:custom_json])
78
+ expect(json['env'].keys).not_to include('FOO')
79
+ end
80
+ subject.send('config:unset', json_path)
81
+ expect(stack.custom_json['env']).to eq('OTHER' => 'something')
82
+ end
83
+
84
+ it 'should work even with nil values' do
85
+ stack.custom_json['env'] = { 'FOO' => nil }
86
+ expect(client).to receive(:update_stack) do |hash|
87
+ json = JSON.parse(hash[:custom_json])
88
+ expect(json['env'].keys).not_to include('FOO')
89
+ end
90
+ subject.send('config:unset', json_path)
91
+ expect(stack.custom_json['env'].keys).not_to include('FOO')
92
+ end
93
+ end
94
+ end
95
+ 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.2.3
4
+ version: 0.2.4
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-17 00:00:00.000000000 Z
11
+ date: 2014-11-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: jsonpath
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: aws-keychain-util
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -158,6 +172,7 @@ files:
158
172
  - lib/opsworks/cli/helpers/keychain.rb
159
173
  - lib/opsworks/cli/helpers/options.rb
160
174
  - lib/opsworks/cli/subcommands/allow.rb
175
+ - lib/opsworks/cli/subcommands/config.rb
161
176
  - lib/opsworks/cli/subcommands/deploy.rb
162
177
  - lib/opsworks/cli/subcommands/exec.rb
163
178
  - lib/opsworks/cli/subcommands/lockdown.rb
@@ -177,6 +192,7 @@ files:
177
192
  - spec/fabricators/opsworks/stack_fabricator.rb
178
193
  - spec/opsworks/cli/agent_spec.rb
179
194
  - spec/opsworks/cli/subcommands/allow_spec.rb
195
+ - spec/opsworks/cli/subcommands/config_spec.rb
180
196
  - spec/opsworks/cli/subcommands/deploy_spec.rb
181
197
  - spec/opsworks/cli/subcommands/exec_spec.rb
182
198
  - spec/opsworks/cli/subcommands/lockdown_spec.rb
@@ -213,6 +229,7 @@ test_files:
213
229
  - spec/fabricators/opsworks/stack_fabricator.rb
214
230
  - spec/opsworks/cli/agent_spec.rb
215
231
  - spec/opsworks/cli/subcommands/allow_spec.rb
232
+ - spec/opsworks/cli/subcommands/config_spec.rb
216
233
  - spec/opsworks/cli/subcommands/deploy_spec.rb
217
234
  - spec/opsworks/cli/subcommands/exec_spec.rb
218
235
  - spec/opsworks/cli/subcommands/lockdown_spec.rb