knife-cloudformation 0.1.22 → 0.2.0
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 +7 -0
- data/CHANGELOG.md +6 -0
- data/README.md +56 -2
- data/knife-cloudformation.gemspec +4 -7
- data/lib/chef/knife/cloudformation_create.rb +105 -245
- data/lib/chef/knife/cloudformation_describe.rb +50 -26
- data/lib/chef/knife/cloudformation_destroy.rb +17 -18
- data/lib/chef/knife/cloudformation_events.rb +48 -14
- data/lib/chef/knife/cloudformation_export.rb +117 -34
- data/lib/chef/knife/cloudformation_import.rb +124 -18
- data/lib/chef/knife/cloudformation_inspect.rb +159 -71
- data/lib/chef/knife/cloudformation_list.rb +20 -24
- data/lib/chef/knife/cloudformation_promote.rb +40 -0
- data/lib/chef/knife/cloudformation_update.rb +132 -15
- data/lib/chef/knife/cloudformation_validate.rb +35 -0
- data/lib/knife-cloudformation.rb +28 -0
- data/lib/knife-cloudformation/cache.rb +213 -35
- data/lib/knife-cloudformation/knife.rb +9 -0
- data/lib/knife-cloudformation/knife/base.rb +179 -0
- data/lib/knife-cloudformation/knife/stack.rb +94 -0
- data/lib/knife-cloudformation/knife/template.rb +174 -0
- data/lib/knife-cloudformation/monkey_patch.rb +8 -0
- data/lib/knife-cloudformation/monkey_patch/stack.rb +195 -0
- data/lib/knife-cloudformation/provider.rb +225 -0
- data/lib/knife-cloudformation/utils.rb +18 -98
- data/lib/knife-cloudformation/utils/animal_strings.rb +28 -0
- data/lib/knife-cloudformation/utils/debug.rb +31 -0
- data/lib/knife-cloudformation/utils/json.rb +64 -0
- data/lib/knife-cloudformation/utils/object_storage.rb +28 -0
- data/lib/knife-cloudformation/utils/output.rb +79 -0
- data/lib/knife-cloudformation/utils/path_selector.rb +99 -0
- data/lib/knife-cloudformation/utils/ssher.rb +29 -0
- data/lib/knife-cloudformation/utils/stack_exporter.rb +271 -0
- data/lib/knife-cloudformation/utils/stack_parameter_scrubber.rb +35 -0
- data/lib/knife-cloudformation/utils/stack_parameter_validator.rb +124 -0
- data/lib/knife-cloudformation/version.rb +2 -4
- metadata +47 -94
- data/Gemfile +0 -3
- data/Gemfile.lock +0 -90
- data/knife-cloudformation-0.1.20.gem +0 -0
- data/lib/knife-cloudformation/aws_commons.rb +0 -267
- data/lib/knife-cloudformation/aws_commons/stack.rb +0 -435
- data/lib/knife-cloudformation/aws_commons/stack_parameter_validator.rb +0 -79
- data/lib/knife-cloudformation/cloudformation_base.rb +0 -168
- data/lib/knife-cloudformation/export.rb +0 -174
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'knife-cloudformation'
|
3
|
+
|
4
|
+
module KnifeCloudformation
|
5
|
+
module MonkeyPatch
|
6
|
+
|
7
|
+
# Expand stack model functionality
|
8
|
+
module Stack
|
9
|
+
|
10
|
+
include KnifeCloudformation::Utils::AnimalStrings
|
11
|
+
|
12
|
+
## Status helpers
|
13
|
+
|
14
|
+
# Check for state suffix
|
15
|
+
#
|
16
|
+
# @param args [String, Symbol] state suffix to check for (multiple allowed)
|
17
|
+
# @return [TrueClass, FalseClass] true if any matches found in argument list
|
18
|
+
def status_ends_with?(*args)
|
19
|
+
stat = status.to_s.downcase
|
20
|
+
!!args.map(&:to_s).map(&:downcase).detect do |suffix|
|
21
|
+
stat.end_with?(suffix)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Check for state prefix
|
26
|
+
#
|
27
|
+
# @param args [String, Symbol] state prefix to check for (multiple allowed)
|
28
|
+
# @return [TrueClass, FalseClass] true if any matches found in argument list
|
29
|
+
def status_starts_with?(*args)
|
30
|
+
stat = status.to_s.downcase
|
31
|
+
!!args.map(&:to_s).map(&:downcase).detect do |prefix|
|
32
|
+
stat.start_with?(prefix)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Check for state inclusion
|
37
|
+
#
|
38
|
+
# @param args [String, Symbol] state string to check for (multiple allowed)
|
39
|
+
# @return [TrueClass, FalseClass] true if any matches found in argument list
|
40
|
+
def status_includes?(*args)
|
41
|
+
stat = status.to_s.downcase
|
42
|
+
!!args.map(&:to_s).map(&:downcase).detect do |string|
|
43
|
+
stat.include?(string)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [TrueClass, FalseClass] stack is in progress
|
48
|
+
def in_progress?
|
49
|
+
status_ends_with?(:in_progress)
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [TrueClass, FalseClass] stack is in complete state
|
53
|
+
def complete?
|
54
|
+
status_ends_with?(:complete, :failed)
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [TrueClass, FalseClass] stack is failed state
|
58
|
+
def failed?
|
59
|
+
status_ends_with?(:failed) ||
|
60
|
+
(status_includes?(:rollback) && status_ends_with?(:complete))
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [TrueClass, FalseClass] stack is in success state
|
64
|
+
def success?
|
65
|
+
!failed? && complete?
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [TrueClass, FalseClass] stack is creating
|
69
|
+
def creating?
|
70
|
+
in_progress? && status_starts_with?(:create)
|
71
|
+
end
|
72
|
+
|
73
|
+
# @return [TrueClass, FalseClass] stack is deleting
|
74
|
+
def deleting?
|
75
|
+
in_progress? && status_starts_with?(:delete)
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [TrueClass, FalseClass] stack is updating
|
79
|
+
def updating?
|
80
|
+
in_progress? && status_starts_with?(:update)
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [TrueClass, FalseClass] stack is rolling back
|
84
|
+
def rollbacking?
|
85
|
+
in_progress? && status_starts_with?(:rollback)
|
86
|
+
end
|
87
|
+
|
88
|
+
# @return [String] action currently being performed
|
89
|
+
def performing
|
90
|
+
if(in_progress?)
|
91
|
+
status.to_s.downcase.split('_').first.to_sym
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
### Color coders
|
96
|
+
|
97
|
+
# @return [TrueClass, FalseClass] stack is in red state
|
98
|
+
def red?
|
99
|
+
failed? || deleting?
|
100
|
+
end
|
101
|
+
|
102
|
+
# @return [TrueClass, FalseClass] stack is in green state
|
103
|
+
def green?
|
104
|
+
success?
|
105
|
+
end
|
106
|
+
|
107
|
+
# @return [TrueClass, FalseClass] stack is in yellow state
|
108
|
+
def yellow?
|
109
|
+
!red? && !green?
|
110
|
+
end
|
111
|
+
|
112
|
+
# Provides color of stack state. Red is an error state, yellow
|
113
|
+
# is a warning state and green is a success state
|
114
|
+
#
|
115
|
+
# @return [Symbol] color of state (:red, :yellow, :green)
|
116
|
+
def color_state
|
117
|
+
red? ? :red : green? ? :green : :yellow
|
118
|
+
end
|
119
|
+
|
120
|
+
# Provides text of stack state. Danger is an error state, warning
|
121
|
+
# is a warning state and success is a success state
|
122
|
+
#
|
123
|
+
# @return [Symbol] color of state (:danger, :warning, :success)
|
124
|
+
def text_state
|
125
|
+
red? ? :danger : green? ? :success : :warning
|
126
|
+
end
|
127
|
+
|
128
|
+
# @return [String] URL safe encoded stack id
|
129
|
+
def encoded_id
|
130
|
+
Base64.urlsafe_encode64(id)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Whole number representation of current completion
|
134
|
+
#
|
135
|
+
# @param min [Integer] lowest allowed return value (defaults 5)
|
136
|
+
# @return [Integer] percent complete (0..100)
|
137
|
+
def percent_complete(min = 5)
|
138
|
+
if(in_progress?)
|
139
|
+
total_resources = load_template.fetch('Resources', []).size
|
140
|
+
total_complete = resources.all.find_all do |resource|
|
141
|
+
resource.resource_status.downcase.end_with?('complete')
|
142
|
+
end.size
|
143
|
+
result = ((total_complete.to_f / total_resources) * 100).to_i
|
144
|
+
result > min.to_i ? result : min
|
145
|
+
else
|
146
|
+
100
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Apply stack outputs to current stack parameters
|
151
|
+
#
|
152
|
+
# @param remote_stack [Miasma::Orchestration::Stack]
|
153
|
+
# @return [self]
|
154
|
+
# @note setting `DisableApply` within parameter hash will
|
155
|
+
# prevent parameters being overridden
|
156
|
+
def apply_stack(remote_stack)
|
157
|
+
default_key = 'Default'
|
158
|
+
stack_parameters = template.fetch('Parameters',
|
159
|
+
template.fetch('parameters', {})
|
160
|
+
)
|
161
|
+
valid_parameters = Hash[
|
162
|
+
stack_parameters.map do |key, val|
|
163
|
+
unless(val['DisableApply'])
|
164
|
+
[snake(key), key]
|
165
|
+
end
|
166
|
+
end.compact
|
167
|
+
]
|
168
|
+
if(defined?(Chef::Config) && Chef::Config[:knife][:cloudformation][:ignore_parameters])
|
169
|
+
valid_parameters = valid_parameters.map do |snake_param, camel_param|
|
170
|
+
unless(Chef::Config[:knife][:cloudformation][:ignore_parameters].include?(camel_param))
|
171
|
+
[snake_param, camel_param]
|
172
|
+
end
|
173
|
+
end.compact
|
174
|
+
end
|
175
|
+
if(persisted?)
|
176
|
+
remote_stack.outputs.each do |output|
|
177
|
+
if(param_key = valid_parameters[snake(output.key)])
|
178
|
+
parameters.merge!(param_key => output.value)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
else
|
182
|
+
remote_stack.outputs.each do |output|
|
183
|
+
if(param_key = valid_parameters[snake(output.key)])
|
184
|
+
stack_parameters[param_key][default_key] = output.value
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Infect miasma
|
195
|
+
Miasma::Models::Orchestration::Stack.send(:include, KnifeCloudformation::MonkeyPatch::Stack)
|
@@ -0,0 +1,225 @@
|
|
1
|
+
require 'chef/mash'
|
2
|
+
require 'logger'
|
3
|
+
require 'chef/mixin/deep_merge'
|
4
|
+
require 'knife-cloudformation'
|
5
|
+
|
6
|
+
module KnifeCloudformation
|
7
|
+
# Remote provider interface
|
8
|
+
class Provider
|
9
|
+
|
10
|
+
include KnifeCloudformation::Utils::AnimalStrings
|
11
|
+
|
12
|
+
# Minimum number of seconds to wait before re-expanding in
|
13
|
+
# progress stack
|
14
|
+
STACK_EXPAND_INTERVAL = 45
|
15
|
+
|
16
|
+
# Default interval for refreshing stack list in cache
|
17
|
+
STACK_LIST_INTERVAL = 120
|
18
|
+
|
19
|
+
# @return [Miasma::Models::Orchestration]
|
20
|
+
attr_reader :connection
|
21
|
+
# @return [Cache]
|
22
|
+
attr_reader :cache
|
23
|
+
# @return [Thread, NilClass] stack list updater
|
24
|
+
attr_accessor :updater
|
25
|
+
# @return [TrueClass, FalseClass] async updates
|
26
|
+
attr_reader :async
|
27
|
+
# @return [Logger, NilClass] logger in use
|
28
|
+
attr_reader :logger
|
29
|
+
# @return [Numeric] interval between stack expansions
|
30
|
+
attr_reader :stack_expansion_interval
|
31
|
+
# @return [Numeric] interval between stack list updates
|
32
|
+
attr_reader :stack_list_interval
|
33
|
+
|
34
|
+
# Create new instance
|
35
|
+
#
|
36
|
+
# @param args [Hash]
|
37
|
+
# @option args [Hash] :miasma miasma connection hash
|
38
|
+
# @option args [Cache] :cache
|
39
|
+
# @option args [TrueClass, FalseClass] :async fetch stacks async (defaults true)
|
40
|
+
# @option args [Logger] :logger use custom logger
|
41
|
+
# @option args [Numeric] :stack_expansion_interval interval to wait between stack data expands
|
42
|
+
# @option args [Numeric] :stack_list_interval interval to wait between stack list refresh
|
43
|
+
def initialize(args={})
|
44
|
+
unless(args[:miasma][:provider])
|
45
|
+
best_guess = args[:miasma].keys.group_by do |key|
|
46
|
+
key.to_s.split('_').first
|
47
|
+
end.sort do |x, y|
|
48
|
+
y.size <=> x.size
|
49
|
+
end.first
|
50
|
+
if(best_guess)
|
51
|
+
provider = best_guess.first.to_sym
|
52
|
+
else
|
53
|
+
raise ArgumentError.new 'Cannot auto determine :provider value for credentials'
|
54
|
+
end
|
55
|
+
else
|
56
|
+
provider = args[:miasma].delete(:provider).to_sym
|
57
|
+
end
|
58
|
+
if(provider == :aws)
|
59
|
+
if(args[:miasma][:region])
|
60
|
+
args[:miasma][:aws_region] = args[:miasma].delete(:region)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
if(ENV['DEBUG'].to_s.downcase == 'true')
|
64
|
+
log_to = STDOUT
|
65
|
+
else
|
66
|
+
if(Gem.win_platform?)
|
67
|
+
log_to = 'NUL'
|
68
|
+
else
|
69
|
+
log_to = '/dev/null'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
@logger = args.fetch(:logger, Logger.new(log_to))
|
73
|
+
@stack_expansion_interval = args.fetch(:stack_expansion_interval, STACK_EXPAND_INTERVAL)
|
74
|
+
@stack_list_interval = args.fetch(:stack_list_interval, STACK_LIST_INTERVAL)
|
75
|
+
@connection = Miasma.api(
|
76
|
+
:provider => provider,
|
77
|
+
:type => :orchestration,
|
78
|
+
:credentials => args[:miasma]
|
79
|
+
)
|
80
|
+
@cache = args.fetch(:cache, Cache.new(:local))
|
81
|
+
@async = args.fetch(:async, true)
|
82
|
+
@miamsa_args = args[:miasma].dup
|
83
|
+
cache.init(:stacks_lock, :lock, :timeout => 0.1)
|
84
|
+
cache.init(:stacks, :stamped)
|
85
|
+
cache.init(:stack_expansion_lock, :lock, :timeout => 0.1)
|
86
|
+
if(args.fetch(:fetch, false))
|
87
|
+
async ? update_stack_list! : fetch_stacks
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# @return [Miasma::Orchestration::Stacks]
|
92
|
+
def stacks
|
93
|
+
connection.stacks.from_json(cached_stacks)
|
94
|
+
end
|
95
|
+
|
96
|
+
# @return [String] json representation of cached stacks
|
97
|
+
def cached_stacks
|
98
|
+
fetch_stacks unless @initial_fetch_complete
|
99
|
+
value = cache[:stacks].value
|
100
|
+
value ? MultiJson.dump(MultiJson.load(value).values) : '[]'
|
101
|
+
end
|
102
|
+
|
103
|
+
# @return [Miasma::Orchestration::Stack, NilClass]
|
104
|
+
def stack(stack_id)
|
105
|
+
stacks.get(stack_id)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Store stack attribute changes
|
109
|
+
#
|
110
|
+
# @param stack_id [String]
|
111
|
+
# @param stack_attributes [Hash]
|
112
|
+
# @return [TrueClass]
|
113
|
+
def save_expanded_stack(stack_id, stack_attributes)
|
114
|
+
current_stacks = MultiJson.load(cached_stacks)
|
115
|
+
cache.locked_action(:stacks_lock) do
|
116
|
+
logger.info "Saving expanded stack attributes in cache (#{stack_id})"
|
117
|
+
current_stacks[stack_id] = stack_attributes.merge('Cached' => Time.now.to_i)
|
118
|
+
cache[:stacks].value = MultiJson.dump(current_stacks)
|
119
|
+
end
|
120
|
+
true
|
121
|
+
end
|
122
|
+
|
123
|
+
# Remove stack from the cache
|
124
|
+
#
|
125
|
+
# @param stack_id [String]
|
126
|
+
# @return [TrueClass, FalseClass]
|
127
|
+
def remove_stack(stack_id)
|
128
|
+
current_stacks = MultiJson.load(cached_stacks)
|
129
|
+
logger.info "Attempting to remove stack from internal cache (#{stack_id})"
|
130
|
+
cache.locked_action(:stacks_lock) do
|
131
|
+
val = current_stacks.delete(stack_id)
|
132
|
+
logger.info "Successfully removed stack from internal cache (#{stack_id})"
|
133
|
+
cache[:stacks].value = MultiJson.dump(current_stacks)
|
134
|
+
!!val
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Expand all lazy loaded attributes within stack
|
139
|
+
#
|
140
|
+
# @param stack [Miasma::Models::Orchestration::Stack]
|
141
|
+
def expand_stack(stack)
|
142
|
+
logger.info "Stack expansion requested (#{stack.id})"
|
143
|
+
if((stack.in_progress? && Time.now.to_i - stack.attributes['Cached'].to_i > stack_expansion_interval) ||
|
144
|
+
!stack.attributes['Cached'])
|
145
|
+
begin
|
146
|
+
expanded = false
|
147
|
+
cache.locked_action(:stack_expansion_lock) do
|
148
|
+
expanded = true
|
149
|
+
stack.reload
|
150
|
+
stack.data['Cached'] = Time.now.to_i
|
151
|
+
end
|
152
|
+
if(expanded)
|
153
|
+
save_expanded_stack(stack.id, stack.to_json)
|
154
|
+
end
|
155
|
+
rescue => e
|
156
|
+
logger.error "Stack expansion failed (#{stack.id}) - #{e.class}: #{e}"
|
157
|
+
end
|
158
|
+
else
|
159
|
+
logger.info "Stack has been cached within expand interval. Expansion prevented. (#{stack.id})"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Request stack information and store in cache
|
164
|
+
#
|
165
|
+
# @return [TrueClass]
|
166
|
+
def fetch_stacks
|
167
|
+
cache.locked_action(:stacks_lock) do
|
168
|
+
logger.info "Lock aquired for stack update. Requesting stacks from upstream. (#{Thread.current})"
|
169
|
+
stacks = Hash[
|
170
|
+
connection.stacks.reload.all.map do |stack|
|
171
|
+
[stack.id, stack.attributes]
|
172
|
+
end
|
173
|
+
]
|
174
|
+
if(cache[:stacks].value)
|
175
|
+
existing_stacks = MultiJson.load(cache[:stacks].value)
|
176
|
+
# Force common types
|
177
|
+
stacks = MultiJson.load(MultiJson.dump(stacks))
|
178
|
+
# Remove stacks that have been deleted
|
179
|
+
stale_ids = existing_stacks.keys - stacks.keys
|
180
|
+
stacks = Chef::Mixin::DeepMerge.merge(existing_stacks, stacks)
|
181
|
+
stale_ids.each do |stale_id|
|
182
|
+
stacks.delete(stale_id)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
cache[:stacks].value = stacks.to_json
|
186
|
+
logger.info 'Stack list has been updated from upstream and cached locally'
|
187
|
+
end
|
188
|
+
@initial_fetch_complete = true
|
189
|
+
end
|
190
|
+
|
191
|
+
# Start async stack list update. Creates thread that loops every
|
192
|
+
# `self.stack_list_interval` seconds and refreshes stack list in cache
|
193
|
+
#
|
194
|
+
# @return [TrueClass, FalseClass]
|
195
|
+
def update_stack_list!
|
196
|
+
if(updater.nil? || !updater.alive?)
|
197
|
+
self.updater = Thread.new{
|
198
|
+
loop do
|
199
|
+
begin
|
200
|
+
fetch_stacks
|
201
|
+
sleep(stack_list_interval)
|
202
|
+
rescue => e
|
203
|
+
logger.error "Failure encountered on stack fetch: #{e.class} - #{e}"
|
204
|
+
end
|
205
|
+
end
|
206
|
+
}
|
207
|
+
true
|
208
|
+
else
|
209
|
+
false
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Build API connection for service type
|
214
|
+
#
|
215
|
+
# @param service [String, Symbol]
|
216
|
+
# @return [Miasma::Model]
|
217
|
+
def service_for(service)
|
218
|
+
connection.api_for(service)
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Release the monkeys!
|
225
|
+
KnifeCloudformation::MonkeyPatch::Stack
|
@@ -1,104 +1,24 @@
|
|
1
|
+
require 'knife-cloudformation'
|
2
|
+
|
1
3
|
module KnifeCloudformation
|
4
|
+
# Utility classes and modules
|
2
5
|
module Utils
|
3
6
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
module JSON
|
22
|
-
|
23
|
-
def try_json_compat
|
24
|
-
unless(@_json_loaded)
|
25
|
-
begin
|
26
|
-
require 'chef/json_compat'
|
27
|
-
rescue
|
28
|
-
require "#{ENV['RUBY_JSON_LIB'] || 'json'}"
|
29
|
-
end
|
30
|
-
@_json_loaded = true
|
31
|
-
end
|
32
|
-
defined?(Chef::JSONCompat)
|
33
|
-
end
|
34
|
-
|
35
|
-
def _to_json(thing)
|
36
|
-
if(try_json_compat)
|
37
|
-
Chef::JSONCompat.to_json(thing)
|
38
|
-
else
|
39
|
-
JSON.dump(thing)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def _from_json(thing)
|
44
|
-
if(try_json_compat)
|
45
|
-
Chef::JSONCompat.from_json(thing)
|
46
|
-
else
|
47
|
-
JSON.read(thing)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def _format_json(thing)
|
52
|
-
thing = _from_json(thing) if thing.is_a?(String)
|
53
|
-
if(try_json_compat)
|
54
|
-
Chef::JSONCompat.to_json_pretty(thing)
|
55
|
-
else
|
56
|
-
JSON.pretty_generate(thing)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
|
62
|
-
module AnimalStrings
|
63
|
-
|
64
|
-
def camel(string)
|
65
|
-
string.to_s.split('_').map{|k| "#{k.slice(0,1).upcase}#{k.slice(1,k.length)}"}.join
|
66
|
-
end
|
67
|
-
|
68
|
-
def snake(string)
|
69
|
-
string.to_s.gsub(/([a-z])([A-Z])/, '\1_\2').downcase.to_sym
|
70
|
-
end
|
71
|
-
|
72
|
-
end
|
7
|
+
autoload :Output, 'knife-cloudformation/utils/output'
|
8
|
+
autoload :StackParameterValidator, 'knife-cloudformation/utils/stack_parameter_validator'
|
9
|
+
autoload :StackParameterScrubber, 'knife-cloudformation/utils/stack_parameter_scrubber'
|
10
|
+
autoload :StackExporter, 'knife-cloudformation/utils/stack_exporter'
|
11
|
+
autoload :Debug, 'knife-cloudformation/utils/debug'
|
12
|
+
autoload :JSON, 'knife-cloudformation/utils/json'
|
13
|
+
autoload :AnimalStrings, 'knife-cloudformation/utils/animal_strings'
|
14
|
+
autoload :Ssher, 'knife-cloudformation/utils/ssher'
|
15
|
+
autoload :ObjectStorage, 'knife-cloudformation/utils/object_storage'
|
16
|
+
autoload :PathSelector, 'knife-cloudformation/utils/path_selector'
|
17
|
+
|
18
|
+
# Provide methods directly from module for previous version compatibility
|
19
|
+
extend JSON
|
20
|
+
extend AnimalStrings
|
21
|
+
extend ObjectStorage
|
73
22
|
|
74
|
-
module Ssher
|
75
|
-
def remote_file_contents(address, user, path, ssh_opts={})
|
76
|
-
require 'net/sftp'
|
77
|
-
content = ''
|
78
|
-
ssh_session = Net::SSH.start(address, user, ssh_opts)
|
79
|
-
con = Net::SFTP::Session.new(ssh_session)
|
80
|
-
con.loop{ con.opening? }
|
81
|
-
f_handle = con.open!(path)
|
82
|
-
data = ''
|
83
|
-
count = 0
|
84
|
-
while(data)
|
85
|
-
data = nil
|
86
|
-
request = con.read(f_handle, count, 1024) do |response|
|
87
|
-
unless(response.eof?)
|
88
|
-
if(response.ok?)
|
89
|
-
count += 1024
|
90
|
-
content << response[:data]
|
91
|
-
data = true
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
request.wait
|
96
|
-
end
|
97
|
-
con.close!(f_handle)
|
98
|
-
con.close_channel
|
99
|
-
ssh_session.close
|
100
|
-
content.empty? ? nil : content
|
101
|
-
end
|
102
|
-
end
|
103
23
|
end
|
104
24
|
end
|