kontena-cli 1.4.0.pre8 → 1.4.0.pre9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/kontena/cli/certificate/list_command.rb +1 -1
- data/lib/kontena/cli/certificate/remove_command.rb +23 -0
- data/lib/kontena/cli/certificate_command.rb +1 -0
- data/lib/kontena/cli/stacks/change_resolver.rb +96 -0
- data/lib/kontena/cli/stacks/install_command.rb +1 -1
- data/lib/kontena/cli/stacks/list_command.rb +1 -0
- data/lib/kontena/cli/stacks/upgrade_command.rb +165 -98
- data/lib/kontena/cli/stacks/validate_command.rb +5 -3
- data/lib/kontena/cli/stacks/yaml/reader.rb +2 -2
- data/lib/kontena/cli/stacks/yaml/stack_file_loader.rb +59 -4
- data/spec/kontena/cli/stacks/upgrade_command_spec.rb +11 -11
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ee7784ef8718b8afec8e31001cb8386de5ae3f9
|
4
|
+
data.tar.gz: d5dca2cd45bcd062bce2935e81cfdf33bca6572e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35c6016071aa01ef2a0a85a2b7e088f76fb24d74bd16bd7ea9db99da9c6ffe78f5f9bb7fee7d7395e3080534b2b9f30e1b87b0bfdc112d3633bc38423e8cc11b
|
7
|
+
data.tar.gz: 25e2c16f93d615d012157460d3edf46d01f7fa73a90a67daea6ad028ebb16556a37262d2233d95d9036ef1721a17136a8df1ab70ebb55c514d6467eed9fbe779
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.4.0.
|
1
|
+
1.4.0.pre9
|
@@ -15,7 +15,7 @@ module Kontena::Cli::Certificate
|
|
15
15
|
THREE_DAYS = 3 * 24 * 60 * 60
|
16
16
|
|
17
17
|
def fields
|
18
|
-
quiet? ? ['subject'] : {subject: 'subject', "expiration" => 'expires_in'}
|
18
|
+
quiet? ? ['subject'] : {subject: 'subject', "expiration" => 'expires_in', auto_renewable?: 'auto_renewable'}
|
19
19
|
end
|
20
20
|
|
21
21
|
def certificates
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative '../services/services_helper'
|
2
|
+
|
3
|
+
module Kontena::Cli::Certificate
|
4
|
+
class RemoveCommand < Kontena::Command
|
5
|
+
include Kontena::Cli::Common
|
6
|
+
include Kontena::Cli::GridOptions
|
7
|
+
|
8
|
+
parameter "SUBJECT", "Certificate subject"
|
9
|
+
option "--force", :flag, "Force remove", default: false, attribute_name: :forced
|
10
|
+
|
11
|
+
requires_current_master
|
12
|
+
requires_current_master_token
|
13
|
+
requires_current_grid
|
14
|
+
|
15
|
+
def execute
|
16
|
+
confirm_command(self.subject) unless forced?
|
17
|
+
|
18
|
+
spinner "Removing certificate for #{self.subject.colorize(:cyan)} from #{current_grid.colorize(:cyan)} grid " do
|
19
|
+
client.delete("certificates/#{current_grid}/#{self.subject}")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -7,6 +7,7 @@ class Kontena::Cli::CertificateCommand < Kontena::Command
|
|
7
7
|
subcommand "authorize", "Create DNS authorization for domain", load_subcommand('certificate/authorize_command')
|
8
8
|
subcommand "request", "Request certificate for domain", load_subcommand('certificate/request_command')
|
9
9
|
subcommand "get", "Get certificate for domain", load_subcommand('certificate/get_command')
|
10
|
+
subcommand ["remove", "rm"], "Remove certificate for domain", load_subcommand('certificate/remove_command')
|
10
11
|
|
11
12
|
|
12
13
|
def execute
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require_relative 'yaml/stack_file_loader'
|
2
|
+
|
3
|
+
module Kontena::Cli::Stacks
|
4
|
+
class ChangeResolver
|
5
|
+
|
6
|
+
attr_reader :old_data, :new_data
|
7
|
+
|
8
|
+
# Creates a change analysis from two sets of stack data.
|
9
|
+
# The format is a flat hash of all related stacks.
|
10
|
+
#
|
11
|
+
# @param old_data [Hash]
|
12
|
+
# @param new_data [Hash]
|
13
|
+
def initialize(old_data, new_data)
|
14
|
+
@old_data = old_data
|
15
|
+
@new_data = new_data
|
16
|
+
analyze
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Array<String>] an array of services that should be added
|
20
|
+
def added_services
|
21
|
+
@added_services ||= []
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Array<String>] an array of services that should be removed
|
25
|
+
def removed_services
|
26
|
+
@removed_services ||= []
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Array<String>] an array of services that should be upgraded
|
30
|
+
def upgraded_services
|
31
|
+
@upgraded_services ||= []
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Array<String>] an array of stack installation names that should be removed
|
35
|
+
def removed_stacks
|
36
|
+
@removed_stacks ||= []
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Array<String>] an array of stack installation names that should be removed
|
40
|
+
def added_stacks
|
41
|
+
@added_stacks ||= []
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [Array<String>] an array of stack installation names that should be upgraded
|
45
|
+
def upgraded_stacks
|
46
|
+
@upgraded_stacks ||= []
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [Hash] a hash of "installed-stack-name" => { :from => 'stackname', :to => 'new-stackname' }
|
50
|
+
def replaced_stacks
|
51
|
+
@replaced_stacks ||= {}
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Array<String>] an array of installed stack names that should exist after upgrade
|
55
|
+
def remaining_stacks
|
56
|
+
@remaining_stacks ||= added_stacks + upgraded_stacks
|
57
|
+
end
|
58
|
+
|
59
|
+
def analyze
|
60
|
+
old_names = old_data.keys
|
61
|
+
new_names = new_data.keys
|
62
|
+
|
63
|
+
removed_stacks.concat(old_names - new_names)
|
64
|
+
added_stacks.concat(new_names - old_names)
|
65
|
+
upgraded_stacks.concat(new_names & old_names)
|
66
|
+
|
67
|
+
removed_stacks.each do |removed_stack|
|
68
|
+
removed_services.concat(
|
69
|
+
old_data[removed_stack][:stack_data]['services'].map { |svc| "#{removed_stack}/#{svc['name']}"}
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
added_stacks.each do |added_stack|
|
74
|
+
added_services.concat(
|
75
|
+
new_data[added_stack][:stack_data]['services'].map { |svc| "#{added_stack}/#{svc['name']}"}
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
upgraded_stacks.each do |upgraded_stack|
|
80
|
+
old_stack = old_data[upgraded_stack][:stack_data]['stack']
|
81
|
+
new_stack = new_data[upgraded_stack][:stack_data]['stack']
|
82
|
+
|
83
|
+
unless old_stack == new_stack
|
84
|
+
replaced_stacks[upgraded_stack] = { from: old_stack, to: new_stack }
|
85
|
+
end
|
86
|
+
|
87
|
+
old_services = old_data[upgraded_stack][:stack_data]['services'].map { |svc| "#{upgraded_stack}/#{svc['name']}" }
|
88
|
+
new_services = new_data[upgraded_stack][:stack_data]['services'].map { |svc| "#{upgraded_stack}/#{svc['name']}" }
|
89
|
+
|
90
|
+
removed_services.concat(old_services - new_services)
|
91
|
+
added_services.concat(new_services - old_services)
|
92
|
+
upgraded_services.concat(new_services & old_services)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -44,7 +44,7 @@ module Kontena::Cli::Stacks
|
|
44
44
|
return if dependencies.nil?
|
45
45
|
dependencies.each do |dependency|
|
46
46
|
target_name = "#{stack_name}-#{dependency['name']}"
|
47
|
-
caret "Installing dependency #{pastel.cyan(dependency[
|
47
|
+
caret "Installing dependency #{pastel.cyan(dependency['stack'])} as #{pastel.cyan(target_name)}"
|
48
48
|
cmd = ['stack', 'install', '-n', target_name, '--parent-name', stack_name]
|
49
49
|
|
50
50
|
dependency['variables'].merge(dependency_values_from_options(dependency['name'])).each do |key, value|
|
@@ -1,4 +1,7 @@
|
|
1
1
|
require_relative 'common'
|
2
|
+
require_relative 'change_resolver'
|
3
|
+
|
4
|
+
require 'json'
|
2
5
|
|
3
6
|
module Kontena::Cli::Stacks
|
4
7
|
class UpgradeCommand < Kontena::Command
|
@@ -19,143 +22,207 @@ module Kontena::Cli::Stacks
|
|
19
22
|
|
20
23
|
option '--force', :flag, 'Force upgrade'
|
21
24
|
option '--skip-dependencies', :flag, "Do not install any stack dependencies"
|
22
|
-
option '--dry-run', :flag, "
|
25
|
+
option '--dry-run', :flag, "Simulate upgrade"
|
23
26
|
|
24
27
|
requires_current_master
|
25
28
|
requires_current_master_token
|
26
29
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
depends = stack_data.delete('depends') || []
|
31
|
-
normalized_data = {
|
32
|
-
parent_name => stack_data.merge(
|
33
|
-
:loader => loader_class.for(stack_data['stack'])
|
34
|
-
)
|
35
|
-
}
|
30
|
+
# @return [Kontena::Cli::Stacks::ChangeResolver]
|
31
|
+
def execute
|
32
|
+
set_env_variables(stack_name, current_grid)
|
36
33
|
|
37
|
-
|
38
|
-
|
39
|
-
normalized_data.merge!(normalize_local_data(stack.merge('parent_name' => parent_name), key))
|
34
|
+
old_data = spinner "Reading stack #{pastel.cyan(stack_name)} from master" do
|
35
|
+
gather_master_data(stack_name)
|
40
36
|
end
|
41
|
-
normalized_data
|
42
|
-
end
|
43
37
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
raise ex
|
38
|
+
new_data = spinner "Parsing #{pastel.cyan(source)}" do
|
39
|
+
loader.flat_dependencies(
|
40
|
+
stack_name,
|
41
|
+
variables: values_from_options
|
42
|
+
)
|
50
43
|
end
|
51
|
-
depends = data.delete('children') || []
|
52
44
|
|
53
|
-
|
45
|
+
changes = process_data(old_data, new_data)
|
46
|
+
|
47
|
+
display_report(changes)
|
48
|
+
|
49
|
+
return if dry_run?
|
50
|
+
|
51
|
+
get_confirmation(changes)
|
52
|
+
|
53
|
+
deployable_stacks = []
|
54
|
+
deployable_stacks.concat run_installs(changes)
|
55
|
+
deployable_stacks.concat run_upgrades(changes)
|
56
|
+
|
57
|
+
run_deploys(deployable_stacks) if deploy?
|
58
|
+
|
59
|
+
run_removes(changes.removed_stacks)
|
60
|
+
|
61
|
+
changes
|
62
|
+
end
|
54
63
|
|
55
|
-
|
64
|
+
private
|
56
65
|
|
57
|
-
|
58
|
-
|
66
|
+
# Recursively fetch master data in StackFileLoader#flat_dependencies format
|
67
|
+
# @return [Hash{string => Hash}] stackname => hash
|
68
|
+
def gather_master_data(stackname)
|
69
|
+
response = fetch_master_data(stackname)
|
70
|
+
children = response.delete('children') || []
|
71
|
+
result = { stackname => { stack_data: response } }
|
72
|
+
children.each do |child|
|
73
|
+
result.merge!(gather_master_data(child['name']))
|
59
74
|
end
|
60
|
-
|
75
|
+
result
|
61
76
|
end
|
62
77
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
78
|
+
# Preprocess data and return a ChangeResolver
|
79
|
+
# @param old_data [Hash] data from master
|
80
|
+
# @param new_data [Hash] data from files
|
81
|
+
# @return [Kontena::Cli::Stacks::ChangeRsolver]
|
82
|
+
def process_data(old_data, new_data)
|
83
|
+
logger.debug { "Master stacks: #{old_data.keys.join(",")} YAML stacks: #{new_data.keys.join(",")}" }
|
84
|
+
|
85
|
+
new_data.reverse_each do |stackname, data|
|
86
|
+
reader = data[:loader].reader
|
87
|
+
set_env_variables(stackname, current_grid) # set envs for execution time
|
88
|
+
data[:stack_data] = reader.execute(
|
89
|
+
values: data[:variables],
|
90
|
+
defaults: old_data[stackname].nil? ? nil : old_data[stackname][:stack_data]['variables'],
|
91
|
+
parent_name: data[:parent_name],
|
92
|
+
name: data[:name]
|
93
|
+
)
|
94
|
+
hint_on_validation_notifications(reader.notifications, reader.loader.source)
|
95
|
+
abort_on_validation_errors(reader.errors, reader.loader.source)
|
70
96
|
end
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
97
|
+
|
98
|
+
set_env_variables(stack_name, current_grid) # restore envs
|
99
|
+
|
100
|
+
spinner "Analyzing upgrade" do
|
101
|
+
Kontena::Cli::Stacks::ChangeResolver.new(old_data, new_data)
|
76
102
|
end
|
77
|
-
merged
|
78
103
|
end
|
79
104
|
|
80
|
-
def
|
81
|
-
|
105
|
+
def display_report(changes)
|
106
|
+
if !dry_run? && changes.removed_stacks.empty? && changes.replaced_stacks.empty? && changes.upgraded_stacks.size == 1 && changes.removed_services.empty?
|
107
|
+
return
|
108
|
+
end
|
109
|
+
|
110
|
+
will = dry_run? ? "would" : "will"
|
111
|
+
|
112
|
+
puts "SERVICES:"
|
113
|
+
puts "-" * 40
|
82
114
|
|
83
|
-
|
84
|
-
|
115
|
+
unless changes.removed_services.empty?
|
116
|
+
puts pastel.yellow("These services #{will} be removed from master:")
|
117
|
+
changes.removed_services.each { |svc| puts pastel.yellow(" - #{svc}") }
|
118
|
+
puts
|
85
119
|
end
|
86
120
|
|
87
|
-
|
88
|
-
|
121
|
+
unless changes.added_services.empty?
|
122
|
+
puts pastel.green("These new services #{will} be created to master:")
|
123
|
+
changes.added_services.each { |svc| puts pastel.green(" - #{svc}") }
|
124
|
+
puts
|
89
125
|
end
|
90
126
|
|
91
|
-
|
127
|
+
unless changes.upgraded_services.empty?
|
128
|
+
puts pastel.cyan("These services #{will} be upgraded:")
|
129
|
+
changes.upgraded_services.each do |svc|
|
130
|
+
puts pastel.cyan("- #{svc}")
|
131
|
+
end
|
132
|
+
puts
|
133
|
+
end
|
92
134
|
|
93
|
-
|
135
|
+
puts "STACKS:"
|
136
|
+
puts "-" * 40
|
94
137
|
|
95
|
-
unless
|
138
|
+
unless changes.removed_stacks.empty?
|
139
|
+
puts pastel.red("These stacks #{will} be removed because they are no longer depended on:")
|
140
|
+
changes.removed_stacks.each { |stack| puts pastel.red("- #{stack}") }
|
96
141
|
puts
|
97
|
-
|
98
|
-
|
99
|
-
|
142
|
+
end
|
143
|
+
|
144
|
+
unless changes.replaced_stacks.empty?
|
145
|
+
puts pastel.yellow("These stacks #{will} be replaced by other stacks:")
|
146
|
+
changes.replaced_stacks.each do |installed_name, data|
|
147
|
+
puts "- #{pastel.yellow(installed_name)} from #{pastel.cyan(data[:from])} to #{pastel.cyan(data[:to])}"
|
100
148
|
end
|
101
149
|
puts
|
102
|
-
|
150
|
+
end
|
151
|
+
|
152
|
+
unless changes.added_stacks.empty?
|
153
|
+
puts pastel.red("These new stack dependencies #{will} be installed:")
|
154
|
+
changes.added_stacks.each { |stack| puts pastel.red("- #{stack}") }
|
155
|
+
puts
|
156
|
+
end
|
157
|
+
|
158
|
+
unless changes.upgraded_stacks.empty?
|
159
|
+
puts pastel.cyan("These stacks #{will} be upgraded#{' and deployed' if deploy?}:")
|
160
|
+
changes.upgraded_stacks.each { |stack| puts pastel.cyan("- #{stack}") }
|
161
|
+
puts
|
162
|
+
end
|
163
|
+
|
164
|
+
puts
|
165
|
+
end
|
166
|
+
|
167
|
+
# requires heavier confirmation when something very dangerous is going to happen
|
168
|
+
def get_confirmation(changes)
|
169
|
+
unless force?
|
170
|
+
unless changes.removed_services.empty? && changes.removed_stacks.empty? && changes.replaced_stacks.empty?
|
103
171
|
puts "#{pastel.red('Warning:')} This can not be undone, data will be lost."
|
104
|
-
|
105
|
-
confirm unless force?
|
106
|
-
removes.reverse_each do |removed_stack|
|
107
|
-
Kontena.run!('stack', 'remove', '--force', '--keep-dependencies', removed_stack)
|
108
|
-
merged.delete(removed_stack)
|
172
|
+
confirm
|
109
173
|
end
|
110
174
|
end
|
175
|
+
end
|
111
176
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
177
|
+
def deployable_stacks
|
178
|
+
@deployable_stacks ||= []
|
179
|
+
end
|
180
|
+
|
181
|
+
def run_removes(removed_stacks)
|
182
|
+
removed_stacks.reverse_each do |removed_stack|
|
183
|
+
Kontena.run!('stack', 'remove', '--force', '--keep-dependencies', removed_stack)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# @return [Array] an array of stack names that have been installed, but not yet deployed
|
188
|
+
def run_installs(changes)
|
189
|
+
deployable_stacks = []
|
190
|
+
changes.added_stacks.reverse_each do |added_stack|
|
191
|
+
data = changes.new_data[added_stack]
|
192
|
+
cmd = ['stack', 'install', '--name', added_stack, '--no-deploy']
|
193
|
+
cmd.concat ['--parent-name', data[:parent_name]] if data[:parent_name]
|
194
|
+
data[:variables].each do |k,v|
|
195
|
+
cmd.concat ['-v', "#{k}=#{v}"]
|
118
196
|
end
|
197
|
+
cmd << data[:loader].source
|
198
|
+
caret "Installing new dependency #{cmd.last} as #{added_stack}"
|
199
|
+
deployable_stacks << added_stack
|
200
|
+
Kontena.run!(cmd)
|
119
201
|
end
|
202
|
+
deployable_stacks
|
203
|
+
end
|
120
204
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
hint_on_validation_notifications(data[:local][:loader].reader.notifications, data[:local][:loader].source)
|
130
|
-
abort_on_validation_errors(data[:local][:loader].reader.errors, data[:local][:loader].source)
|
131
|
-
end
|
132
|
-
|
133
|
-
merged.reverse_each do |stackname, data|
|
134
|
-
stack = data[:local][:stack]
|
135
|
-
if data[:remote]
|
136
|
-
spinner "Upgrading #{stack_name == stackname ? 'stack' : 'dependency'} #{pastel.cyan(stackname)}" do |spin|
|
137
|
-
update_stack(stackname, stack) || spin.fail!
|
138
|
-
end
|
139
|
-
else
|
140
|
-
cmd = ['stack', 'install', '--name', stackname]
|
141
|
-
cmd.concat ['--parent-name', stack['parent_name']] if stack['parent_name']
|
142
|
-
|
143
|
-
stack['variables'].merge(dependency_values_from_options(stackname)).each do |k, v|
|
144
|
-
cmd.concat ['-v', "#{k}=#{v}"]
|
145
|
-
end
|
146
|
-
|
147
|
-
cmd << '--no-deploy'
|
148
|
-
cmd << data[:local][:loader].source
|
149
|
-
caret "Installing new dependency #{cmd.last} as #{stackname}"
|
150
|
-
Kontena.run!(cmd)
|
205
|
+
# @return [Array] an array of stack names that have been upgraded, but not yet deployed
|
206
|
+
def run_upgrades(changes)
|
207
|
+
deployable_stacks = []
|
208
|
+
changes.upgraded_stacks.reverse_each do |upgraded_stack|
|
209
|
+
data = changes.new_data[upgraded_stack]
|
210
|
+
spinner "Upgrading #{stack_name == upgraded_stack ? 'stack' : 'dependency'} #{pastel.cyan(upgraded_stack)}" do |spin|
|
211
|
+
deployable_stacks << upgraded_stack
|
212
|
+
update_stack(upgraded_stack, data[:stack_data]) || spin.fail!
|
151
213
|
end
|
214
|
+
end
|
215
|
+
deployable_stacks
|
216
|
+
end
|
152
217
|
|
153
|
-
|
218
|
+
# @param deployable_stacks [Array<String>] an array of stack names that should be deployed
|
219
|
+
def run_deploys(deployable_stacks)
|
220
|
+
deployable_stacks.each do |deployable_stack|
|
221
|
+
Kontena.run!(['stack', 'deploy', deployable_stack])
|
154
222
|
end
|
155
223
|
end
|
156
224
|
|
157
225
|
def update_stack(name, data)
|
158
|
-
return true if dry_run?
|
159
226
|
client.put(stack_url(name), data)
|
160
227
|
end
|
161
228
|
|
@@ -163,8 +230,8 @@ module Kontena::Cli::Stacks
|
|
163
230
|
"stacks/#{current_grid}/#{name}"
|
164
231
|
end
|
165
232
|
|
166
|
-
def fetch_master_data(
|
167
|
-
client.get(stack_url(
|
233
|
+
def fetch_master_data(stackname)
|
234
|
+
client.get(stack_url(stackname))
|
168
235
|
end
|
169
236
|
end
|
170
237
|
end
|
@@ -30,7 +30,6 @@ module Kontena::Cli::Stacks
|
|
30
30
|
cmd.concat ['--parent-name', stack_name]
|
31
31
|
|
32
32
|
dependency['variables'].merge(dependency_values_from_options(dependency['name'])).each do |key, value|
|
33
|
-
next if key == 'PARENT_STACK'
|
34
33
|
cmd.concat ['-v', "#{key}=#{value}"]
|
35
34
|
end
|
36
35
|
cmd << dependency['stack']
|
@@ -58,8 +57,11 @@ module Kontena::Cli::Stacks
|
|
58
57
|
|
59
58
|
dump_variables if values_to
|
60
59
|
|
61
|
-
result =
|
62
|
-
|
60
|
+
result = stack.reject { |k, _| k == 'source' }
|
61
|
+
result.merge!(
|
62
|
+
'variables' => Kontena::Util.stringify_keys(
|
63
|
+
reader.variable_values(without_defaults: true, without_vault: true, with_errors: true)
|
64
|
+
)
|
63
65
|
)
|
64
66
|
if dependencies?
|
65
67
|
puts ::YAML.dump(result).sub(/\A---$/, "---\n# #{loader.source}")
|
@@ -49,8 +49,8 @@ module Kontena::Cli::Stacks
|
|
49
49
|
# @param without_defaults [TrueClass,FalseClass] strip the GRID, STACK, etc from response
|
50
50
|
# @param without_vault [TrueClass,FalseClass] strip out any values that are going to or coming from VAULT
|
51
51
|
# @return [Hash] a hash of key value pairs representing the values of stack variables
|
52
|
-
def variable_values(without_defaults: false, without_vault: false)
|
53
|
-
result = variables.to_h(values_only: true)
|
52
|
+
def variable_values(without_defaults: false, without_vault: false, with_errors: false)
|
53
|
+
result = variables.to_h(values_only: true, with_errors: with_errors)
|
54
54
|
if without_defaults
|
55
55
|
result.delete_if { |k, _| default_envs.key?(k.to_s) || k.to_s == 'PARENT_STACK' }
|
56
56
|
end
|
@@ -43,6 +43,11 @@ module Kontena::Cli::Stacks
|
|
43
43
|
@parent = parent
|
44
44
|
end
|
45
45
|
|
46
|
+
# @return [String] a stripped down version of inspect without all the yaml source
|
47
|
+
def inspect
|
48
|
+
"#<#{self.class.name}:#{object_id} @source=#{source.inspect} @parent=#{parent.nil? ? 'nil' : parent.source}>"
|
49
|
+
end
|
50
|
+
|
46
51
|
# @return [Hash] a hash parsed from the YAML content
|
47
52
|
def yaml
|
48
53
|
@yaml ||= ::YAML.safe_load(content, [], [], true, source)
|
@@ -75,21 +80,71 @@ module Kontena::Cli::Stacks
|
|
75
80
|
# @return [Array<Hash>] an array of hashes ('name', 'stack', 'variables', and 'depends')
|
76
81
|
def dependencies(recurse: true)
|
77
82
|
return @dependencies if @dependencies
|
78
|
-
depends = yaml['depends']
|
79
83
|
if depends.nil? || depends.empty?
|
80
84
|
@dependencies = nil
|
81
85
|
else
|
82
86
|
@dependencies = depends.map do |name, dependency|
|
83
|
-
|
84
|
-
deps = { 'name' => name, 'stack' =>
|
87
|
+
loader = StackFileLoader.for(dependency['stack'], self)
|
88
|
+
deps = { 'name' => name, 'stack' => loader.source, 'variables' => dependency.fetch('variables', Hash.new) }
|
85
89
|
if recurse
|
86
|
-
child_deps =
|
90
|
+
child_deps = loader.dependencies
|
87
91
|
deps['depends'] = child_deps unless child_deps.nil?
|
88
92
|
end
|
89
93
|
deps
|
90
94
|
end
|
91
95
|
end
|
92
96
|
end
|
97
|
+
|
98
|
+
def to_h
|
99
|
+
{
|
100
|
+
'stack' => stack_name.stack_name,
|
101
|
+
:loader => self,
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns a non nested hash of all dependencies.
|
106
|
+
# Processes :variables hash and moves the related variables to children
|
107
|
+
#
|
108
|
+
# @param basename [String] installed stack name
|
109
|
+
# @param opts [Hash] extra data such as variable lists
|
110
|
+
# @return [Hash] { installation_name => { 'name' => installation-name, 'stack' => stack_name, :loader => self }, child_install_name => { ... } }
|
111
|
+
def flat_dependencies(basename, opts = {})
|
112
|
+
opt_variables = opts[:variables] || {}
|
113
|
+
|
114
|
+
result = {
|
115
|
+
basename => self.to_h.merge(opts).merge(
|
116
|
+
name: basename,
|
117
|
+
variables: opt_variables.reject { |k, _| k.include?('.') }
|
118
|
+
)
|
119
|
+
}
|
120
|
+
|
121
|
+
depends.each do |as_name, data|
|
122
|
+
variables = {}
|
123
|
+
|
124
|
+
opt_variables.select { |k, _| k.start_with?(as_name + '.') }.each do |k,v|
|
125
|
+
variables[k.split('.', 2).last] = v
|
126
|
+
end
|
127
|
+
|
128
|
+
data['variables'] ||= {}
|
129
|
+
|
130
|
+
loader = StackFileLoader.for(data['stack'], self)
|
131
|
+
result.merge!(
|
132
|
+
loader.flat_dependencies(
|
133
|
+
basename + '-' + as_name,
|
134
|
+
variables: data['variables'].merge(variables),
|
135
|
+
parent_name: basename
|
136
|
+
)
|
137
|
+
)
|
138
|
+
end
|
139
|
+
|
140
|
+
result
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
def depends
|
146
|
+
yaml['depends'] || {}
|
147
|
+
end
|
93
148
|
end
|
94
149
|
end
|
95
150
|
end
|
@@ -51,13 +51,13 @@ describe Kontena::Cli::Stacks::UpgradeCommand do
|
|
51
51
|
it 'sends stack to master' do
|
52
52
|
expect(client).to receive(:get).with('stacks/test-grid/stack-a').and_return(stack_response)
|
53
53
|
expect(client).to receive(:put).with('stacks/test-grid/stack-a', hash_including(stack_expectation.merge('name' => 'stack-a'))).and_return(true)
|
54
|
-
subject.run(['--no-deploy', 'stack-a', fixture_path('kontena_v3.yml')])
|
54
|
+
subject.run(['--no-deploy', '--force', 'stack-a', fixture_path('kontena_v3.yml')])
|
55
55
|
end
|
56
56
|
|
57
57
|
it 'requires confirmation when master stack is different than input stack' do
|
58
58
|
expect(client).to receive(:get).with('stacks/test-grid/stack-b').and_return(stack_response.merge('stack' => 'foo/otherstack'))
|
59
|
-
expect(subject).to receive(:confirm).
|
60
|
-
expect{subject.run(['stack-b',
|
59
|
+
expect(subject).to receive(:confirm).and_call_original
|
60
|
+
expect{subject.run(['stack-b', fixture_path('kontena_v3.yml')])}.to exit_with_error.and output(/- stack-b from foo\/otherstack to user\/stackname/).to_stdout
|
61
61
|
end
|
62
62
|
|
63
63
|
it 'triggers deploy by default' do
|
@@ -66,7 +66,7 @@ describe Kontena::Cli::Stacks::UpgradeCommand do
|
|
66
66
|
'stacks/test-grid/stack-a', anything
|
67
67
|
).and_return({})
|
68
68
|
expect(Kontena).to receive(:run!).with(['stack', 'deploy', 'stack-a']).once
|
69
|
-
subject.run(['stack-a', fixture_path('kontena_v3.yml')])
|
69
|
+
subject.run(['--force', 'stack-a', fixture_path('kontena_v3.yml')])
|
70
70
|
end
|
71
71
|
|
72
72
|
context '--no-deploy option' do
|
@@ -83,13 +83,13 @@ describe Kontena::Cli::Stacks::UpgradeCommand do
|
|
83
83
|
context 'with a stack including dependencies' do
|
84
84
|
|
85
85
|
let(:expectation) {{ 'name' => 'deptest', 'stack' => 'user/depstack1' }}
|
86
|
-
let(:expectation_1) {{ 'name' => 'deptest-dep_1', 'stack' => 'user/depstack1child1'
|
86
|
+
let(:expectation_1) {{ 'name' => 'deptest-dep_1', 'stack' => 'user/depstack1child1'}}
|
87
87
|
let(:expectation_1_1) {{ 'name' => 'deptest-dep_1-dep_1', 'stack' => 'user/depstack1child1child1', 'services' => array_including(hash_including('image' => 'image:2')) }}
|
88
88
|
let(:expectation_2) {{ 'name' => 'deptest-dep_2', 'stack' => 'user/depstack1child2', 'services' => array_including(hash_including('image' => 'image:1')), 'variables' => hash_including('dep_var' => 1) }}
|
89
89
|
|
90
|
-
let(:response) { expectation.merge('parent' => nil, 'children' => [{'name' => 'deptest-dep_1'}, {'name' => 'deptest-dep_2'}])
|
91
|
-
let(:response_1) { expectation_1.merge('parent' => { 'name' => 'deptest' }, 'children' => [{'name' => 'deptest-dep_1-dep_1'}]) }
|
92
|
-
let(:response_1_1) { expectation_1_1.merge('parent' => { 'name' => 'deptest-dep_1' }, 'children' => []) }
|
90
|
+
let(:response) { expectation.merge('parent' => nil, 'children' => [{'name' => 'deptest-dep_1'}, {'name' => 'deptest-dep_2'}], 'services' => []) }
|
91
|
+
let(:response_1) { expectation_1.merge('parent' => { 'name' => 'deptest' }, 'children' => [{'name' => 'deptest-dep_1-dep_1'}], 'services' => []) }
|
92
|
+
let(:response_1_1) { expectation_1_1.merge('parent' => { 'name' => 'deptest-dep_1' }, 'children' => [], 'services' => []) }
|
93
93
|
let(:response_2) { expectation_2.merge('parent' => { 'name' => 'deptest' }, 'children' => [], 'variables' => {}, 'services' => []) }
|
94
94
|
|
95
95
|
before do
|
@@ -104,7 +104,7 @@ describe Kontena::Cli::Stacks::UpgradeCommand do
|
|
104
104
|
expect(client).to receive(:put).with('stacks/test-grid/deptest-dep_1-dep_1', hash_including(expectation_1_1)).and_return(true)
|
105
105
|
expect(client).to receive(:put).with('stacks/test-grid/deptest-dep_1', hash_including(expectation_1)).and_return(true)
|
106
106
|
expect(client).to receive(:put).with('stacks/test-grid/deptest', hash_including(expectation)).and_return(true)
|
107
|
-
subject.run(['--no-deploy', '-v', 'dep_2.dep_var=1', 'deptest', fixture_path('stack-with-dependencies.yml')])
|
107
|
+
subject.run(['--force', '--no-deploy', '-v', 'dep_2.dep_var=1', 'deptest', fixture_path('stack-with-dependencies.yml')])
|
108
108
|
end
|
109
109
|
|
110
110
|
context 'when a dependency has been removed' do
|
@@ -117,8 +117,8 @@ describe Kontena::Cli::Stacks::UpgradeCommand do
|
|
117
117
|
context 'when a dependency has been added' do
|
118
118
|
it 'installs any new stacks in the dependency chain' do
|
119
119
|
allow(client).to receive(:put).and_return(true)
|
120
|
-
expect(Kontena).to receive(:run!).with(["stack", "install", "--name", "deptest-dep_3", "--
|
121
|
-
subject.run(['--no-deploy', '-v', 'dep_2.dep_var=1', 'deptest', fixture_path('stack-with-dependencies-dep_3-added.yml')])
|
120
|
+
expect(Kontena).to receive(:run!).with(["stack", "install", "--name", "deptest-dep_3", "--no-deploy", "--parent-name", "deptest", fixture_path('stack-with-dependencies-dep-3.yml')]).and_return(true)
|
121
|
+
subject.run(['--no-deploy', '--force', '-v', 'dep_2.dep_var=1', 'deptest', fixture_path('stack-with-dependencies-dep_3-added.yml')])
|
122
122
|
end
|
123
123
|
end
|
124
124
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kontena-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.0.
|
4
|
+
version: 1.4.0.pre9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kontena, Inc
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-09-
|
11
|
+
date: 2017-09-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -257,6 +257,7 @@ files:
|
|
257
257
|
- lib/kontena/cli/certificate/get_command.rb
|
258
258
|
- lib/kontena/cli/certificate/list_command.rb
|
259
259
|
- lib/kontena/cli/certificate/register_command.rb
|
260
|
+
- lib/kontena/cli/certificate/remove_command.rb
|
260
261
|
- lib/kontena/cli/certificate/request_command.rb
|
261
262
|
- lib/kontena/cli/certificate/show_command.rb
|
262
263
|
- lib/kontena/cli/certificate_command.rb
|
@@ -408,6 +409,7 @@ files:
|
|
408
409
|
- lib/kontena/cli/spinner.rb
|
409
410
|
- lib/kontena/cli/stack_command.rb
|
410
411
|
- lib/kontena/cli/stacks/build_command.rb
|
412
|
+
- lib/kontena/cli/stacks/change_resolver.rb
|
411
413
|
- lib/kontena/cli/stacks/common.rb
|
412
414
|
- lib/kontena/cli/stacks/deploy_command.rb
|
413
415
|
- lib/kontena/cli/stacks/events_command.rb
|