kontena-cli 0.7.3 → 0.8.0

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 (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