kontena-cli 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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