kontena-cli 0.7.3 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/bin/kontena +64 -2
  4. data/kontena-cli.gemspec +4 -2
  5. data/lib/kontena/cli/app_command.rb +20 -0
  6. data/lib/kontena/cli/apps/common.rb +53 -0
  7. data/lib/kontena/cli/{stacks/stacks.rb → apps/deploy_command.rb} +17 -31
  8. data/lib/kontena/cli/apps/init_command.rb +101 -0
  9. data/lib/kontena/cli/apps/list_command.rb +41 -0
  10. data/lib/kontena/cli/apps/logs_command.rb +58 -0
  11. data/lib/kontena/cli/apps/remove_command.rb +64 -0
  12. data/lib/kontena/cli/apps/start_command.rb +38 -0
  13. data/lib/kontena/cli/apps/stop_command.rb +38 -0
  14. data/lib/kontena/cli/container_command.rb +9 -0
  15. data/lib/kontena/cli/containers/{containers.rb → exec_command.rb} +5 -5
  16. data/lib/kontena/cli/deploy_command.rb +159 -0
  17. data/lib/kontena/cli/external_registries/add_command.rb +22 -0
  18. data/lib/kontena/cli/external_registries/delete_command.rb +13 -0
  19. data/lib/kontena/cli/external_registries/list_command.rb +16 -0
  20. data/lib/kontena/cli/external_registry_command.rb +14 -0
  21. data/lib/kontena/cli/forgot_password_command.rb +13 -0
  22. data/lib/kontena/cli/grid_command.rb +27 -0
  23. data/lib/kontena/cli/grids/add_user_command.rb +17 -0
  24. data/lib/kontena/cli/grids/{audit_log.rb → audit_log_command.rb} +7 -6
  25. data/lib/kontena/cli/grids/common.rb +24 -0
  26. data/lib/kontena/cli/grids/create_command.rb +26 -0
  27. data/lib/kontena/cli/grids/current_command.rb +18 -0
  28. data/lib/kontena/cli/grids/list_command.rb +26 -0
  29. data/lib/kontena/cli/grids/list_users_command.rb +18 -0
  30. data/lib/kontena/cli/grids/remove_command.rb +26 -0
  31. data/lib/kontena/cli/grids/remove_user_command.rb +16 -0
  32. data/lib/kontena/cli/grids/show_command.rb +19 -0
  33. data/lib/kontena/cli/grids/use_command.rb +21 -0
  34. data/lib/kontena/cli/invite_command.rb +13 -0
  35. data/lib/kontena/cli/login_command.rb +114 -0
  36. data/lib/kontena/cli/logout_command.rb +8 -0
  37. data/lib/kontena/cli/node_command.rb +21 -0
  38. data/lib/kontena/cli/nodes/digital_ocean/create_command.rb +31 -0
  39. data/lib/kontena/cli/nodes/digital_ocean/restart_command.rb +26 -0
  40. data/lib/kontena/cli/nodes/digital_ocean/terminate_command.rb +18 -0
  41. data/lib/kontena/cli/nodes/digital_ocean_command.rb +15 -0
  42. data/lib/kontena/cli/nodes/list_command.rb +28 -0
  43. data/lib/kontena/cli/nodes/remove_command.rb +15 -0
  44. data/lib/kontena/cli/nodes/show_command.rb +31 -0
  45. data/lib/kontena/cli/nodes/update_command.rb +18 -0
  46. data/lib/kontena/cli/nodes/vagrant/create_command.rb +26 -0
  47. data/lib/kontena/cli/nodes/vagrant/restart_command.rb +25 -0
  48. data/lib/kontena/cli/nodes/vagrant/ssh_command.rb +20 -0
  49. data/lib/kontena/cli/nodes/vagrant/start_command.rb +25 -0
  50. data/lib/kontena/cli/nodes/vagrant/stop_command.rb +25 -0
  51. data/lib/kontena/cli/nodes/vagrant/terminate_command.rb +16 -0
  52. data/lib/kontena/cli/nodes/vagrant_command.rb +21 -0
  53. data/lib/kontena/cli/register_command.rb +21 -0
  54. data/lib/kontena/cli/{grids/registry.rb → registry/create_command.rb} +32 -35
  55. data/lib/kontena/cli/registry/delete_command.rb +15 -0
  56. data/lib/kontena/cli/registry_command.rb +11 -0
  57. data/lib/kontena/cli/reset_password_command.rb +17 -0
  58. data/lib/kontena/cli/service_command.rb +33 -0
  59. data/lib/kontena/cli/services/container_command.rb +9 -0
  60. data/lib/kontena/cli/services/containers_command.rb +31 -0
  61. data/lib/kontena/cli/services/create_command.rb +62 -0
  62. data/lib/kontena/cli/services/delete_command.rb +17 -0
  63. data/lib/kontena/cli/services/deploy_command.rb +23 -0
  64. data/lib/kontena/cli/services/list_command.rb +20 -0
  65. data/lib/kontena/cli/services/logs_command.rb +51 -0
  66. data/lib/kontena/cli/services/restart_command.rb +16 -0
  67. data/lib/kontena/cli/services/scale_command.rb +20 -0
  68. data/lib/kontena/cli/services/services_helper.rb +94 -0
  69. data/lib/kontena/cli/services/show_command.rb +17 -0
  70. data/lib/kontena/cli/services/start_command.rb +16 -0
  71. data/lib/kontena/cli/services/{stats.rb → stats_command.rb} +11 -10
  72. data/lib/kontena/cli/services/stop_command.rb +16 -0
  73. data/lib/kontena/cli/services/update_command.rb +51 -0
  74. data/lib/kontena/cli/verify_account_command.rb +13 -0
  75. data/lib/kontena/cli/version_command.rb +8 -0
  76. data/lib/kontena/cli/vpn/config_command.rb +12 -0
  77. data/lib/kontena/cli/{grids/vpn.rb → vpn/create_command.rb} +12 -29
  78. data/lib/kontena/cli/vpn/delete_command.rb +15 -0
  79. data/lib/kontena/cli/vpn_command.rb +13 -0
  80. data/lib/kontena/cli/whoami_command.rb +19 -0
  81. data/lib/kontena/client.rb +14 -11
  82. data/lib/kontena/machine/common.rb +17 -0
  83. data/lib/kontena/machine/digital_ocean.rb +11 -0
  84. data/lib/kontena/machine/digital_ocean/cloudinit.yml +66 -0
  85. data/lib/kontena/machine/digital_ocean/node_destroyer.rb +38 -0
  86. data/lib/kontena/machine/digital_ocean/node_provisioner.rb +74 -0
  87. data/lib/kontena/machine/random_name.rb +42 -0
  88. data/lib/kontena/machine/vagrant.rb +10 -0
  89. data/lib/kontena/machine/vagrant/Vagrantfile.coreos.rb.erb +32 -0
  90. data/lib/kontena/machine/vagrant/cloudinit.yml +65 -0
  91. data/lib/kontena/machine/vagrant/node_destroyer.rb +36 -0
  92. data/lib/kontena/machine/vagrant/node_provisioner.rb +68 -0
  93. data/lib/kontena/scripts/completer +5 -5
  94. data/spec/kontena/cli/app/deploy_command_spec.rb +227 -0
  95. data/spec/kontena/cli/deploy_command_spec.rb +213 -0
  96. data/spec/kontena/cli/login_command_spec.rb +22 -0
  97. data/spec/kontena/cli/register_command_spec.rb +57 -0
  98. data/spec/spec_helper.rb +5 -1
  99. metadata +132 -36
  100. data/lib/kontena/cli/commands.rb +0 -20
  101. data/lib/kontena/cli/containers/commands.rb +0 -12
  102. data/lib/kontena/cli/grids/commands.rb +0 -169
  103. data/lib/kontena/cli/grids/external_registries.rb +0 -40
  104. data/lib/kontena/cli/grids/grids.rb +0 -108
  105. data/lib/kontena/cli/grids/users.rb +0 -32
  106. data/lib/kontena/cli/nodes/commands.rb +0 -27
  107. data/lib/kontena/cli/nodes/nodes.rb +0 -64
  108. data/lib/kontena/cli/server/commands.rb +0 -69
  109. data/lib/kontena/cli/server/server.rb +0 -45
  110. data/lib/kontena/cli/server/user.rb +0 -174
  111. data/lib/kontena/cli/services/commands.rb +0 -138
  112. data/lib/kontena/cli/services/containers.rb +0 -24
  113. data/lib/kontena/cli/services/logs.rb +0 -44
  114. data/lib/kontena/cli/services/services.rb +0 -175
  115. data/lib/kontena/cli/stacks/commands.rb +0 -13
  116. data/spec/kontena/cli/server/user_spec.rb +0 -59
  117. data/spec/kontena/cli/stacks/stacks_spec.rb +0 -212
@@ -0,0 +1,64 @@
1
+ require 'yaml'
2
+ require_relative 'common'
3
+
4
+ module Kontena::Cli::Apps
5
+ class RemoveCommand < Clamp::Command
6
+ include Kontena::Cli::Common
7
+ include Common
8
+
9
+ option ['-f', '--file'], 'FILE', 'Specify an alternate Kontena compose file', attribute_name: :filename, default: 'kontena.yml'
10
+ option ['-p', '--project-name'], 'NAME', 'Specify an alternate project name (default: directory name)'
11
+
12
+ parameter "[SERVICE] ...", "Remove services"
13
+
14
+ attr_reader :services, :service_prefix
15
+
16
+ def execute
17
+ require_api_url
18
+ require_token
19
+
20
+ @services = load_services_from_yml
21
+ if services.size > 0
22
+ Dir.chdir(File.dirname(filename))
23
+ remove_services(services)
24
+ elsif !service_list.empty?
25
+ puts "No such service: #{service_list.join(', ')}".colorize(:red)
26
+ end
27
+
28
+ end
29
+
30
+ def remove_services(services)
31
+ services.find_all {|service_name, options| options['links'] && options['links'].size > 0 }.each do |service_name, options|
32
+ delete(service_name, options)
33
+ services.delete(service_name)
34
+ end
35
+ services.each do |service_name, options|
36
+ delete(service_name, options)
37
+ end
38
+ end
39
+
40
+ def delete(name, options)
41
+ unless deleted_services.include?(name)
42
+ print "deleting #{name}"
43
+ service = get_service(token, prefixed_name(name)) rescue nil
44
+
45
+ if(!service.nil?)
46
+ print "."
47
+ delete_service(token, prefixed_name(name))
48
+ print ". done"
49
+ deleted_services << name
50
+ puts ''
51
+ else
52
+ puts "No such service: #{name}".colorize(:red)
53
+ end
54
+ end
55
+ end
56
+
57
+ ##
58
+ #
59
+ # @return [Array]
60
+ def deleted_services
61
+ @deleted_services ||= []
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,38 @@
1
+ require 'yaml'
2
+ require_relative 'common'
3
+
4
+ module Kontena::Cli::Apps
5
+ class StartCommand < Clamp::Command
6
+ include Kontena::Cli::Common
7
+ include Common
8
+
9
+ option ['-f', '--file'], 'FILE', 'Specify an alternate Kontena compose file', attribute_name: :filename, default: 'kontena.yml'
10
+ option ['-p', '--project-name'], 'NAME', 'Specify an alternate project name (default: directory name)'
11
+
12
+ parameter "[SERVICE] ...", "Services to start"
13
+
14
+ attr_reader :services, :service_prefix
15
+
16
+ def execute
17
+ @services = load_services_from_yml
18
+ if services.size > 0
19
+ Dir.chdir(File.dirname(filename))
20
+ start_services(services)
21
+ elsif !service_list.empty?
22
+ puts "No such service: #{service_list.join(', ')}".colorize(:red)
23
+ end
24
+
25
+ end
26
+
27
+ def start_services(services)
28
+ services.each do |service_name, opts|
29
+ if service_exists?(service_name)
30
+ puts "starting #{prefixed_name(service_name)}"
31
+ start_service(token, prefixed_name(service_name))
32
+ else
33
+ puts "No such service: #{service_name}".colorize(:red)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ require 'yaml'
2
+ require_relative 'common'
3
+
4
+ module Kontena::Cli::Apps
5
+ class StopCommand < Clamp::Command
6
+ include Kontena::Cli::Common
7
+ include Common
8
+
9
+ option ['-f', '--file'], 'FILE', 'Specify an alternate Kontena compose file', attribute_name: :filename, default: 'kontena.yml'
10
+ option ['-p', '--project-name'], 'NAME', 'Specify an alternate project name (default: directory name)'
11
+
12
+ parameter "[SERVICE] ...", "Services to stop"
13
+
14
+ attr_reader :services, :service_prefix
15
+
16
+ def execute
17
+ @services = load_services_from_yml
18
+ if services.size > 0
19
+ Dir.chdir(File.dirname(filename))
20
+ stop_services(services)
21
+ elsif !service_list.empty?
22
+ puts "No such service: #{service_list.join(', ')}".colorize(:red)
23
+ end
24
+
25
+ end
26
+
27
+ def stop_services(services)
28
+ services.each do |service_name, opts|
29
+ if service_exists?(service_name)
30
+ puts "stopping #{prefixed_name(service_name)}"
31
+ stop_service(token, prefixed_name(service_name))
32
+ else
33
+ puts "No such service: #{service_name}".colorize(:red)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,9 @@
1
+ require_relative 'containers/exec_command'
2
+
3
+ class Kontena::Cli::ContainerCommand < Clamp::Command
4
+
5
+ subcommand "exec", "Execute command inside a container", Kontena::Cli::Containers::ExecCommand
6
+
7
+ def execute
8
+ end
9
+ end
@@ -1,11 +1,11 @@
1
- require 'kontena/client'
2
- require_relative '../common'
3
-
4
1
  module Kontena::Cli::Containers
5
- class Containers
2
+ class ExecCommand < Clamp::Command
6
3
  include Kontena::Cli::Common
7
4
 
8
- def exec(container_id, cmd)
5
+ parameter "CONTAINER_ID", "Container id"
6
+ parameter "CMD ...", "Command"
7
+
8
+ def execute
9
9
  require_api_url
10
10
  token = require_token
11
11
 
@@ -0,0 +1,159 @@
1
+ require 'yaml'
2
+ require_relative 'services/services_helper'
3
+
4
+ class Kontena::Cli::DeployCommand < Clamp::Command
5
+ include Kontena::Cli::Common
6
+ include Kontena::Cli::Services::ServicesHelper
7
+
8
+ option ['-f', '--file'], 'FILE', 'Path to kontena.yml file, default: current directory'
9
+ option ['-p', '--prefix'], 'PREFIX', 'Prefix of service names, default: name of the current directory'
10
+ option ['-s', '--service'], 'SERVICE', 'Services to deploy', multivalued: true
11
+
12
+ attr_reader :services, :service_prefix
13
+
14
+ def execute
15
+ puts "DEPRECATION WARNING: Support for 'kontena deploy' will be dropped. Use 'kontena app deploy' instead.".colorize(:red)
16
+ require_api_url
17
+ require_token
18
+
19
+ filename = file || './kontena.yml'
20
+ @service_prefix = prefix || current_dir
21
+
22
+ abort("File #{filename} does not exist") unless File.exists?(filename)
23
+
24
+ @services = YAML.load(File.read(filename) % {prefix: service_prefix})
25
+ @services = @services.delete_if { |name, service| !service_list.include?(name)} unless service_list.empty?
26
+
27
+ Dir.chdir(File.dirname(filename))
28
+ init_services(services)
29
+ deploy_services(deploy_queue)
30
+ end
31
+
32
+ private
33
+
34
+ def init_services(services)
35
+ services.each do |name, config|
36
+ create_or_update_service(name, config)
37
+ end
38
+ end
39
+
40
+ def deploy_services(queue)
41
+ queue.each do |service|
42
+ puts "deploying #{service['id']}"
43
+ data = {}
44
+ if service['deploy']
45
+ data[:strategy] = service['deploy']['strategy'] if service['deploy']['strategy']
46
+ data[:wait_for_port] = service['deploy']['wait_for_port'] if service['deploy']['wait_for_port']
47
+ end
48
+ deploy_service(token, service['id'].split('/').last, data)
49
+ end
50
+ end
51
+
52
+ def create_or_update_service(name, options)
53
+
54
+ # skip if service is already created or updated or it's not present
55
+ return nil if in_deploy_queue?(name) || !services.keys.include?(name)
56
+
57
+ # create/update linked services recursively before continuing
58
+ unless options['links'].nil?
59
+ parse_links(options['links']).each_with_index do |linked_service, index|
60
+ # change prefixed service name also to links options
61
+ options['links'][index] = "#{prefixed_name(linked_service[:name])}:#{linked_service[:alias]}"
62
+
63
+ create_or_update_service(linked_service[:name], services[linked_service[:name]]) unless in_deploy_queue?(linked_service[:name])
64
+ end
65
+ end
66
+
67
+ merge_env_vars(options)
68
+
69
+ if find_service_by_name(name)
70
+ service = update(name, options)
71
+ else
72
+ service = create(name, options)
73
+ end
74
+
75
+ # add deploy options to service
76
+ service['deploy'] = options['deploy']
77
+
78
+ deploy_queue.push service
79
+ end
80
+
81
+ def find_service_by_name(name)
82
+ get_service(token, prefixed_name(name)) rescue nil
83
+ end
84
+
85
+ def create(name, options)
86
+ name = prefixed_name(name)
87
+ puts "creating #{name}"
88
+ data = {name: name}
89
+ data.merge!(parse_data(options))
90
+ create_service(token, current_grid, data)
91
+ end
92
+
93
+ def update(id, options)
94
+ id = prefixed_name(id)
95
+ data = parse_data(options)
96
+ puts "updating #{id}"
97
+ update_service(token, id, data)
98
+ end
99
+
100
+ def in_deploy_queue?(name)
101
+ deploy_queue.find {|service| service['id'] == prefixed_name(name)} != nil
102
+ end
103
+
104
+ def prefixed_name(name)
105
+ "#{service_prefix}-#{name}"
106
+ end
107
+
108
+ def current_dir
109
+ File.basename(Dir.getwd)
110
+ end
111
+
112
+ def merge_env_vars(options)
113
+ return unless options['env_file']
114
+
115
+ options['env_file'] = [options['env_file']] if options['env_file'].is_a?(String)
116
+ options['environment'] = [] unless options['environment']
117
+
118
+ options['env_file'].each do |env_file|
119
+ options['environment'].concat(read_env_file(env_file))
120
+ end
121
+
122
+ options['environment'].uniq! {|s| s.split('=').first}
123
+ end
124
+
125
+ def read_env_file(path)
126
+ File.readlines(path).delete_if { |line| line.start_with?('#') || line.empty? }
127
+ end
128
+
129
+ ##
130
+ # @param [Hash] options
131
+ def parse_data(options)
132
+ data = {}
133
+ data[:image] = options['image']
134
+ data[:env] = options['environment']
135
+ data[:container_count] = options['instances']
136
+ data[:links] = parse_links(options['links']) if options['links']
137
+ data[:ports] = parse_ports(options['ports']) if options['ports']
138
+ data[:memory] = parse_memory(options['mem_limit']) if options['mem_limit']
139
+ data[:memory_swap] = parse_memory(options['memswap_limit']) if options['memswap_limit']
140
+ data[:cpu_shares] = options['cpu_shares'] if options['cpu_shares']
141
+ data[:volumes] = options['volumes'] if options['volumes']
142
+ data[:volumes_from] = options['volumes_from'] if options['volumes_from']
143
+ data[:cmd] = options['command'].split(" ") if options['command']
144
+ data[:affinity] = options['affinity'] if options['affinity']
145
+ data[:user] = options['user'] if options['user']
146
+ data[:stateful] = options['stateful'] == true
147
+ data[:cap_add] = options['cap_add'] if options['cap_add']
148
+ data[:cap_drop] = options['cap_drop'] if options['cap_drop']
149
+ data
150
+ end
151
+
152
+ def token
153
+ @token ||= require_token
154
+ end
155
+
156
+ def deploy_queue
157
+ @deploy_queue ||= []
158
+ end
159
+ end
@@ -0,0 +1,22 @@
1
+ module Kontena::Cli::ExternalRegistries
2
+ class AddCommand < Clamp::Command
3
+ include Kontena::Cli::Common
4
+
5
+ def execute
6
+ require 'highline/import'
7
+
8
+ default_url = 'https://index.docker.io/v1/'
9
+ require_api_url
10
+ require_current_grid
11
+ token = require_token
12
+
13
+ username = ask("Username: ")
14
+ password = ask("Password: ") { |q| q.echo = "*" }
15
+ email = ask("Email: ")
16
+ url = ask("URL [#{default_url}]: ")
17
+ url = default_url if url.strip == ''
18
+ data = { username: username, password: password, email: email, url: url }
19
+ client(token).post("grids/#{current_grid}/external_registries", data)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ module Kontena::Cli::ExternalRegistries
2
+ class DeleteCommand < Clamp::Command
3
+ include Kontena::Cli::Common
4
+
5
+ parameter "NAME", "External registry name to delete"
6
+
7
+ def execute
8
+ require_api_url
9
+ token = require_token
10
+ client(token).delete("external_registries/#{current_grid}/#{name}")
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ module Kontena::Cli::ExternalRegistries
2
+ class ListCommand < Clamp::Command
3
+ include Kontena::Cli::Common
4
+
5
+ def execute
6
+ require_api_url
7
+ require_current_grid
8
+ token = require_token
9
+ result = client(token).get("grids/#{current_grid}/external_registries")
10
+ puts "%-30s %-20s %-30s" % ['Name', 'Username', 'Email']
11
+ result['external_registries'].each { |r|
12
+ puts "%-30.30s %-20.20s %-30.30s" % [r['name'], r['username'], r['email']]
13
+ }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ require_relative 'external_registries/add_command'
2
+ require_relative 'external_registries/list_command'
3
+ require_relative 'external_registries/delete_command'
4
+
5
+
6
+ class Kontena::Cli::ExternalRegistryCommand < Clamp::Command
7
+
8
+ subcommand "add", "Add external Docker image registry", Kontena::Cli::ExternalRegistries::AddCommand
9
+ subcommand "list", "List external Docker image registries", Kontena::Cli::ExternalRegistries::ListCommand
10
+ subcommand "delete", "Delete external Docker image registry", Kontena::Cli::ExternalRegistries::DeleteCommand
11
+
12
+ def execute
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ class Kontena::Cli::ForgotPasswordCommand < Clamp::Command
2
+ include Kontena::Cli::Common
3
+
4
+ parameter "EMAIL", "Email address"
5
+
6
+ def execute
7
+ require_api_url
8
+
9
+ params = {email: email}
10
+ client.post('user/password_reset', params)
11
+ puts 'Email with password reset instructions is sent to your email address. Please follow the instructions to change your password.'
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ require_relative 'grids/list_command'
2
+ require_relative 'grids/create_command'
3
+ require_relative 'grids/use_command'
4
+ require_relative 'grids/show_command'
5
+ require_relative 'grids/remove_command'
6
+ require_relative 'grids/current_command'
7
+ require_relative 'grids/audit_log_command'
8
+ require_relative 'grids/list_users_command'
9
+ require_relative 'grids/add_user_command'
10
+ require_relative 'grids/remove_user_command'
11
+
12
+ class Kontena::Cli::GridCommand < Clamp::Command
13
+
14
+ subcommand "list", "List all grids", Kontena::Cli::Grids::ListCommand
15
+ subcommand "create", "Create a new grid", Kontena::Cli::Grids::CreateCommand
16
+ subcommand "use", "Switch to use specific grid", Kontena::Cli::Grids::UseCommand
17
+ subcommand "show", "Show grid details", Kontena::Cli::Grids::ShowCommand
18
+ subcommand "remove", "Remove a grid", Kontena::Cli::Grids::RemoveCommand
19
+ subcommand "current", "Show current grid details", Kontena::Cli::Grids::CurrentCommand
20
+ subcommand "audit-log", "Show audit log of the current grid", Kontena::Cli::Grids::AuditLogCommand
21
+ subcommand "list-users", "List current grid users", Kontena::Cli::Grids::ListUsersCommand
22
+ subcommand "add-user", "Add user to the current grid", Kontena::Cli::Grids::AddUserCommand
23
+ subcommand "remove-user", "Remove user from the current grid", Kontena::Cli::Grids::RemoveUserCommand
24
+
25
+ def execute
26
+ end
27
+ end