kontena-cli 1.0.6 → 1.1.0.pre1

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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/VERSION +1 -1
  4. data/bin/kontena +4 -1
  5. data/kontena-cli.gemspec +1 -1
  6. data/lib/kontena/callback.rb +1 -1
  7. data/lib/kontena/callbacks/master/01_clear_current_master_after_terminate.rb +1 -1
  8. data/lib/kontena/callbacks/master/deploy/50_authenticate_after_deploy.rb +5 -5
  9. data/lib/kontena/callbacks/master/deploy/55_create_initial_grid_after_deploy.rb +1 -1
  10. data/lib/kontena/callbacks/master/deploy/56_set_server_provider_after_deploy.rb +25 -0
  11. data/lib/kontena/callbacks/master/deploy/70_invite_self_after_deploy.rb +1 -1
  12. data/lib/kontena/cli/common.rb +3 -3
  13. data/lib/kontena/cli/config.rb +1 -1
  14. data/lib/kontena/cli/grid_command.rb +2 -0
  15. data/lib/kontena/cli/grids/common.rb +12 -0
  16. data/lib/kontena/cli/grids/health_command.rb +69 -0
  17. data/lib/kontena/cli/helpers/health_helper.rb +53 -0
  18. data/lib/kontena/cli/localhost_web_server.rb +3 -3
  19. data/lib/kontena/cli/master/users/invite_command.rb +1 -1
  20. data/lib/kontena/cli/node_command.rb +2 -0
  21. data/lib/kontena/cli/nodes/health_command.rb +32 -0
  22. data/lib/kontena/cli/nodes/list_command.rb +40 -26
  23. data/lib/kontena/cli/nodes/show_command.rb +0 -1
  24. data/lib/kontena/cli/plugins/install_command.rb +28 -30
  25. data/lib/kontena/cli/plugins/search_command.rb +6 -14
  26. data/lib/kontena/cli/plugins/uninstall_command.rb +7 -11
  27. data/lib/kontena/cli/services/stats_command.rb +4 -2
  28. data/lib/kontena/cli/spinner.rb +20 -4
  29. data/lib/kontena/cli/stacks/show_command.rb +5 -1
  30. data/lib/kontena/cli/stacks/yaml/opto/service_instances_resolver.rb +22 -0
  31. data/lib/kontena/cli/stacks/yaml/opto/vault_setter.rb +1 -1
  32. data/lib/kontena/cli/stacks/yaml/reader.rb +1 -0
  33. data/lib/kontena/cli/vault/export_command.rb +22 -0
  34. data/lib/kontena/cli/vault/import_command.rb +80 -0
  35. data/lib/kontena/cli/vault/list_command.rb +4 -0
  36. data/lib/kontena/cli/vault/read_command.rb +8 -3
  37. data/lib/kontena/cli/vault/remove_command.rb +2 -1
  38. data/lib/kontena/cli/vault/update_command.rb +5 -7
  39. data/lib/kontena/cli/vault_command.rb +5 -1
  40. data/lib/kontena/client.rb +25 -2
  41. data/lib/kontena/command.rb +1 -1
  42. data/lib/kontena/debug_instrumentor.rb +70 -0
  43. data/lib/kontena/light_prompt.rb +103 -0
  44. data/lib/kontena/plugin_manager.rb +167 -6
  45. data/lib/kontena/stacks_cache.rb +1 -1
  46. data/lib/kontena_cli.rb +23 -6
  47. data/spec/kontena/cli/grids/health_command_spec.rb +390 -0
  48. data/spec/kontena/cli/nodes/health_command_spec.rb +206 -0
  49. data/spec/kontena/cli/nodes/list_command_spec.rb +205 -0
  50. data/spec/kontena/cli/vault/export_spec.rb +32 -0
  51. data/spec/kontena/cli/vault/import_spec.rb +69 -0
  52. data/spec/kontena/client_spec.rb +39 -0
  53. data/spec/kontena/plugin_manager_spec.rb +7 -7
  54. data/spec/spec_helper.rb +1 -0
  55. data/spec/support/output_helpers.rb +51 -0
  56. metadata +27 -6
@@ -0,0 +1,206 @@
1
+ require 'kontena/cli/nodes/health_command'
2
+
3
+ describe Kontena::Cli::Nodes::HealthCommand do
4
+ include ClientHelpers
5
+ include OutputHelpers
6
+
7
+ before do
8
+ allow(subject).to receive(:health_icon) {|health| health.inspect }
9
+ end
10
+
11
+ context "for an online node" do
12
+ let :node do
13
+ {
14
+ "connected" => true,
15
+ "name" => "node-4",
16
+ "node_number" => 4,
17
+ "initial_member" => false,
18
+ }
19
+ end
20
+
21
+ let :grid_health do
22
+ :ok
23
+ end
24
+
25
+ describe '#node_health' do
26
+ it "returns ok" do
27
+ expect(subject.node_health(node, grid_health)).to eq(:ok)
28
+ end
29
+ end
30
+
31
+ describe '#show_node_health' do
32
+ it "returns true" do
33
+ expect{subject.show_node_health(node)}.to return_and_output true,
34
+ ":ok Node is online"
35
+ end
36
+ end
37
+ end
38
+
39
+ context "for an offline node" do
40
+ let :node do
41
+ {
42
+ "connected" => false,
43
+ "name" => "node-4",
44
+ "node_number" => 4,
45
+ "initial_member" => false,
46
+ }
47
+ end
48
+
49
+ let :grid_health do
50
+ :ok
51
+ end
52
+
53
+ describe '#node_health' do
54
+ it "returns offline" do
55
+ expect(subject.node_health(node, grid_health)).to eq(:offline)
56
+ end
57
+ end
58
+
59
+ describe '#show_node_health' do
60
+ it "returns false" do
61
+ expect{subject.show_node_health(node)}.to return_and_output false,
62
+ ":offline Node is offline"
63
+ end
64
+ end
65
+ end
66
+
67
+ context "for an online initial node in an ok grid" do
68
+ let :node do
69
+ {
70
+ "connected" => true,
71
+ "name" => "node-1",
72
+ "node_number" => 1,
73
+ "initial_member" => true,
74
+ }
75
+ end
76
+
77
+ let :grid_health do
78
+ :ok
79
+ end
80
+
81
+ describe '#node_health' do
82
+ it "returns ok" do
83
+ expect(subject.node_health(node, grid_health)).to eq(:ok)
84
+ end
85
+ end
86
+
87
+ describe '#show_node_health' do
88
+ it "returns true" do
89
+ expect{subject.show_node_health(node)}.to return_and_output true,
90
+ ":ok Node is online"
91
+ end
92
+ end
93
+ end
94
+
95
+ context "for an online initial node in a warning grid" do
96
+ let :node do
97
+ {
98
+ "connected" => true,
99
+ "name" => "node-1",
100
+ "node_number" => 1,
101
+ "initial_member" => true,
102
+ }
103
+ end
104
+
105
+ let :grid_health do
106
+ :warning
107
+ end
108
+
109
+ describe '#node_health' do
110
+ it "returns ok" do
111
+ expect(subject.node_health(node, grid_health)).to eq(:warning)
112
+ end
113
+ end
114
+
115
+ describe '#show_node_health' do
116
+ it "returns true" do
117
+ expect{subject.show_node_health(node)}.to return_and_output true,
118
+ ":ok Node is online"
119
+ end
120
+ end
121
+ end
122
+
123
+ context "for an online initial node in an error grid" do
124
+ let :node do
125
+ {
126
+ "connected" => true,
127
+ "name" => "node-1",
128
+ "node_number" => 1,
129
+ "initial_member" => true,
130
+ }
131
+ end
132
+
133
+ let :grid_health do
134
+ :error
135
+ end
136
+
137
+ describe '#node_health' do
138
+ it "returns ok" do
139
+ expect(subject.node_health(node, grid_health)).to eq(:error)
140
+ end
141
+ end
142
+
143
+ describe '#show_node_health' do
144
+ it "returns true" do
145
+ expect{subject.show_node_health(node)}.to return_and_output true,
146
+ ":ok Node is online"
147
+ end
148
+ end
149
+ end
150
+
151
+ context "for an offline initial node in a warning grid" do
152
+ let :node do
153
+ {
154
+ "connected" => false,
155
+ "name" => "node-1",
156
+ "node_number" => 1,
157
+ "initial_member" => true,
158
+ }
159
+ end
160
+
161
+ let :grid_health do
162
+ :warning
163
+ end
164
+
165
+ describe '#node_health' do
166
+ it "returns offline" do
167
+ expect(subject.node_health(node, grid_health)).to eq(:offline)
168
+ end
169
+ end
170
+
171
+ describe '#show_node_health' do
172
+ it "returns false" do
173
+ expect{subject.show_node_health(node)}.to return_and_output false,
174
+ ":offline Node is offline"
175
+ end
176
+ end
177
+ end
178
+
179
+ context "for an offline initial node in a error grid" do
180
+ let :node do
181
+ {
182
+ "connected" => false,
183
+ "name" => "node-1",
184
+ "node_number" => 1,
185
+ "initial_member" => true,
186
+ }
187
+ end
188
+
189
+ let :grid_health do
190
+ :error
191
+ end
192
+
193
+ describe '#node_health' do
194
+ it "returns offline" do
195
+ expect(subject.node_health(node, grid_health)).to eq(:offline)
196
+ end
197
+ end
198
+
199
+ describe '#show_node_health' do
200
+ it "returns false" do
201
+ expect{subject.show_node_health(node)}.to return_and_output false,
202
+ ":offline Node is offline"
203
+ end
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,205 @@
1
+ describe Kontena::Cli::Nodes::ListCommand do
2
+ include ClientHelpers
3
+ include OutputHelpers
4
+
5
+ before do
6
+ allow(subject).to receive(:health_icon) {|health| health.inspect }
7
+ end
8
+
9
+ describe '#show_grid_nodes' do
10
+ context "For a initial_size=3 grid" do
11
+ let :grid do
12
+ {
13
+ "id" => "test",
14
+ "name" => "test",
15
+ "initial_size" => 3,
16
+ "node_count" => 1,
17
+ }
18
+ end
19
+
20
+ context "with a single node" do
21
+ let :grid_nodes do
22
+ { "nodes" => [
23
+ {
24
+ "connected" => true,
25
+ "name" => "node-1",
26
+ "node_number" => 1,
27
+ "initial_member" => true,
28
+ 'agent_version' => '1.1-dev',
29
+ },
30
+ ] }
31
+ end
32
+
33
+ it "outputs node with error" do
34
+ expect{subject.show_grid_nodes(grid, grid_nodes['nodes'])}.to output_table [
35
+ [':error node-1', '1.1-dev', 'online', '1 / 3', '-'],
36
+ ]
37
+ end
38
+ end
39
+
40
+ context "with a single online node" do
41
+ let :grid_nodes do
42
+ { "nodes" => [
43
+ {
44
+ "connected" => true,
45
+ "name" => "node-1",
46
+ "node_number" => 1,
47
+ "initial_member" => true,
48
+ 'agent_version' => '1.1-dev',
49
+ },
50
+ {
51
+ "connected" => false,
52
+ "name" => "node-2",
53
+ "node_number" => 2,
54
+ "initial_member" => true,
55
+ 'agent_version' => '1.1-dev',
56
+ },
57
+ ] }
58
+ end
59
+
60
+ it "outputs online node with error" do
61
+ expect{subject.show_grid_nodes(grid, grid_nodes['nodes'])}.to output_table [
62
+ [':error node-1', '1.1-dev', 'online', '1 / 3', '-'],
63
+ [':offline node-2', '1.1-dev', 'offline', '2 / 3', '-'],
64
+ ]
65
+ end
66
+ end
67
+
68
+ context "with two online nodes" do
69
+ let :grid_nodes do
70
+ { "nodes" => [
71
+ {
72
+ "connected" => true,
73
+ "name" => "node-1",
74
+ "node_number" => 1,
75
+ "initial_member" => true,
76
+ 'agent_version' => '1.1-dev',
77
+ },
78
+ {
79
+ "connected" => true,
80
+ "name" => "node-2",
81
+ "node_number" => 2,
82
+ "initial_member" => true,
83
+ 'agent_version' => '1.1-dev',
84
+ },
85
+ ] }
86
+ end
87
+
88
+ it "outputs both nodes with warning" do
89
+ expect{subject.show_grid_nodes(grid, grid_nodes['nodes'])}.to output_table [
90
+ [':warning node-1', '1.1-dev', 'online', '1 / 3', '-'],
91
+ [':warning node-2', '1.1-dev', 'online', '2 / 3', '-'],
92
+ ]
93
+ end
94
+ end
95
+
96
+ context "with two online nodes and one offline node" do
97
+ let :grid_nodes do
98
+ { "nodes" => [
99
+ {
100
+ "connected" => true,
101
+ "name" => "node-1",
102
+ "node_number" => 1,
103
+ "initial_member" => true,
104
+ 'agent_version' => '1.1-dev',
105
+ },
106
+ {
107
+ "connected" => true,
108
+ "name" => "node-2",
109
+ "node_number" => 2,
110
+ "initial_member" => true,
111
+ 'agent_version' => '1.1-dev',
112
+ },
113
+ {
114
+ "connected" => false,
115
+ "name" => "node-3",
116
+ "node_number" => 3,
117
+ "initial_member" => true,
118
+ 'agent_version' => '1.1-dev',
119
+ },
120
+ ] }
121
+ end
122
+
123
+ it "outputs two nodes with warning and one offline" do
124
+ expect{subject.show_grid_nodes(grid, grid_nodes['nodes'])}.to output_table [
125
+ [':warning node-1', '1.1-dev', 'online', '1 / 3', '-'],
126
+ [':warning node-2', '1.1-dev', 'online', '2 / 3', '-'],
127
+ [':offline node-3', '1.1-dev', 'offline', '3 / 3', '-'],
128
+ ]
129
+ end
130
+ end
131
+
132
+ context "with two online initial nodes and one non-initial node" do
133
+ let :grid_nodes do
134
+ { "nodes" => [
135
+ {
136
+ "connected" => true,
137
+ "name" => "node-1",
138
+ "node_number" => 1,
139
+ "initial_member" => true,
140
+ 'agent_version' => '1.1-dev',
141
+ },
142
+ {
143
+ "connected" => true,
144
+ "name" => "node-2",
145
+ "node_number" => 2,
146
+ "initial_member" => true,
147
+ 'agent_version' => '1.1-dev',
148
+ },
149
+ {
150
+ "connected" => true,
151
+ "name" => "node-4",
152
+ "node_number" => 4,
153
+ "initial_member" => false,
154
+ 'agent_version' => '1.1-dev',
155
+ },
156
+ ] }
157
+ end
158
+
159
+ it "outputs two nodes with warning and one online" do
160
+ expect{subject.show_grid_nodes(grid, grid_nodes['nodes'])}.to output_table [
161
+ [':warning node-1', '1.1-dev', 'online', '1 / 3', '-'],
162
+ [':warning node-2', '1.1-dev', 'online', '2 / 3', '-'],
163
+ [':ok node-4', '1.1-dev', 'online', '-', '-'],
164
+ ]
165
+ end
166
+ end
167
+
168
+ context "with three online initial nodes" do
169
+ let :grid_nodes do
170
+ { "nodes" => [
171
+ {
172
+ "connected" => true,
173
+ "name" => "node-1",
174
+ "node_number" => 1,
175
+ "initial_member" => true,
176
+ 'agent_version' => '1.1-dev',
177
+ },
178
+ {
179
+ "connected" => true,
180
+ "name" => "node-2",
181
+ "node_number" => 2,
182
+ "initial_member" => true,
183
+ 'agent_version' => '1.1-dev',
184
+ },
185
+ {
186
+ "connected" => true,
187
+ "name" => "node-3",
188
+ "node_number" => 3,
189
+ "initial_member" => true,
190
+ 'agent_version' => '1.1-dev',
191
+ },
192
+ ] }
193
+ end
194
+
195
+ it "outputs three nodes with ok" do
196
+ expect{subject.show_grid_nodes(grid, grid_nodes['nodes'])}.to output_table [
197
+ [':ok node-1', '1.1-dev', 'online', '1 / 3', '-'],
198
+ [':ok node-2', '1.1-dev', 'online', '2 / 3', '-'],
199
+ [':ok node-3', '1.1-dev', 'online', '3 / 3', '-'],
200
+ ]
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,32 @@
1
+ require_relative "../../../spec_helper"
2
+ require 'kontena/cli/vault/export_command'
3
+
4
+ describe Kontena::Cli::Vault::ExportCommand do
5
+
6
+ include RequirementsHelper
7
+
8
+ let(:subject) do
9
+ described_class.new(File.basename($0))
10
+ end
11
+
12
+ expect_to_require_current_master
13
+
14
+ before(:each) do
15
+ allow(Kontena::Cli::Config.instance).to receive(:current_master).and_return(Kontena::Cli::Config::Server.new)
16
+ end
17
+
18
+ it 'goes through the list of vault keys and outputs a yaml' do
19
+ expect(Kontena).to receive(:run).with(/^vault ls/, returning: :result).and_return(['foo', 'bar'])
20
+ expect(Kontena).to receive(:run).with(/^vault read.*bar/, returning: :result).and_return('barbar')
21
+ expect(Kontena).to receive(:run).with(/^vault read.*foo/, returning: :result).and_return('foofoo')
22
+ expect{subject.run([])}.to output(/bar: barbar\nfoo: foofoo/).to_stdout
23
+ end
24
+
25
+ it 'goes through the list of vault keys and outputs a json' do
26
+ expect(Kontena).to receive(:run).with(/^vault ls/, returning: :result).and_return(['foo', 'bar'])
27
+ expect(Kontena).to receive(:run).with(/^vault read.*bar/, returning: :result).and_return('barbar')
28
+ expect(Kontena).to receive(:run).with(/^vault read.*foo/, returning: :result).and_return('foofoo')
29
+ expect{subject.run(['--json'])}.to output(/\"bar\":\"barbar\",\"foo\":\"foofoo\"/).to_stdout
30
+ end
31
+
32
+ end
@@ -0,0 +1,69 @@
1
+ require_relative "../../../spec_helper"
2
+ require 'kontena/cli/vault/import_command'
3
+
4
+
5
+ describe Kontena::Cli::Vault::ImportCommand do
6
+
7
+ include RequirementsHelper
8
+
9
+ let(:subject) do
10
+ described_class.new(File.basename($0))
11
+ end
12
+
13
+ before(:each) do
14
+ allow(Kontena::Cli::Config.instance).to receive(:current_master).and_return(Kontena::Cli::Config::Server.new)
15
+ allow(Kontena::Cli::Config.instance).to receive(:current_grid).and_return("foofoo")
16
+ end
17
+
18
+ expect_to_require_current_master
19
+
20
+ it 'asks for confirmation' do
21
+ expect(subject).to receive(:confirm).and_raise(RuntimeError, 'confirm')
22
+ expect(File).to receive(:read).with('foo.yml').and_return("foo: bar\n")
23
+ expect{subject.run(['foo.yml'])}.to raise_error(RuntimeError, 'confirm')
24
+ end
25
+
26
+ it 'dies if the yml contains something odd' do
27
+ expect(File).to receive(:read).with('foo.yml').and_return({foo: 'bar', bar: { foo: ["bar"] }}.to_yaml)
28
+ expect(subject).to receive(:exit_with_error).with(/Invalid value/).and_raise(RuntimeError, 'invalid')
29
+ expect{subject.run(['foo.yml'])}.to raise_error(RuntimeError, 'invalid')
30
+ end
31
+
32
+ it 'runs vault write for kv-pairs in yaml' do
33
+ expect(File).to receive(:read).with('foo.yml').and_return("foo: bar\nbar: foo\n")
34
+ expect(Kontena).to receive(:run).with(/vault update.*foo bar/).and_return(0)
35
+ expect(Kontena).to receive(:run).with(/vault update.*bar foo/).and_return(0)
36
+ subject.run(['--force', 'foo.yml'])
37
+ end
38
+
39
+ it 'runs vault rm for kv-pairs with null value in yaml' do
40
+ expect(File).to receive(:read).with('foo.yml').and_return("foo: bar\nbar: null\n")
41
+ expect(Kontena).to receive(:run).with(/vault update.*foo bar/).and_return(0)
42
+ expect(Kontena).to receive(:run).with(/vault rm.*bar/).and_return(0)
43
+ subject.run(['--force', 'foo.yml'])
44
+ end
45
+
46
+ it 'runs vault rm for kv-pairs with empty value in yaml when --empty-is-null' do
47
+ expect(File).to receive(:read).with('foo.yml').and_return("foo: bar\nbar:\n")
48
+ expect(Kontena).to receive(:run).with(/vault update.*foo bar/).and_return(0)
49
+ expect(Kontena).to receive(:run).with(/vault rm.*bar/).and_return(0)
50
+ subject.run(['--force', '--empty-is-null', 'foo.yml'])
51
+ end
52
+
53
+ it 'does not run vault rm for kv-pairs with empty value in yaml when no --empty-is-null' do
54
+ expect(File).to receive(:read).with('foo.yml').and_return("foo: bar\nbar: \"\"\n")
55
+ expect(Kontena).to receive(:run).with(/vault update.*foo bar/).and_return(0)
56
+ expect(Kontena).to receive(:run).with(/vault update.*bar ''/).and_return(0)
57
+ expect(Kontena).not_to receive(:run).with(/vault rm.*bar/)
58
+ subject.run(['--force', 'foo.yml'])
59
+ end
60
+
61
+ it 'doesnt vault rm for kv-pairs with null value in yaml when --skip-null used' do
62
+ expect(File).to receive(:read).with('foo.yml').and_return("foo: bar\nbar: null\n")
63
+ expect(Kontena).to receive(:run).with(/vault update.*foo bar/).and_return(0)
64
+ expect(Kontena).not_to receive(:run).with(/vault rm.*bar/)
65
+ subject.run(['--force', '--skip-null', 'foo.yml'])
66
+ end
67
+
68
+ end
69
+