kontena-cli 1.3.5 → 1.4.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +5 -5
  2. data/VERSION +1 -1
  3. data/lib/kontena/cli/etcd/health_command.rb +1 -1
  4. data/lib/kontena/cli/grids/common.rb +51 -0
  5. data/lib/kontena/cli/grids/create_command.rb +13 -9
  6. data/lib/kontena/cli/grids/update_command.rb +11 -44
  7. data/lib/kontena/cli/node_command.rb +3 -0
  8. data/lib/kontena/cli/nodes/create_command.rb +25 -0
  9. data/lib/kontena/cli/nodes/env_command.rb +32 -0
  10. data/lib/kontena/cli/nodes/health_command.rb +2 -2
  11. data/lib/kontena/cli/nodes/labels/add_command.rb +3 -3
  12. data/lib/kontena/cli/nodes/labels/list_command.rb +2 -2
  13. data/lib/kontena/cli/nodes/labels/remove_command.rb +3 -3
  14. data/lib/kontena/cli/nodes/remove_command.rb +9 -7
  15. data/lib/kontena/cli/nodes/reset_token_command.rb +31 -0
  16. data/lib/kontena/cli/nodes/show_command.rb +4 -4
  17. data/lib/kontena/cli/nodes/ssh_command.rb +4 -4
  18. data/lib/kontena/cli/nodes/update_command.rb +13 -8
  19. data/lib/kontena/cli/services/create_command.rb +4 -0
  20. data/lib/kontena/cli/services/services_helper.rb +2 -0
  21. data/lib/kontena/cli/services/update_command.rb +2 -0
  22. data/lib/kontena/cli/stacks/service_generator.rb +2 -0
  23. data/lib/kontena/cli/stacks/show_command.rb +1 -0
  24. data/lib/kontena/cli/stacks/stacks_helper.rb +2 -3
  25. data/lib/kontena/cli/stacks/yaml/validations.rb +3 -1
  26. data/lib/kontena/machine/cloud_config/cloudinit.yml +17 -4
  27. data/omnibus/package-scripts/kontena/postinst +0 -4
  28. data/omnibus/package-scripts/kontena/postrm +1 -1
  29. data/omnibus/package-scripts/kontena/preinst +0 -2
  30. data/spec/fixtures/api/node.json +93 -0
  31. data/spec/kontena/cli/containers/logs_command_spec.rb +0 -4
  32. data/spec/kontena/cli/etcd/health_command_spec.rb +128 -63
  33. data/spec/kontena/cli/nodes/create_command_spec.rb +24 -0
  34. data/spec/kontena/cli/nodes/env_command_spec.rb +49 -0
  35. data/spec/kontena/cli/nodes/health_command_spec.rb +15 -173
  36. data/spec/kontena/cli/nodes/labels/add_command_spec.rb +56 -0
  37. data/spec/kontena/cli/nodes/labels/list_command_spec.rb +43 -0
  38. data/spec/kontena/cli/nodes/labels/remove_command_spec.rb +57 -0
  39. data/spec/kontena/cli/nodes/list_command_spec.rb +0 -2
  40. data/spec/kontena/cli/nodes/remove_command_spec.rb +76 -0
  41. data/spec/kontena/cli/nodes/reset_token_command_spec.rb +38 -0
  42. data/spec/kontena/cli/nodes/show_command_spec.rb +46 -0
  43. data/spec/kontena/cli/nodes/ssh_command_spec.rb +5 -0
  44. data/spec/kontena/cli/nodes/update_command_spec.rb +24 -0
  45. data/spec/kontena/cli/stacks/deploy_command_spec.rb +21 -0
  46. data/spec/kontena/cli/stacks/logs_command_spec.rb +0 -4
  47. data/spec/kontena/cli/table_generator_spec.rb +0 -4
  48. data/spec/spec_helper.rb +5 -0
  49. data/spec/support/output_helpers.rb +3 -14
  50. data/tasks/release.rake +2 -2
  51. metadata +28 -5
@@ -3,7 +3,7 @@ module Kontena::Cli::Nodes
3
3
  include Kontena::Cli::Common
4
4
  include Kontena::Cli::GridOptions
5
5
 
6
- parameter "[NODE_ID]", "SSH to Grid node. Use --any to connect to the first available node"
6
+ parameter "[NODE]", "SSH to Grid node. Use --any to connect to the first available node"
7
7
  parameter "[COMMANDS] ...", "Run command on host"
8
8
  option ["-a", "--any"], :flag, "Connect to first available node"
9
9
  option ["-i", "--identity-file"], "IDENTITY_FILE", "Path to ssh private key"
@@ -15,10 +15,10 @@ module Kontena::Cli::Nodes
15
15
  requires_current_grid
16
16
 
17
17
  def execute
18
- exit_with_error "Cannot combine --any with a node name" if node_id && any?
18
+ exit_with_error "Cannot combine --any with a node name" if self.node && any?
19
19
 
20
- if node_id
21
- node = client.get("nodes/#{current_grid}/#{node_id}")
20
+ if self.node
21
+ node = client.get("nodes/#{current_grid}/#{self.node}")
22
22
  elsif any?
23
23
  nodes = client.get("grids/#{current_grid}/nodes")['nodes']
24
24
  node = nodes.select{ |node| node['connected'] }.first
@@ -3,18 +3,23 @@ module Kontena::Cli::Nodes
3
3
  include Kontena::Cli::Common
4
4
  include Kontena::Cli::GridOptions
5
5
 
6
- parameter "NODE_ID", "Node id"
6
+ requires_current_master
7
+ requires_current_master_token
8
+ requires_current_grid
9
+
10
+ parameter "NODE", "Node name"
11
+
7
12
  option ["-l", "--label"], "LABEL", "Node label", multivalued: true
13
+ option "--clear-labels", :flag, "Clear node labels"
8
14
 
9
15
  def execute
10
- require_api_url
11
- require_current_grid
12
- token = require_token
13
-
14
16
  data = {}
15
- data[:labels] = label_list if label_list
16
- spinner "Updating #{node_id.colorize(:cyan)} node " do
17
- client.put("nodes/#{current_grid}/#{node_id}", data)
17
+
18
+ data[:labels] = self.label_list unless self.label_list.empty?
19
+ data[:labels] = [] if self.clear_labels?
20
+
21
+ spinner "Updating #{self.node.colorize(:cyan)} node " do
22
+ client.put("nodes/#{current_grid}/#{self.node}", data)
18
23
  end
19
24
  end
20
25
  end
@@ -41,6 +41,8 @@ module Kontena::Cli::Services
41
41
  option "--health-check-initial-delay", "DELAY", "Initial delay for health check"
42
42
  option "--health-check-port", "PORT", "Port for health check"
43
43
  option "--health-check-protocol", "PROTOCOL", "Protocol of health check"
44
+ option "--stop-timeout", "STOP_TIMEOUT", "Timeout (duration) to stop a container"
45
+ option "--read-only", :flag, "Read-only root fs for the container", default: false
44
46
 
45
47
  def execute
46
48
  require_api_url
@@ -87,6 +89,8 @@ module Kontena::Cli::Services
87
89
  health_check = parse_health_check
88
90
  data[:health_check] = health_check unless health_check.empty?
89
91
  data[:pid] = pid if pid
92
+ data[:stop_grace_period] = stop_timeout if stop_timeout
93
+ data[:read_only] = read_only?
90
94
  data
91
95
  end
92
96
  end
@@ -52,6 +52,8 @@ module Kontena
52
52
  puts " stateful: #{service['stateful'] == true ? 'yes' : 'no' }"
53
53
  puts " scaling: #{service['instances'] }"
54
54
  puts " strategy: #{service['strategy']}"
55
+ puts " read_only: #{service['read_only'] == true ? 'yes' : 'no'}"
56
+ puts " stop_grace_period: #{service['stop_grace_period']}s"
55
57
  puts " deploy_opts:"
56
58
  if service['deploy_opts']['min_health']
57
59
  puts " min_health: #{service['deploy_opts']['min_health']}"
@@ -38,6 +38,7 @@ module Kontena::Cli::Services
38
38
  option "--health-check-initial-delay", "DELAY", "Initial for HTTP health check"
39
39
  option "--health-check-port", "PORT", "Port for HTTP health check"
40
40
  option "--health-check-protocol", "PROTOCOL", "Protocol of health check"
41
+ option "--stop-timeout", "STOP_TIMEOUT", "Timeout (duration) to stop a container"
41
42
 
42
43
  def execute
43
44
  require_api_url
@@ -78,6 +79,7 @@ module Kontena::Cli::Services
78
79
  health_check = parse_health_check
79
80
  data[:health_check] = health_check unless health_check.empty?
80
81
  data[:pid] = pid if pid
82
+ data[:stop_grace_period] = stop_timeout if stop_timeout
81
83
  data
82
84
  end
83
85
  end
@@ -63,6 +63,8 @@ module Kontena::Cli::Stacks
63
63
  data['secrets'] = options['secrets'] if options['secrets']
64
64
  data['build'] = parse_build_options(options) if options['build']
65
65
  data['health_check'] = parse_health_check(options)
66
+ data['stop_grace_period'] = options['stop_grace_period'] if options['stop_grace_period']
67
+ data['read_only'] = options['read_only'] || false
66
68
  data
67
69
  end
68
70
 
@@ -57,6 +57,7 @@ module Kontena::Cli::Stacks
57
57
  puts "#{pad} stateful: #{service['stateful'] == true ? 'yes' : 'no' }"
58
58
  puts "#{pad} scaling: #{service['instances'] }"
59
59
  puts "#{pad} strategy: #{service['strategy']}"
60
+ puts "#{pad} read_only: #{service['read_only'] == true ? 'yes' : 'no'}"
60
61
  puts "#{pad} deploy_opts:"
61
62
  puts "#{pad} min_health: #{service['deploy_opts']['min_health']}"
62
63
  if service['deploy_opts']['wait_for_port']
@@ -4,10 +4,9 @@ module Kontena::Cli::Stacks
4
4
  def wait_for_deployment_to_start(deployment, timeout = 600)
5
5
  started = false
6
6
  Timeout::timeout(timeout) do
7
- until started
8
- deployment = client.get("stacks/#{deployment['stack_id']}/deploys/#{deployment['id']}")
9
- started = true if deployment['service_deploys'].size > 0
7
+ while deployment['state'] == 'created'
10
8
  sleep 1
9
+ deployment = client.get("stacks/#{deployment['stack_id']}/deploys/#{deployment['id']}")
11
10
  end
12
11
  if deployment['state'] == 'error'
13
12
  deployment['service_deploys'].each do |service_deploy|
@@ -74,7 +74,9 @@ module Kontena::Cli::Stacks::YAML
74
74
  'timeout' => optional('integer'),
75
75
  'interval' => optional('integer'),
76
76
  'initial_delay' => optional('integer')
77
- })
77
+ }),
78
+ 'stop_grace_period' => optional(/(\d+(?:\.\d+)?)([hms])/),
79
+ 'read_only' => optional('boolean')
78
80
  }
79
81
  end
80
82
 
@@ -19,10 +19,23 @@ write_files:
19
19
  fs.inotify.max_user_instances = 8192
20
20
  coreos:
21
21
  units:
22
- - name: 50-docker.network
23
- mask: true
24
- - name: 50-docker-veth.network
25
- mask: true
22
+ - name: 50-weave.network
23
+ content: |
24
+ [Match]
25
+ Name=weave datapath vxlan-6784
26
+
27
+ [Network]
28
+ Description=Network interfaces managed by weave
29
+
30
+ [Link]
31
+ Unmanaged=true
32
+ - name: 90-dummy.network
33
+ content: |
34
+ [Match]
35
+ Name=dummy0
36
+
37
+ [Link]
38
+ Unmanaged=true
26
39
  - name: zz-default.network
27
40
  content: |
28
41
  # default should not match virtual Docker/weave bridge/veth network interfaces
@@ -12,11 +12,7 @@ error_exit()
12
12
  exit 1
13
13
  }
14
14
 
15
- chown $USER /opt/kontena/embedded/bin
16
- chown $USER -R /opt/kontena/embedded/lib/ruby/gems/2.1.0
17
15
  mkdir -p /usr/local/bin
18
16
  ln -s /opt/kontena/bin/kontena /usr/local/bin/kontena
19
17
 
20
- echo "Thank you for installing Kontena!"
21
-
22
18
  exit 0
@@ -4,6 +4,6 @@
4
4
  # after package is uninstalled.
5
5
  #
6
6
 
7
- echo "Kontena has been uninstalled!"
7
+ [ -L /usr/local/bin/kontena ] && rm /usr/local/bin/kontena
8
8
 
9
9
  exit 0
@@ -3,5 +3,3 @@
3
3
  # Perform necessary kontena setup steps
4
4
  # before package is installed.
5
5
  #
6
-
7
- echo "You're about to install Kontena!"
@@ -0,0 +1,93 @@
1
+ {
2
+ "initial_member" : true,
3
+ "peer_ips" : [
4
+ "192.168.66.102"
5
+ ],
6
+ "last_seen_at" : "2017-07-04T08:36:02.280Z",
7
+ "updated_at" : "2017-07-04T08:35:09.455Z",
8
+ "node_number" : 1,
9
+ "volume_drivers" : [
10
+ {
11
+ "version" : null,
12
+ "name" : "local"
13
+ }
14
+ ],
15
+ "resource_usage" : {
16
+ "memory" : {
17
+ "free" : 194068480,
18
+ "inactive" : 225415168,
19
+ "buffers" : 37978112,
20
+ "cached" : 390828032,
21
+ "total" : 1045561344,
22
+ "used" : 851492864,
23
+ "active" : 521949184
24
+ },
25
+ "cpu" : null,
26
+ "usage" : null,
27
+ "load" : {
28
+ "5m" : 0.33642578125,
29
+ "1m" : 1.48779296875,
30
+ "15m" : 0.10888671875
31
+ },
32
+ "filesystem" : [
33
+ {
34
+ "total" : 16718393344,
35
+ "used" : 3104915456,
36
+ "name" : "/var/lib/docker",
37
+ "free" : 13613477888,
38
+ "available" : 12730281984
39
+ }
40
+ ]
41
+ },
42
+ "overlay_ip" : "10.81.0.1",
43
+ "labels" : [
44
+ "test"
45
+ ],
46
+ "engine_root_dir" : "/var/lib/docker",
47
+ "cpus" : 1,
48
+ "os" : "Container Linux by CoreOS 1409.5.0 (Ladybug)",
49
+ "public_ip" : "91.150.10.190",
50
+ "kernel_version" : "4.11.6-coreos-r1",
51
+ "node_id" : "XI4K:NPOL:EQJ4:S4V7:EN3B:DHC5:KZJD:F3U2:PCAN:46EV:IO4A:63S5",
52
+ "created_at" : "2016-10-27T08:15:31.084Z",
53
+ "id" : "development/core-01",
54
+ "name" : "core-01",
55
+ "network_drivers" : [
56
+ {
57
+ "version" : null,
58
+ "name" : "bridge"
59
+ },
60
+ {
61
+ "version" : null,
62
+ "name" : "host"
63
+ },
64
+ {
65
+ "version" : null,
66
+ "name" : "overlay"
67
+ },
68
+ {
69
+ "name" : "null",
70
+ "version" : null
71
+ }
72
+ ],
73
+ "driver" : "overlay",
74
+ "mem_limit" : 0,
75
+ "docker_version" : "1.12.6",
76
+ "agent_version" : "1.4.0.dev",
77
+ "private_ip" : "192.168.66.101",
78
+ "connected_at" : "2017-07-04T08:36:02.235Z",
79
+ "grid" : {
80
+ "supernet" : "10.80.0.0/12",
81
+ "trusted_subnets" : [],
82
+ "stats" : {
83
+ "statsd" : null
84
+ },
85
+ "token" : "tBsoco4sBClMtIOD/bHeIqS1KYM0kZMG7B960jNWWQXMOxqhOf8W1QgiNJlW9u6B/qS+rfS2ZLpC/ZJsyPNj0A==",
86
+ "id" : "development",
87
+ "name" : "development",
88
+ "subnet" : "10.81.0.0/16",
89
+ "initial_size" : 1
90
+ },
91
+ "mem_total" : 1045032960,
92
+ "connected" : true
93
+ }
@@ -27,10 +27,6 @@ describe Kontena::Cli::Containers::LogsCommand do
27
27
  ]
28
28
  end
29
29
 
30
- before(:each) do
31
- Kontena.pastel.resolver.color.disable!
32
- end
33
-
34
30
  it "shows all logs" do
35
31
  allow(client).to receive(:get).with('containers/test-grid/node-1/test-mysql-1/logs', {
36
32
  limit: 100,
@@ -8,82 +8,147 @@ describe Kontena::Cli::Etcd::HealthCommand do
8
8
  allow(subject).to receive(:health_icon) {|health| health.inspect }
9
9
  end
10
10
 
11
- describe '#show_node_health' do
12
- context "For an offline node" do
13
- let :node_health do
14
- {
15
- "connected" => false,
16
- "name" => "node-1",
17
- 'etcd_health' => {
18
- 'health' => nil,
19
- 'error' => nil,
20
- },
21
- }
22
- end
11
+ let :node1_health do
12
+ {
13
+ "id" => 'test-grid/node-1',
14
+ "connected" => false,
15
+ "name" => "node-1",
16
+ 'etcd_health' => {
17
+ 'health' => nil,
18
+ 'error' => nil,
19
+ },
20
+ }
21
+ end
22
+ let :node2_health do
23
+ {
24
+ "id" => 'test-grid/node-2',
25
+ "name" => "node-2",
26
+ "connected" => true,
27
+ 'etcd_health' => {
28
+ 'health' => nil,
29
+ 'error' => "timeout",
30
+ },
31
+ }
32
+ end
33
+ let :node3_health do
34
+ {
35
+ "id" => 'test-grid/node-3',
36
+ "name" => "node-3",
37
+ "connected" => true,
38
+ 'etcd_health' => {
39
+ 'health' => false,
40
+ 'error' => nil,
41
+ },
42
+ }
43
+ end
44
+ let :node4_health do
45
+ {
46
+ "id" => 'test-grid/node-4',
47
+ "name" => "node-4",
48
+ "connected" => true,
49
+ 'etcd_health' => {
50
+ 'health' => true,
51
+ 'error' => nil,
52
+ },
53
+ }
54
+ end
23
55
 
24
- it "shows offline and returns false" do
25
- expect{subject.show_node_health(node_health)}.to return_and_output false, [
26
- ":offline Node node-1 is offline",
27
- ]
28
- end
56
+ before do
57
+ allow(client).to receive(:get).with('nodes/test-grid/node-1/health').and_return(node1_health)
58
+ allow(client).to receive(:get).with('nodes/test-grid/node-2/health').and_return(node2_health)
59
+ allow(client).to receive(:get).with('nodes/test-grid/node-3/health').and_return(node3_health)
60
+ allow(client).to receive(:get).with('nodes/test-grid/node-4/health').and_return(node4_health)
61
+ end
62
+
63
+ context "For an offline node-1" do
64
+ it "shows offline and returns false" do
65
+ expect{subject.run(['node-1'])}.to exit_with_error.and output_lines [
66
+ ":offline Node node-1 is offline",
67
+ ]
29
68
  end
69
+ end
30
70
 
31
- context "For a node with health errors" do
32
- let :node_health do
33
- {
34
- "name" => "node-1",
35
- "connected" => true,
36
- 'etcd_health' => {
37
- 'health' => nil,
38
- 'error' => "timeout",
39
- },
40
- }
41
- end
71
+ context "For a node-2 with health errors" do
72
+ it "shows errored and returns false" do
73
+ expect{subject.run(['node-2'])}.to exit_with_error.and output_lines [
74
+ ":error Node node-2 is unhealthy: timeout",
75
+ ]
76
+ end
77
+ end
78
+
79
+ context "For a node-3 that returns health=false" do
80
+ it "shows unhealthy and returns false" do
81
+ expect{subject.run(['node-3'])}.to exit_with_error.and output_lines [
82
+ ":error Node node-3 is unhealthy",
83
+ ]
84
+ end
85
+ end
42
86
 
43
- it "shows errored and returns false" do
44
- expect{subject.show_node_health(node_health)}.to return_and_output false, [
45
- ":error Node node-1 is unhealthy: timeout",
46
- ]
47
- end
87
+ context "For a healthy node-4" do
88
+ it "shows healthy and returns true" do
89
+ expect{subject.run(['node-4'])}.to output_lines [
90
+ ":ok Node node-4 is healthy",
91
+ ]
48
92
  end
93
+ end
49
94
 
50
- context "For a node that returns health=false" do
51
- let :node_health do
95
+ context "For a grid of mixed nodes" do
96
+ let :grid_nodes do
97
+ [
98
+ {
99
+ 'id' => 'test-grid/node-1',
100
+ },
52
101
  {
53
- "name" => "node-1",
54
- "connected" => true,
55
- 'etcd_health' => {
56
- 'health' => false,
57
- 'error' => nil,
58
- },
59
- }
60
- end
102
+ 'id' => 'test-grid/node-2',
103
+ },
104
+ {
105
+ 'id' => 'test-grid/node-3',
106
+ },
107
+ {
108
+ 'id' => 'test-grid/node-4',
109
+ },
110
+ ]
111
+ end
61
112
 
62
- it "shows unhealthy and returns false" do
63
- expect{subject.show_node_health(node_health)}.to return_and_output false, [
64
- ":error Node node-1 is unhealthy",
65
- ]
66
- end
113
+ before do
114
+ allow(client).to receive(:get).with('grids/test-grid/nodes').and_return(
115
+ 'nodes' => grid_nodes,
116
+ )
67
117
  end
68
118
 
69
- context "For a healthy node" do
70
- let :node_health do
119
+ it 'shows all nodes and exits with an error' do
120
+ expect{subject.run([])}.to exit_with_error.and output_lines [
121
+ ":offline Node node-1 is offline",
122
+ ":error Node node-2 is unhealthy: timeout",
123
+ ":error Node node-3 is unhealthy",
124
+ ":ok Node node-4 is healthy",
125
+ ]
126
+ end
127
+ end
128
+
129
+ context 'for a grid with healthy nodes' do
130
+ let :grid_nodes do
131
+ [
71
132
  {
72
- "name" => "node-1",
73
- "connected" => true,
74
- 'etcd_health' => {
75
- 'health' => true,
76
- 'error' => nil,
77
- },
78
- }
79
- end
133
+ 'id' => 'test-grid/node-4',
134
+ },
135
+ {
136
+ 'id' => 'test-grid/node-4',
137
+ },
138
+ ]
139
+ end
80
140
 
141
+ before do
142
+ allow(client).to receive(:get).with('grids/test-grid/nodes').and_return(
143
+ 'nodes' => grid_nodes,
144
+ )
145
+ end
81
146
 
82
- it "shows healthy and returns true" do
83
- expect{subject.show_node_health(node_health)}.to return_and_output true, [
84
- ":ok Node node-1 is healthy",
85
- ]
86
- end
147
+ it 'shows nodes as healthy anddoes not and exit with an error' do
148
+ expect{subject.run([])}.to output_lines [
149
+ ":ok Node node-4 is healthy",
150
+ ":ok Node node-4 is healthy",
151
+ ]
87
152
  end
88
153
  end
89
154
  end