kontena-cli 0.8.4 → 0.9.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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/bin/kontena +4 -0
- data/lib/kontena/cli/app_command.rb +2 -0
- data/lib/kontena/cli/apps/build_command.rb +26 -0
- data/lib/kontena/cli/apps/common.rb +41 -12
- data/lib/kontena/cli/apps/deploy_command.rb +31 -13
- data/lib/kontena/cli/apps/docker_helper.rb +34 -0
- data/lib/kontena/cli/apps/init_command.rb +130 -10
- data/lib/kontena/cli/apps/list_command.rb +4 -2
- data/lib/kontena/cli/apps/logs_command.rb +4 -2
- data/lib/kontena/cli/apps/remove_command.rb +4 -2
- data/lib/kontena/cli/apps/start_command.rb +4 -2
- data/lib/kontena/cli/apps/stop_command.rb +4 -2
- data/lib/kontena/cli/common.rb +3 -3
- data/lib/kontena/cli/container_command.rb +3 -0
- data/lib/kontena/cli/containers/inspect_command.rb +16 -0
- data/lib/kontena/cli/deploy_command.rb +3 -0
- data/lib/kontena/cli/etcd/get_command.rb +21 -0
- data/lib/kontena/cli/etcd/list_command.rb +26 -0
- data/lib/kontena/cli/etcd/mkdir_command.rb +17 -0
- data/lib/kontena/cli/etcd/remove_command.rb +21 -0
- data/lib/kontena/cli/etcd/set_command.rb +18 -0
- data/lib/kontena/cli/etcd_command.rb +17 -0
- data/lib/kontena/cli/grid_command.rb +2 -0
- data/lib/kontena/cli/grids/logs_command.rb +71 -0
- data/lib/kontena/cli/master/aws/create_command.rb +33 -0
- data/lib/kontena/cli/master/aws_command.rb +8 -0
- data/lib/kontena/cli/master/azure/create_command.rb +33 -0
- data/lib/kontena/cli/master/azure_command.rb +13 -0
- data/lib/kontena/cli/master/digital_ocean/create_command.rb +30 -0
- data/lib/kontena/cli/master/digital_ocean_command.rb +13 -0
- data/lib/kontena/cli/master/vagrant/create_command.rb +19 -0
- data/lib/kontena/cli/master/vagrant/restart_command.rb +20 -0
- data/lib/kontena/cli/master/vagrant/ssh_command.rb +15 -0
- data/lib/kontena/cli/master/vagrant/start_command.rb +20 -0
- data/lib/kontena/cli/master/vagrant/stop_command.rb +20 -0
- data/lib/kontena/cli/master/vagrant/terminate_command.rb +13 -0
- data/lib/kontena/cli/master/vagrant_command.rb +23 -0
- data/lib/kontena/cli/master_command.rb +15 -0
- data/lib/kontena/cli/node_command.rb +4 -0
- data/lib/kontena/cli/nodes/aws/create_command.rb +39 -0
- data/lib/kontena/cli/nodes/aws/restart_command.rb +28 -0
- data/lib/kontena/cli/nodes/aws/terminate_command.rb +20 -0
- data/lib/kontena/cli/nodes/aws_command.rb +15 -0
- data/lib/kontena/cli/nodes/azure/create_command.rb +39 -0
- data/lib/kontena/cli/nodes/azure/restart_command.rb +31 -0
- data/lib/kontena/cli/nodes/azure/terminate_command.rb +20 -0
- data/lib/kontena/cli/nodes/azure_command.rb +15 -0
- data/lib/kontena/cli/nodes/digital_ocean/create_command.rb +1 -1
- data/lib/kontena/cli/nodes/vagrant/create_command.rb +1 -1
- data/lib/kontena/cli/service_command.rb +4 -0
- data/lib/kontena/cli/services/add_env_command.rb +18 -0
- data/lib/kontena/cli/services/create_command.rb +8 -0
- data/lib/kontena/cli/services/remove_env_command.rb +17 -0
- data/lib/kontena/cli/services/services_helper.rb +20 -1
- data/lib/kontena/cli/services/update_command.rb +10 -0
- data/lib/kontena/client.rb +22 -1
- data/lib/kontena/machine/aws.rb +13 -0
- data/lib/kontena/machine/aws/cloudinit.yml +66 -0
- data/lib/kontena/machine/aws/cloudinit_master.yml +105 -0
- data/lib/kontena/machine/aws/master_provisioner.rb +161 -0
- data/lib/kontena/machine/aws/node_destroyer.rb +39 -0
- data/lib/kontena/machine/aws/node_provisioner.rb +168 -0
- data/lib/kontena/machine/azure.rb +13 -0
- data/lib/kontena/machine/azure/cloudinit.yml +59 -0
- data/lib/kontena/machine/azure/cloudinit_master.yml +105 -0
- data/lib/kontena/machine/azure/logger.rb +27 -0
- data/lib/kontena/machine/azure/master_provisioner.rb +126 -0
- data/lib/kontena/machine/azure/node_destroyer.rb +53 -0
- data/lib/kontena/machine/azure/node_provisioner.rb +128 -0
- data/lib/kontena/machine/digital_ocean.rb +1 -0
- data/lib/kontena/machine/digital_ocean/cloudinit.yml +1 -0
- data/lib/kontena/machine/digital_ocean/cloudinit_master.yml +105 -0
- data/lib/kontena/machine/digital_ocean/master_provisioner.rb +94 -0
- data/lib/kontena/machine/digital_ocean/node_provisioner.rb +8 -1
- data/lib/kontena/machine/vagrant.rb +2 -0
- data/lib/kontena/machine/vagrant/Vagrantfile.master.rb.erb +101 -0
- data/lib/kontena/machine/vagrant/{Vagrantfile.coreos.rb.erb → Vagrantfile.node.rb.erb} +0 -0
- data/lib/kontena/machine/vagrant/cloudinit.yml +2 -1
- data/lib/kontena/machine/vagrant/master_destroyer.rb +37 -0
- data/lib/kontena/machine/vagrant/master_provisioner.rb +75 -0
- data/lib/kontena/machine/vagrant/node_destroyer.rb +4 -0
- data/lib/kontena/machine/vagrant/node_provisioner.rb +1 -1
- data/lib/kontena/scripts/completer +29 -3
- data/spec/kontena/cli/app/common_spec.rb +61 -0
- data/spec/kontena/cli/app/deploy_command_spec.rb +25 -6
- data/spec/kontena/cli/app/docker_helper_spec.rb +32 -0
- data/spec/kontena/cli/common_spec.rb +53 -0
- metadata +61 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5495634079f9438b5489ef1b059675ecd1b1816e
|
|
4
|
+
data.tar.gz: 2f2d23237da702fe1dd1919a83621d004d129d9a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e44eaeae82ce15d7e09cb238cca449c8d7cfcf9ce666018dc8daa89aceb5a8ddc00a5077d5dbfe81aaae71a7ccb61f4e410054bc2f59dcbe13ed94ff6eacae08
|
|
7
|
+
data.tar.gz: 1a0ac1c623f49eb325c31d41cc108d0fabcce469848cca141b3b280827b6919f3f26a2456f4addac0ad378d74db41ed2ec47a86d67d62f5ad58018af620b95bd
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.9.0
|
data/bin/kontena
CHANGED
|
@@ -24,6 +24,7 @@ require 'kontena/cli/forgot_password_command'
|
|
|
24
24
|
require 'kontena/cli/reset_password_command'
|
|
25
25
|
require 'kontena/cli/container_command'
|
|
26
26
|
require 'kontena/cli/grid_command'
|
|
27
|
+
require 'kontena/cli/master_command'
|
|
27
28
|
require 'kontena/cli/node_command'
|
|
28
29
|
require 'kontena/cli/service_command'
|
|
29
30
|
require 'kontena/cli/vpn_command'
|
|
@@ -31,6 +32,7 @@ require 'kontena/cli/registry_command'
|
|
|
31
32
|
require 'kontena/cli/external_registry_command'
|
|
32
33
|
require 'kontena/cli/deploy_command'
|
|
33
34
|
require 'kontena/cli/app_command'
|
|
35
|
+
require 'kontena/cli/etcd_command'
|
|
34
36
|
require 'kontena/cli/version_command'
|
|
35
37
|
|
|
36
38
|
class MainCommand < Clamp::Command
|
|
@@ -41,9 +43,11 @@ class MainCommand < Clamp::Command
|
|
|
41
43
|
subcommand "service", "Service specific commands", Kontena::Cli::ServiceCommand
|
|
42
44
|
subcommand "deploy", "Create and deploy multiple services from YAML file", Kontena::Cli::DeployCommand
|
|
43
45
|
subcommand "node", "Node specific commands", Kontena::Cli::NodeCommand
|
|
46
|
+
subcommand "master", "Master specific commands", Kontena::Cli::MasterCommand
|
|
44
47
|
subcommand "vpn", "VPN specific commands", Kontena::Cli::VpnCommand
|
|
45
48
|
subcommand "registry", "Registry specific commands", Kontena::Cli::RegistryCommand
|
|
46
49
|
subcommand "container", "Container specific commands", Kontena::Cli::ContainerCommand
|
|
50
|
+
subcommand "etcd", "Etcd specific commands", Kontena::Cli::EtcdCommand
|
|
47
51
|
subcommand "external-registry", "External registry specific commands", Kontena::Cli::ExternalRegistryCommand
|
|
48
52
|
subcommand "register", "Register Kontena Account", Kontena::Cli::RegisterCommand
|
|
49
53
|
subcommand "login", "Login to Kontena Master", Kontena::Cli::LoginCommand
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require_relative 'apps/init_command'
|
|
2
|
+
require_relative 'apps/build_command'
|
|
2
3
|
require_relative 'apps/deploy_command'
|
|
3
4
|
require_relative 'apps/start_command'
|
|
4
5
|
require_relative 'apps/stop_command'
|
|
@@ -9,6 +10,7 @@ require_relative 'apps/logs_command'
|
|
|
9
10
|
class Kontena::Cli::AppCommand < Clamp::Command
|
|
10
11
|
|
|
11
12
|
subcommand "init", "Init Kontena application", Kontena::Cli::Apps::InitCommand
|
|
13
|
+
subcommand "build", "Build Kontena services", Kontena::Cli::Apps::BuildCommand
|
|
12
14
|
subcommand "deploy", "Deploy Kontena services", Kontena::Cli::Apps::DeployCommand
|
|
13
15
|
subcommand "start", "Start services", Kontena::Cli::Apps::StartCommand
|
|
14
16
|
subcommand "stop", "Stop services", Kontena::Cli::Apps::StopCommand
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require_relative 'common'
|
|
3
|
+
require_relative 'docker_helper'
|
|
4
|
+
|
|
5
|
+
module Kontena::Cli::Apps
|
|
6
|
+
class BuildCommand < Clamp::Command
|
|
7
|
+
include Kontena::Cli::Common
|
|
8
|
+
include Common
|
|
9
|
+
include DockerHelper
|
|
10
|
+
|
|
11
|
+
option ['-p', '--project-name'], 'NAME', 'Specify an alternate project name (default: directory name)'
|
|
12
|
+
option ['-f', '--file'], 'FILE', 'Specify an alternate Kontena compose file', attribute_name: :filename, default: 'kontena.yml'
|
|
13
|
+
parameter "[SERVICE] ...", "Services to start"
|
|
14
|
+
|
|
15
|
+
attr_reader :services, :service_prefix
|
|
16
|
+
|
|
17
|
+
def execute
|
|
18
|
+
require_config_file(filename)
|
|
19
|
+
@service_prefix = project_name || current_dir
|
|
20
|
+
dir = Dir.getwd
|
|
21
|
+
@services = load_services(filename, service_list, service_prefix)
|
|
22
|
+
Dir.chdir(dir)
|
|
23
|
+
process_docker_images(services, true) if dockerfile_exist?
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -4,12 +4,12 @@ module Kontena::Cli::Apps
|
|
|
4
4
|
module Common
|
|
5
5
|
include Kontena::Cli::Services::ServicesHelper
|
|
6
6
|
|
|
7
|
-
def
|
|
8
|
-
@service_prefix = project_name || current_dir
|
|
9
|
-
|
|
7
|
+
def require_config_file(filename)
|
|
10
8
|
abort("File #{filename} does not exist") unless File.exists?(filename)
|
|
11
|
-
|
|
9
|
+
end
|
|
12
10
|
|
|
11
|
+
def load_services(filename, service_list, prefix)
|
|
12
|
+
services = parse_services(filename, nil, prefix)
|
|
13
13
|
services.delete_if { |name, service| !service_list.include?(name)} unless service_list.empty?
|
|
14
14
|
services
|
|
15
15
|
end
|
|
@@ -32,18 +32,17 @@ module Kontena::Cli::Apps
|
|
|
32
32
|
get_service(token, prefixed_name(name)) rescue false
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
def
|
|
36
|
-
services = YAML.load(File.read(file) % {
|
|
35
|
+
def parse_services(file, name = nil, prefix='')
|
|
36
|
+
services = YAML.load(File.read(File.expand_path(file)) % {project: prefix})
|
|
37
|
+
Dir.chdir(File.dirname(File.expand_path(file)))
|
|
37
38
|
services.each do |name, options|
|
|
39
|
+
normalize_env_vars(options)
|
|
38
40
|
if options.has_key?('extends')
|
|
39
|
-
|
|
41
|
+
extension_file = options['extends']['file']
|
|
42
|
+
service_name = options['extends']['service']
|
|
40
43
|
options.delete('extends')
|
|
41
|
-
services[name] =
|
|
42
|
-
end
|
|
43
|
-
if options.has_key?('build')
|
|
44
|
-
options.delete('build')
|
|
44
|
+
services[name] = extend_options(options, extension_file , service_name, prefix)
|
|
45
45
|
end
|
|
46
|
-
|
|
47
46
|
end
|
|
48
47
|
if name.nil?
|
|
49
48
|
services
|
|
@@ -51,5 +50,35 @@ module Kontena::Cli::Apps
|
|
|
51
50
|
services[name]
|
|
52
51
|
end
|
|
53
52
|
end
|
|
53
|
+
|
|
54
|
+
def extend_options(options, file, service_name, prefix)
|
|
55
|
+
parent_options = parse_services(file, service_name, prefix)
|
|
56
|
+
options['environment'] = extend_env_vars(parent_options, options)
|
|
57
|
+
parent_options.merge(options)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def normalize_env_vars(options)
|
|
61
|
+
if options['environment'].is_a?(Hash)
|
|
62
|
+
options['environment'] = options['environment'].map{|k, v| "#{k}=#{v}"}
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def extend_env_vars(from, to)
|
|
67
|
+
env_vars = to['environment'] || []
|
|
68
|
+
if from['environment']
|
|
69
|
+
from['environment'].each do |env|
|
|
70
|
+
env_vars << env unless to['environment'] && to['environment'].find {|key| key.split('=').first == env.split('=').first}
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
env_vars
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def dockerfile_exist?
|
|
77
|
+
!dockerfile.nil?
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def dockerfile
|
|
81
|
+
@dockerfile ||= File.new('Dockerfile') rescue nil
|
|
82
|
+
end
|
|
54
83
|
end
|
|
55
84
|
end
|
|
@@ -1,30 +1,38 @@
|
|
|
1
1
|
require 'yaml'
|
|
2
2
|
require_relative 'common'
|
|
3
|
+
require_relative 'docker_helper'
|
|
3
4
|
|
|
4
5
|
module Kontena::Cli::Apps
|
|
5
6
|
class DeployCommand < Clamp::Command
|
|
6
7
|
include Kontena::Cli::Common
|
|
7
8
|
include Common
|
|
9
|
+
include DockerHelper
|
|
8
10
|
|
|
9
11
|
option ['-f', '--file'], 'FILE', 'Specify an alternate Kontena compose file', attribute_name: :filename, default: 'kontena.yml'
|
|
12
|
+
option ['--no-build'], :flag, 'Don\'t build an image, even if it\'s missing', default: false
|
|
10
13
|
option ['-p', '--project-name'], 'NAME', 'Specify an alternate project name (default: directory name)'
|
|
11
14
|
|
|
12
15
|
parameter "[SERVICE] ...", "Services to start"
|
|
13
16
|
|
|
14
|
-
attr_reader :services, :service_prefix
|
|
17
|
+
attr_reader :services, :service_prefix, :deploy_queue
|
|
15
18
|
|
|
16
19
|
def execute
|
|
17
20
|
require_api_url
|
|
18
21
|
require_token
|
|
19
|
-
|
|
20
|
-
Dir.
|
|
21
|
-
|
|
22
|
+
require_config_file(filename)
|
|
23
|
+
dir = Dir.getwd
|
|
24
|
+
@deploy_queue = []
|
|
25
|
+
@service_prefix = project_name || current_dir
|
|
26
|
+
@services = load_services(filename, service_list, service_prefix)
|
|
27
|
+
Dir.chdir(dir)
|
|
28
|
+
process_docker_images(services) if !no_build? && dockerfile_exist?
|
|
29
|
+
create_or_update_services(services)
|
|
22
30
|
deploy_services(deploy_queue)
|
|
23
31
|
end
|
|
24
32
|
|
|
25
33
|
private
|
|
26
34
|
|
|
27
|
-
def
|
|
35
|
+
def create_or_update_services(services)
|
|
28
36
|
services.each do |name, config|
|
|
29
37
|
create_or_update_service(name, config)
|
|
30
38
|
end
|
|
@@ -32,7 +40,7 @@ module Kontena::Cli::Apps
|
|
|
32
40
|
|
|
33
41
|
def deploy_services(queue)
|
|
34
42
|
queue.each do |service|
|
|
35
|
-
puts "deploying #{service['id']}"
|
|
43
|
+
puts "deploying #{service['id'].colorize(:cyan)}"
|
|
36
44
|
data = {}
|
|
37
45
|
if service['deploy']
|
|
38
46
|
data[:strategy] = service['deploy']['strategy'] if service['deploy']['strategy']
|
|
@@ -43,8 +51,7 @@ module Kontena::Cli::Apps
|
|
|
43
51
|
end
|
|
44
52
|
|
|
45
53
|
def create_or_update_service(name, options)
|
|
46
|
-
|
|
47
|
-
# skip if service is already created or updated or it's not present
|
|
54
|
+
# skip if service is already processed or it's not present
|
|
48
55
|
return nil if in_deploy_queue?(name) || !services.keys.include?(name)
|
|
49
56
|
|
|
50
57
|
# create/update linked services recursively before continuing
|
|
@@ -57,6 +64,7 @@ module Kontena::Cli::Apps
|
|
|
57
64
|
end
|
|
58
65
|
end
|
|
59
66
|
|
|
67
|
+
merge_external_links(options)
|
|
60
68
|
merge_env_vars(options)
|
|
61
69
|
|
|
62
70
|
if service_exists?(name)
|
|
@@ -77,7 +85,7 @@ module Kontena::Cli::Apps
|
|
|
77
85
|
|
|
78
86
|
def create(name, options)
|
|
79
87
|
name = prefixed_name(name)
|
|
80
|
-
puts "creating #{name}"
|
|
88
|
+
puts "creating #{name.colorize(:cyan)}"
|
|
81
89
|
data = {name: name}
|
|
82
90
|
data.merge!(parse_data(options))
|
|
83
91
|
create_service(token, current_grid, data)
|
|
@@ -86,7 +94,7 @@ module Kontena::Cli::Apps
|
|
|
86
94
|
def update(id, options)
|
|
87
95
|
id = prefixed_name(id)
|
|
88
96
|
data = parse_data(options)
|
|
89
|
-
puts "updating #{id}"
|
|
97
|
+
puts "updating #{id.colorize(:cyan)}"
|
|
90
98
|
update_service(token, id, data)
|
|
91
99
|
end
|
|
92
100
|
|
|
@@ -107,6 +115,14 @@ module Kontena::Cli::Apps
|
|
|
107
115
|
options['environment'].uniq! {|s| s.split('=').first}
|
|
108
116
|
end
|
|
109
117
|
|
|
118
|
+
def merge_external_links(options)
|
|
119
|
+
if options['external_links']
|
|
120
|
+
options['links'] ||= []
|
|
121
|
+
options['links'] = options['links'] + options['external_links']
|
|
122
|
+
options.delete('external_links')
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
110
126
|
def read_env_file(path)
|
|
111
127
|
File.readlines(path).delete_if { |line| line.start_with?('#') || line.empty? }
|
|
112
128
|
end
|
|
@@ -129,13 +145,15 @@ module Kontena::Cli::Apps
|
|
|
129
145
|
data[:affinity] = options['affinity'] if options['affinity']
|
|
130
146
|
data[:user] = options['user'] if options['user']
|
|
131
147
|
data[:stateful] = options['stateful'] == true
|
|
148
|
+
data[:privileged] = options['privileged'] unless options['privileged'].nil?
|
|
132
149
|
data[:cap_add] = options['cap_add'] if options['cap_add']
|
|
133
150
|
data[:cap_drop] = options['cap_drop'] if options['cap_drop']
|
|
151
|
+
data[:net] = options['net'] if options['net']
|
|
152
|
+
data[:log_driver] = options['log_driver'] if options['log_driver']
|
|
153
|
+
data[:log_opts] = options['log_opt'] if options['log_opt'] && !options['log_opt'].empty?
|
|
154
|
+
|
|
134
155
|
data
|
|
135
156
|
end
|
|
136
157
|
|
|
137
|
-
def deploy_queue
|
|
138
|
-
@deploy_queue ||= []
|
|
139
|
-
end
|
|
140
158
|
end
|
|
141
159
|
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Kontena::Cli::Apps
|
|
2
|
+
module DockerHelper
|
|
3
|
+
|
|
4
|
+
def process_docker_images(services, force_build = false)
|
|
5
|
+
services.each do |name, service|
|
|
6
|
+
if service['build'] && (!image_exist?(service['image']) || force_build)
|
|
7
|
+
abort("'#{service['image']}' is not valid Docker image name") unless validate_image_name(service['image'])
|
|
8
|
+
puts "Building image #{service['image'].colorize(:cyan)}"
|
|
9
|
+
build_docker_image(service['image'], service['build'])
|
|
10
|
+
|
|
11
|
+
puts "Pushing image #{service['image'].colorize(:cyan)} to registry"
|
|
12
|
+
push_docker_image(service['image'])
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def validate_image_name(name)
|
|
18
|
+
!(/^[\w.\/\-]+:?+[\w+.]+$/ =~ name).nil?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def build_docker_image(name, path)
|
|
23
|
+
system("docker build -t #{name} #{path}")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def push_docker_image(image)
|
|
27
|
+
system("docker push #{image}")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def image_exist?(image)
|
|
31
|
+
`docker history #{image} 2>&1` ; $?.success?
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'yaml'
|
|
2
|
+
require 'securerandom'
|
|
2
3
|
|
|
3
4
|
module Kontena::Cli::Apps
|
|
4
5
|
class InitCommand < Clamp::Command
|
|
@@ -7,10 +8,15 @@ module Kontena::Cli::Apps
|
|
|
7
8
|
option ["-f", "--file"], "FILE", "Specify a docker-compose file", attribute_name: :docker_compose_file, default: 'docker-compose.yml'
|
|
8
9
|
option ["-i", "--image-name"], "IMAGE_NAME", "Specify a docker image name"
|
|
9
10
|
option ["-b", "--base-image"], "BASE_IMAGE_NAME", "Specify a docker base image name", default: "kontena/buildstep"
|
|
11
|
+
option ["-p", "--project-name"], "NAME", "Specify an alternate project name (default: directory name)"
|
|
12
|
+
|
|
13
|
+
attr_reader :service_prefix
|
|
10
14
|
|
|
11
15
|
def execute
|
|
12
16
|
require 'highline/import'
|
|
13
17
|
|
|
18
|
+
@service_prefix = project_name || File.basename(Dir.getwd)
|
|
19
|
+
|
|
14
20
|
if File.exist?('Dockerfile')
|
|
15
21
|
puts 'Found Dockerfile'
|
|
16
22
|
else
|
|
@@ -20,7 +26,21 @@ module Kontena::Cli::Apps
|
|
|
20
26
|
if File.exist?(docker_compose_file)
|
|
21
27
|
puts "Found #{docker_compose_file}."
|
|
22
28
|
else
|
|
23
|
-
|
|
29
|
+
if File.exist?('Procfile')
|
|
30
|
+
procfile = YAML.load(File.read('Procfile'))
|
|
31
|
+
else
|
|
32
|
+
procfile = {}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
app_env = nil
|
|
36
|
+
addons = []
|
|
37
|
+
|
|
38
|
+
if app_json
|
|
39
|
+
app_env = create_env_file(app_json)
|
|
40
|
+
addons = app_json['addons'] || []
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
create_docker_compose_yml(procfile, addons, app_env) if create_docker_compose_yml?
|
|
24
44
|
end
|
|
25
45
|
|
|
26
46
|
services = generate_kontena_services(docker_compose_file)
|
|
@@ -32,18 +52,44 @@ module Kontena::Cli::Apps
|
|
|
32
52
|
end
|
|
33
53
|
create_yml(services, 'kontena.yml')
|
|
34
54
|
|
|
35
|
-
puts "
|
|
55
|
+
puts "Your app is ready! Deploy with 'kontena app deploy'.".colorize(:green)
|
|
36
56
|
end
|
|
37
57
|
|
|
38
58
|
|
|
39
59
|
protected
|
|
60
|
+
|
|
61
|
+
def app_json
|
|
62
|
+
if !@app_json && File.exist?('app.json')
|
|
63
|
+
@app_json = JSON.parse(File.read('app.json'))
|
|
64
|
+
end
|
|
65
|
+
@app_json
|
|
66
|
+
end
|
|
67
|
+
|
|
40
68
|
def create_dockerfile?
|
|
41
|
-
|
|
69
|
+
['', 'y', 'yes'].include? ask('Dockerfile not found. Do you want to create it? [Yn]: ').downcase
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def create_env_file(app_json)
|
|
73
|
+
|
|
74
|
+
if app_json['env']
|
|
75
|
+
app_env = File.new('.env', 'w')
|
|
76
|
+
app_json['env'].each do |key, env|
|
|
77
|
+
if env['generator'] == 'secret'
|
|
78
|
+
value = SecureRandom.hex(64)
|
|
79
|
+
else
|
|
80
|
+
value = env['value']
|
|
81
|
+
end
|
|
82
|
+
app_env.puts "#{key}=#{value}"
|
|
83
|
+
end
|
|
84
|
+
app_env.close
|
|
85
|
+
return '.env'
|
|
86
|
+
end
|
|
87
|
+
nil
|
|
42
88
|
end
|
|
43
89
|
|
|
44
90
|
def current_user
|
|
45
91
|
token = require_token
|
|
46
|
-
client(token).get('user')
|
|
92
|
+
client(token).get('user') rescue ''
|
|
47
93
|
end
|
|
48
94
|
|
|
49
95
|
def create_dockerfile
|
|
@@ -51,6 +97,7 @@ module Kontena::Cli::Apps
|
|
|
51
97
|
dockerfile = File.new('Dockerfile', 'w')
|
|
52
98
|
dockerfile.puts "FROM #{base_image}"
|
|
53
99
|
dockerfile.puts "MAINTAINER #{current_user['email']}"
|
|
100
|
+
dockerfile.puts 'CMD ["/start", "web"]'
|
|
54
101
|
dockerfile.close
|
|
55
102
|
end
|
|
56
103
|
|
|
@@ -65,28 +112,69 @@ module Kontena::Cli::Apps
|
|
|
65
112
|
end
|
|
66
113
|
|
|
67
114
|
def create_docker_compose_yml?
|
|
68
|
-
|
|
115
|
+
['', 'y', 'yes'].include? ask("#{docker_compose_file} not found. Do you want to create it? [Yn]: ").downcase
|
|
69
116
|
end
|
|
70
117
|
|
|
71
|
-
def create_docker_compose_yml
|
|
118
|
+
def create_docker_compose_yml(procfile, addons, env_file)
|
|
72
119
|
puts "Creating #{docker_compose_file.colorize(:cyan)}"
|
|
73
|
-
|
|
120
|
+
if procfile.keys.size > 0
|
|
121
|
+
# generate services found in Procfile
|
|
122
|
+
docker_compose = {}
|
|
123
|
+
procfile.keys.each do |service|
|
|
124
|
+
docker_compose[service] = {'build' => '.' }
|
|
125
|
+
docker_compose[service]['environment'] = ['PORT=5000'] if app_json && service == 'web' # Heroku generates PORT env variable so should we do too
|
|
126
|
+
docker_compose[service]['command'] = "/start #{service}" if service != 'web'
|
|
127
|
+
docker_compose[service]['env_file'] = env_file if env_file
|
|
128
|
+
|
|
129
|
+
# generate addon services
|
|
130
|
+
addons.each do |addon|
|
|
131
|
+
addon_service = addon.split(":")[0]
|
|
132
|
+
addon_service.slice!('heroku-')
|
|
133
|
+
if valid_addons.has_key?(addon_service)
|
|
134
|
+
docker_compose[service]['links'] = [] unless docker_compose[service]['links']
|
|
135
|
+
docker_compose[service]['links'] << "#{addon_service}:#{addon_service}"
|
|
136
|
+
docker_compose[service]['environment'] = [] unless docker_compose[service]['environment']
|
|
137
|
+
docker_compose[service]['environment'] += valid_addons[addon_service]['environment']
|
|
138
|
+
docker_compose[addon_service] = {'image' => valid_addons[addon_service]['image']}
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
else
|
|
143
|
+
# no Procfile found, create dummy web service
|
|
144
|
+
docker_compose = {'web' => { 'build' => '.'}}
|
|
145
|
+
docker_compose['web']['env_file'] = env_file if env_file
|
|
146
|
+
end
|
|
147
|
+
# create docker-compose.yml file
|
|
148
|
+
create_yml(docker_compose, docker_compose_file)
|
|
74
149
|
end
|
|
75
150
|
|
|
76
151
|
def generate_kontena_services(docker_compose = nil)
|
|
77
152
|
services = {}
|
|
78
153
|
if docker_compose && File.exist?(docker_compose)
|
|
154
|
+
# extend services from docker-compose.yml
|
|
79
155
|
compose_services = YAML.load(File.read(docker_compose))
|
|
80
156
|
compose_services.each do |name, options|
|
|
81
157
|
services[name] = {'extends' => { 'file' => 'docker-compose.yml', 'service' => name }}
|
|
82
158
|
if options.has_key?('build')
|
|
83
|
-
image = image_name || "registry.kontena.local/#{File.basename(Dir.getwd)}
|
|
159
|
+
image = image_name || "registry.kontena.local/#{File.basename(Dir.getwd)}:latest"
|
|
84
160
|
services[name]['image'] = image
|
|
85
|
-
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# we have to generate Kontena urls to env vars for Heroku addons
|
|
164
|
+
# redis://openredis:6379 -> redis://project-name-openredis:6379
|
|
165
|
+
if options['links']
|
|
166
|
+
options['links'].each do |link|
|
|
167
|
+
service_link = link.split(':').first
|
|
168
|
+
if valid_addons.has_key?(service_link)
|
|
169
|
+
services[name]['environment'] ||= []
|
|
170
|
+
services[name]['environment'] += valid_addons(service_prefix)[service_link]['environment']
|
|
171
|
+
end
|
|
172
|
+
end
|
|
86
173
|
end
|
|
87
174
|
end
|
|
88
175
|
else
|
|
89
|
-
|
|
176
|
+
# no docker-compose.yml found, just create dummy service with image name
|
|
177
|
+
services = {'web' => { 'image' => "registry.kontena.local/#{File.basename(Dir.getwd)}:latest" }}
|
|
90
178
|
end
|
|
91
179
|
services
|
|
92
180
|
end
|
|
@@ -97,5 +185,37 @@ module Kontena::Cli::Apps
|
|
|
97
185
|
yml.close
|
|
98
186
|
end
|
|
99
187
|
|
|
188
|
+
def valid_addons(prefix=nil)
|
|
189
|
+
if prefix
|
|
190
|
+
prefix = "#{prefix}-"
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
{
|
|
194
|
+
'openredis' => {
|
|
195
|
+
'image' => 'redis:latest',
|
|
196
|
+
'environment' => ["REDIS_URL=redis://#{prefix}openredis:6379"]
|
|
197
|
+
},
|
|
198
|
+
'redis' => {
|
|
199
|
+
'image' => 'redis:latest',
|
|
200
|
+
'environment' => ["REDIS_URL=redis://#{prefix}redis:6379"]
|
|
201
|
+
},
|
|
202
|
+
'rediscloud' => {
|
|
203
|
+
'image' => 'redis:latest',
|
|
204
|
+
'environment' => ["REDISCLOUD_URL=#{prefix}redis://rediscloud:6379"]
|
|
205
|
+
},
|
|
206
|
+
'postgresql' => {
|
|
207
|
+
'image' => 'postgres:latest',
|
|
208
|
+
'environment' => ["DATABASE_URL=postgres://#{prefix}postgres:@postgresql:5432/postgres"]
|
|
209
|
+
},
|
|
210
|
+
'mongolab' => {
|
|
211
|
+
'image' => 'mongo:latest',
|
|
212
|
+
'environment' => ["MONGOLAB_URI=#{prefix}mongolab:27017"]
|
|
213
|
+
},
|
|
214
|
+
'memcachedcloud' => {
|
|
215
|
+
'image' => 'memcached:latest',
|
|
216
|
+
'enviroment' => ["MEMCACHEDCLOUD_SERVERS=#{prefix}memcachedcloud:11211"]
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
end
|
|
100
220
|
end
|
|
101
221
|
end
|