kontena-cli 0.0.1

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.
@@ -0,0 +1,111 @@
1
+ require 'kontena/client'
2
+ require_relative '../common'
3
+
4
+ module Kontena::Cli::Server
5
+ class User
6
+ include Kontena::Cli::Common
7
+
8
+ def login
9
+ require_api_url
10
+ username = ask("Email: ")
11
+ password = password("Password: ")
12
+ params = {
13
+ username: username,
14
+ password: password,
15
+ grant_type: 'password',
16
+ scope: 'user'
17
+ }
18
+
19
+ response = client.post('auth', params)
20
+
21
+ if response
22
+ inifile['server']['token'] = response['access_token']
23
+ inifile.save(filename: ini_filename)
24
+ print color('Login Successful', :green)
25
+ true
26
+ else
27
+ print color('Login Failed', :red)
28
+ false
29
+ end
30
+ end
31
+
32
+ def logout
33
+ inifile['server'].delete('token')
34
+ inifile.save(filename: ini_filename)
35
+ end
36
+
37
+ def whoami
38
+ require_api_url
39
+ puts "Server: #{inifile['server']['url']}"
40
+ token = require_token
41
+ response = client(token).get('user')
42
+ puts "User: #{response['email']}"
43
+ end
44
+
45
+ def invite(email)
46
+ require_api_url
47
+ token = require_token
48
+ data = { email: email }
49
+ response = client(token).post('users', data)
50
+ puts 'User invited' if response
51
+ end
52
+
53
+ def register
54
+ require_api_url
55
+ email = ask("Email: ")
56
+ password = password("Password: ")
57
+ password2 = password("Password again: ")
58
+ if password != password2
59
+ raise ArgumentError.new("Passwords don't match")
60
+ end
61
+ params = {email: email, password: password}
62
+ client.post('users/register', params)
63
+ end
64
+
65
+ def verify_account(token)
66
+ require_api_url
67
+
68
+ params = {token: token}
69
+ client.post('user/email_confirm', params)
70
+ print color('Account verified', :green)
71
+ end
72
+
73
+ def request_password_reset(email)
74
+ require_api_url
75
+
76
+ params = {email: email}
77
+ client.post('user/password_reset', params)
78
+ puts 'Email with password reset instructions is sent to your email address. Please follow the instructions to change your password.'
79
+ end
80
+
81
+ def reset_password(token)
82
+ require_api_url
83
+ password = password("Password: ")
84
+ password2 = password("Password again: ")
85
+ if password != password2
86
+ raise ArgumentError.new("Passwords don't match")
87
+ end
88
+ params = {token: token, password: password}
89
+ client.put('user/password_reset', params)
90
+ puts 'Password is now changed. To login with the new password, please run: kontena login'
91
+ end
92
+
93
+ def add_registry
94
+ default_url = 'https://index.docker.io/v1/'
95
+ require_api_url
96
+ username = ask("Username: ")
97
+ password = password("Password: ")
98
+ email = ask("Email: ")
99
+ url = ask("URL [#{default_url}]: ")
100
+ url = default_url if url.strip == ''
101
+ data = { username: username, password: password, email: email, url: url }
102
+ client(token).post("user/registries", data)
103
+ end
104
+
105
+ private
106
+
107
+ def token
108
+ @token ||= require_token
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,131 @@
1
+ module Kontena::Cli::Services; end;
2
+
3
+ require_relative 'containers'
4
+ require_relative 'logs'
5
+ require_relative 'services'
6
+ require_relative 'stats'
7
+
8
+ command 'service list' do |c|
9
+ c.syntax = 'kontena service list'
10
+ c.description = 'List all services'
11
+ c.action do |args, options|
12
+ Kontena::Cli::Services::Services.new.list
13
+ end
14
+ end
15
+
16
+ command 'service show' do |c|
17
+ c.syntax = 'kontena service show <service_id>'
18
+ c.description = 'Show service details'
19
+ c.action do |args, options|
20
+ Kontena::Cli::Services::Services.new.show(args[0])
21
+ end
22
+ end
23
+
24
+ command 'service deploy' do |c|
25
+ c.syntax = 'kontena service deploy <service_id>'
26
+ c.description = 'Deploy service to nodes'
27
+ c.action do |args, options|
28
+ Kontena::Cli::Services::Services.new.deploy(args[0])
29
+ end
30
+ end
31
+
32
+ command 'service restart' do |c|
33
+ c.syntax = 'kontena service restart <service_id>'
34
+ c.description = 'Restart service containers'
35
+ c.action do |args, options|
36
+ Kontena::Cli::Services::Services.new.restart(args[0])
37
+ end
38
+ end
39
+
40
+ command 'service stop' do |c|
41
+ c.syntax = 'kontena service stop <service_id>'
42
+ c.description = 'Stop service containers'
43
+ c.action do |args, options|
44
+ Kontena::Cli::Services::Services.new.stop(args[0])
45
+ end
46
+ end
47
+
48
+ command 'service start' do |c|
49
+ c.syntax = 'kontena service start <service_id>'
50
+ c.description = 'Start service containers'
51
+ c.action do |args, options|
52
+ Kontena::Cli::Services::Services.new.start(args[0])
53
+ end
54
+ end
55
+
56
+ command 'service create' do |c|
57
+ c.syntax = 'kontena service create <name> <image>'
58
+ c.description = 'Create new service'
59
+ c.option '-p', '--ports Array', Array, 'Publish a service\'s port to the host'
60
+ c.option '-e', '--env Array', Array, 'Set environment variables'
61
+ c.option '-l', '--link Array', Array, 'Add link to another service in the form of name:alias'
62
+ c.option '-v', '--volume Array', Array, 'Mount a volume'
63
+ c.option '--volumes_from Array', Array, 'Mount volumes from another container'
64
+ c.option '-a', '--affinity Array', Array, 'Set service affinity'
65
+ c.option '-c', '--cpu-shares INTEGER', Integer, 'CPU shares (relative weight)'
66
+ c.option '-m', '--memory INTEGER', String, 'Memory limit (format: <number><optional unit>, where unit = b, k, m or g)'
67
+ c.option '--memory-swap INTEGER', String, 'Total memory usage (memory + swap), set \'-1\' to disable swap (format: <number><optional unit>, where unit = b, k, m or g)'
68
+ c.option '--cmd STRING', String, 'Command to execute'
69
+ c.option '--instances INTEGER', Integer, 'How many instances should be deployed'
70
+ c.option '-u', '--user String', String, 'Username who executes first process inside container'
71
+ c.option '--stateful', 'Set service as stateful'
72
+
73
+ c.action do |args, options|
74
+ Kontena::Cli::Services::Services.new.create(args[0], args[1], options)
75
+ end
76
+ end
77
+
78
+ command 'service update' do |c|
79
+ c.syntax = 'kontena service update <service_id>'
80
+ c.description = 'Update service'
81
+ c.option '-p', '--ports Array', Array, 'Exposed ports'
82
+ c.option '-e', '--env Array', Array, 'Environment variables'
83
+ c.option '--image STRING', String, 'Service image'
84
+ c.option '--instances INTEGER', Integer, 'How many instances should be deployed'
85
+ c.option '--cmd STRING', String, 'Command to execute'
86
+ c.action do |args, options|
87
+ Kontena::Cli::Services::Services.new.update(args[0], options)
88
+ end
89
+ end
90
+
91
+ command 'service scale' do |c|
92
+ c.syntax = 'kontena service scale <service_id> <instances>'
93
+ c.description = 'Scale service horizontally'
94
+ c.action do |args, options|
95
+ Kontena::Cli::Services::Services.new.scale(args[0], args[1])
96
+ end
97
+ end
98
+
99
+ command 'service delete' do |c|
100
+ c.syntax = 'kontena service delete <service_id>'
101
+ c.description = 'Delete service'
102
+ c.action do |args, options|
103
+ Kontena::Cli::Services::Services.new.destroy(args[0])
104
+ end
105
+ end
106
+
107
+ command 'service containers' do |c|
108
+ c.syntax = 'kontena service containers <service_id>'
109
+ c.description = 'Show service containers'
110
+ c.action do |args, options|
111
+ Kontena::Cli::Services::Containers.new.list(args[0])
112
+ end
113
+ end
114
+
115
+ command 'service logs' do |c|
116
+ c.syntax = 'kontena service logs <service_id>'
117
+ c.description = 'Show service logs'
118
+ c.option '-f', '--follow', 'Follow logs in real time'
119
+ c.action do |args, options|
120
+ Kontena::Cli::Services::Logs.new.show(args[0], options)
121
+ end
122
+ end
123
+
124
+ command 'service stats' do |c|
125
+ c.syntax = 'kontena service stats <name>'
126
+ c.description = 'Show service stats'
127
+ c.option '-f', '--follow', 'Follow stats in real time'
128
+ c.action do |args, options|
129
+ Kontena::Cli::Services::Stats.new.show(args[0], options)
130
+ end
131
+ end
@@ -0,0 +1,24 @@
1
+ require 'kontena/client'
2
+ require_relative '../common'
3
+
4
+ module Kontena::Cli::Services
5
+ class Containers
6
+ include Kontena::Cli::Common
7
+
8
+ ##
9
+ # @param [String] service_id
10
+ def list(service_id)
11
+ require_api_url
12
+ token = require_token
13
+
14
+ result = client(token).get("services/#{service_id}/containers")
15
+ result['containers'].each do |container|
16
+ puts "#{container['id']}:"
17
+ puts " node: #{container['node']['name']}"
18
+ puts " ip (internal): #{container['network_settings']['ip_address']}"
19
+ puts " status: #{container['status']}"
20
+ puts ""
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ require 'kontena/client'
2
+ require_relative '../common'
3
+
4
+ module Kontena::Cli::Services
5
+ class Logs
6
+ include Kontena::Cli::Common
7
+
8
+ ##
9
+ # @param [String] service_id
10
+ def show(service_id, options)
11
+ require_api_url
12
+ token = require_token
13
+ last_id = nil
14
+ loop do
15
+ query_params = last_id.nil? ? '' : "from=#{last_id}"
16
+ result = client(token).get("services/#{service_id}/container_logs?#{query_params}")
17
+ result['logs'].each do |log|
18
+ puts log['data']
19
+ last_id = log['id']
20
+ end
21
+ break unless options.follow
22
+ sleep(2)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,196 @@
1
+ require 'kontena/client'
2
+ require_relative '../common'
3
+
4
+ module Kontena::Cli::Services
5
+ class Services
6
+ include Kontena::Cli::Common
7
+
8
+ def list
9
+ require_api_url
10
+ token = require_token
11
+
12
+ grids = client(token).get("grids/#{current_grid}/services")
13
+ puts "%-30.30s %-40.40s %-15s %-8s" % ['NAME', 'IMAGE', 'INSTANCES', 'STATE?']
14
+ grids['services'].each do |service|
15
+ state = service['stateful'] ? 'yes' : 'no'
16
+ puts "%-30.30s %-40.40s %-15.15s %-8s" % [service['id'], service['image'], service['container_count'], state]
17
+ end
18
+ end
19
+
20
+ def show(service_id)
21
+ require_api_url
22
+ token = require_token
23
+
24
+ service = client(token).get("services/#{service_id}")
25
+ puts "#{service['id']}:"
26
+ puts " status: #{service['state'] }"
27
+ puts " stateful: #{service['stateful'] == true ? 'yes' : 'no' }"
28
+ puts " scaling: #{service['container_count'] }"
29
+ puts " image: #{service['image']}"
30
+ if service['cmd']
31
+ puts " cmd: #{service['cmd'].join(' ')}"
32
+ else
33
+ puts " cmd: -"
34
+ end
35
+
36
+ puts " env: "
37
+ if service['env']
38
+ service['env'].each{|e| puts " - #{e}"}
39
+ end
40
+ puts " ports:"
41
+ service['ports'].each do |p|
42
+ puts " - #{p['node_port']}:#{p['container_port']}/#{p['protocol']}"
43
+ end
44
+ puts " links: "
45
+ if service['links']
46
+ service['links'].each do |p|
47
+ puts " - #{p['alias']}"
48
+ end
49
+ end
50
+ puts " containers:"
51
+ result = client(token).get("services/#{service_id}/containers")
52
+ result['containers'].each do |container|
53
+ puts " #{container['id']}:"
54
+ puts " rev: #{container['deploy_rev']}"
55
+ puts " node: #{container['node']['name']}"
56
+ puts " dns: #{container['id']}.kontena.local"
57
+ puts " ip: #{container['network_settings']['ip_address']}"
58
+ if container['status'] == 'unknown'
59
+ puts " status: #{container['status'].colorize(:yellow)}"
60
+ else
61
+ puts " status: #{container['status']}"
62
+ end
63
+ end
64
+ end
65
+
66
+ def scale(service_id, count)
67
+ client(require_token).put("services/#{service_id}", {container_count: count})
68
+ self.deploy(service_id)
69
+ end
70
+
71
+ def deploy(service_id)
72
+ require_api_url
73
+ token = require_token
74
+
75
+ result = client(token).post("services/#{service_id}/deploy", {})
76
+
77
+ print 'deploying '
78
+ until client(token).get("services/#{service_id}")['state'] != 'deploying' do
79
+ print '.'
80
+ sleep 1
81
+ end
82
+ puts ' done'
83
+ puts ''
84
+ self.show(service_id)
85
+ end
86
+
87
+ def restart(service_id)
88
+ require_api_url
89
+ token = require_token
90
+
91
+ result = client(token).post("services/#{service_id}/restart", {})
92
+ end
93
+
94
+ def stop(service_id)
95
+ require_api_url
96
+ token = require_token
97
+
98
+ result = client(token).post("services/#{service_id}/stop", {})
99
+ end
100
+
101
+ def start(service_id)
102
+ require_api_url
103
+ token = require_token
104
+
105
+ result = client(token).post("services/#{service_id}/start", {})
106
+ end
107
+
108
+ def create(name, image, options)
109
+ require_api_url
110
+ token = require_token
111
+ if options.ports
112
+ ports = parse_ports(options.ports)
113
+ end
114
+ data = {
115
+ name: name,
116
+ image: image,
117
+ stateful: !!options.stateful
118
+ }
119
+ if options.link
120
+ links = parse_links(options.link)
121
+ end
122
+ data[:ports] = ports if options.ports
123
+ data[:links] = links if options.link
124
+ data[:volumes] = options.volume if options.volume
125
+ data[:volumes_from] = options.volumes_from if options.volumes_from
126
+ data[:memory] = parse_memory(options.memory) if options.memory
127
+ data[:memory_swap] = parse_memory(options.memory_swap) if options.memory_swap
128
+ data[:cpu_shares] = options.cpu_shares if options.cpu_shares
129
+ data[:affinity] = options.affinity if options.affinity
130
+ data[:env] = options.env if options.env
131
+ data[:container_count] = options.instances if options.instances
132
+ data[:cmd] = options.cmd.split(" ") if options.cmd
133
+ data[:user] = options.user if options.user
134
+ data[:cpu] = options.cpu if options.cpu
135
+ if options.memory
136
+ memory = human_size_to_number(options.memory)
137
+ raise ArgumentError.new('Invalid --memory')
138
+ data[:memory] = memory
139
+ end
140
+ data[:memory] = options.memory if options.memory
141
+ client(token).post("grids/#{current_grid}/services", data)
142
+ end
143
+
144
+ def update(service_id, options)
145
+ require_api_url
146
+ token = require_token
147
+
148
+ data = {}
149
+ data[:env] = options.env if options.env
150
+ data[:container_count] = options.instances if options.instances
151
+ data[:cmd] = options.cmd.split(" ") if options.cmd
152
+ data[:ports] = parse_ports(options.ports) if options.ports
153
+ data[:image] = options.image if options.image
154
+
155
+ client(require_token).put("services/#{service_id}", data)
156
+ end
157
+
158
+ def destroy(service_id)
159
+ require_api_url
160
+ token = require_token
161
+
162
+ result = client(token).delete("services/#{service_id}")
163
+ end
164
+
165
+ private
166
+ def current_grid
167
+ inifile['server']['grid']
168
+ end
169
+
170
+ def parse_ports(port_options)
171
+ port_options.map{|p|
172
+ node_port, container_port = p.split(':')
173
+ if node_port.nil? || container_port.nil?
174
+ raise ArgumentError.new("Invalid port value #{p}")
175
+ end
176
+ {
177
+ container_port: container_port,
178
+ node_port: node_port
179
+ }
180
+ }
181
+ end
182
+
183
+ def parse_links(link_options)
184
+ link_options.map{|l|
185
+ service_name, alias_name = l.split(':')
186
+ if service_name.nil? || alias_name.nil?
187
+ raise ArgumentError.new("Invalid link value #{l}")
188
+ end
189
+ {
190
+ name: service_name,
191
+ alias: alias_name
192
+ }
193
+ }
194
+ end
195
+ end
196
+ end