kontena-cli 1.4.0.pre8 → 1.4.0.pre9
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 +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
|