picsolve_docker_builder 0.2.0 → 0.3.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/CHANGELOG.md +7 -0
- data/Rakefile +1 -0
- data/integration/compose_hello_world_new_config/.docker-builder.yml +1 -1
- data/integration/compose_hello_world_old_config/.docker-builder.yml +1 -1
- data/lib/picsolve_docker_builder/base.rb +5 -1
- data/lib/picsolve_docker_builder/composer/container.rb +27 -2
- data/lib/picsolve_docker_builder/composer/registry.rb +5 -1
- data/lib/picsolve_docker_builder/composer/requirements/base.rb +64 -0
- data/lib/picsolve_docker_builder/composer/requirements/context.rb +21 -0
- data/lib/picsolve_docker_builder/composer/requirements/postgres.rb +139 -0
- data/lib/picsolve_docker_builder/composer/requirements.rb +22 -0
- data/lib/picsolve_docker_builder/frame.rb +33 -4
- data/lib/picsolve_docker_builder/helpers/config/base.rb +1 -0
- data/lib/picsolve_docker_builder/helpers/config/secret.rb +1 -0
- data/lib/picsolve_docker_builder/helpers/config/variable_object.rb +2 -0
- data/lib/picsolve_docker_builder/helpers/config_manager.rb +3 -1
- data/lib/picsolve_docker_builder/helpers/kubeclient.rb +0 -33
- data/lib/picsolve_docker_builder/helpers/kubernetes/rc.rb +10 -7
- data/lib/picsolve_docker_builder/helpers/kubernetes/secret.rb +11 -0
- data/lib/picsolve_docker_builder/helpers/kubernetes_manager.rb +12 -10
- data/lib/picsolve_docker_builder/helpers/registry.rb +165 -0
- data/lib/picsolve_docker_builder/helpers/repository.rb +1 -1
- data/lib/picsolve_docker_builder/helpers/ssh_connection.rb +68 -0
- data/lib/picsolve_docker_builder/helpers/ssh_forward.rb +16 -59
- data/lib/picsolve_docker_builder/nodejs.rb +3 -9
- data/lib/picsolve_docker_builder/scala.rb +0 -5
- data/lib/picsolve_docker_builder/version.rb +1 -1
- data/picsolve_docker_builder.gemspec +2 -1
- metadata +25 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0d987f527c105ef90502bd0201ecb24ada6573e4
|
4
|
+
data.tar.gz: 6cacbe31a88fae3264b790e014f7c2ab40528e50
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 95452c0a4140df6b0e8e4d4af1f12e2ea533affad313e437c8e2768d4841d007f05ffaf20210512ba3b9f7906c4359530052d159ca36e6f3c1feba8f4a0325ec
|
7
|
+
data.tar.gz: 3ff8233cf0152f8078d398b6665caebecc5fb1029abd358f822dfe38d456c925112b559234c30982bc7d1fa3e66d443a4bed66b2d236d3480cad620b23ababd6
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## [0.3.0] - 2015-11-13
|
4
|
+
### Added
|
5
|
+
|
6
|
+
- Support creation of postgres databases if needed
|
7
|
+
- Support build without publishing docker image
|
8
|
+
- Pass environment to docker container
|
9
|
+
|
3
10
|
## [0.2.0] - 2015-08-26
|
4
11
|
### Added
|
5
12
|
- Upgrade kubernetes api to v1
|
data/Rakefile
CHANGED
@@ -39,8 +39,12 @@ module PicsolveDockerBuilder
|
|
39
39
|
Dir.pwd
|
40
40
|
end
|
41
41
|
|
42
|
+
def config_file
|
43
|
+
ENV['DOCKER_BUILDER_FILE'] || '.docker-builder.yml'
|
44
|
+
end
|
45
|
+
|
42
46
|
def config_path
|
43
|
-
File.join(base_dir,
|
47
|
+
File.join(base_dir, config_file)
|
44
48
|
end
|
45
49
|
|
46
50
|
def default_config
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'picsolve_docker_builder/base'
|
2
2
|
require 'picsolve_docker_builder/composer/registry'
|
3
|
+
require 'picsolve_docker_builder/composer/requirements'
|
3
4
|
|
4
5
|
module PicsolveDockerBuilder
|
5
6
|
module Composer
|
@@ -33,8 +34,32 @@ module PicsolveDockerBuilder
|
|
33
34
|
@services[kubernetes]
|
34
35
|
end
|
35
36
|
|
36
|
-
def
|
37
|
-
return
|
37
|
+
def requirements(kubernetes)
|
38
|
+
return [] unless config.key? 'requirements'
|
39
|
+
config['requirements'].map do |name, config|
|
40
|
+
PicsolveDockerBuilder::Composer::Requirements.new(
|
41
|
+
name,
|
42
|
+
config,
|
43
|
+
PicsolveDockerBuilder::Composer::Requirements::Context.new(
|
44
|
+
kubernetes,
|
45
|
+
self
|
46
|
+
)
|
47
|
+
)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def environment(kubernetes)
|
52
|
+
env = []
|
53
|
+
log.info "evaluate dynamic stage variables #{kubernetes.stage}"
|
54
|
+
requirements(kubernetes).each do |req|
|
55
|
+
env += req.environment
|
56
|
+
end
|
57
|
+
env += environment_static
|
58
|
+
env
|
59
|
+
end
|
60
|
+
|
61
|
+
def environment_static
|
62
|
+
return [] unless config.key? 'environment'
|
38
63
|
config['environment'].map do |key, value|
|
39
64
|
{
|
40
65
|
'name' => key,
|
@@ -42,6 +42,11 @@ module PicsolveDockerBuilder
|
|
42
42
|
nil
|
43
43
|
end
|
44
44
|
|
45
|
+
def self.login_basic(*args)
|
46
|
+
auth = creds(*args)
|
47
|
+
Base64.encode64("#{auth['username']}:#{auth['password']}").delete("\n")
|
48
|
+
end
|
49
|
+
|
45
50
|
def self.repo_tag_unique(image_name)
|
46
51
|
repo_tag = image_name.split(%r{/})[1..-1].join('/')
|
47
52
|
repo_tag_split = repo_tag.split(/:/)
|
@@ -49,7 +54,6 @@ module PicsolveDockerBuilder
|
|
49
54
|
tag = repo_tag_split[1] || 'latest'
|
50
55
|
|
51
56
|
headers = {}
|
52
|
-
login_basic = get_login_basic
|
53
57
|
headers['Authorization'] = "Basic #{login_basic}" \
|
54
58
|
unless login_basic.nil?
|
55
59
|
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'picsolve_docker_builder/base'
|
2
|
+
require 'securerandom'
|
3
|
+
module PicsolveDockerBuilder
|
4
|
+
module Composer
|
5
|
+
module Requirements
|
6
|
+
# Base class for requirements
|
7
|
+
class Base
|
8
|
+
include PicsolveDockerBuilder::Base
|
9
|
+
attr_reader :name, :config
|
10
|
+
def initialize(name, config, context)
|
11
|
+
@name = name
|
12
|
+
@config = config
|
13
|
+
@context = context
|
14
|
+
end
|
15
|
+
|
16
|
+
def environment(_kubernetes)
|
17
|
+
[]
|
18
|
+
end
|
19
|
+
|
20
|
+
def stage
|
21
|
+
@context.kubernetes.stage
|
22
|
+
end
|
23
|
+
|
24
|
+
def kubernetes
|
25
|
+
@context.kubernetes
|
26
|
+
end
|
27
|
+
|
28
|
+
def namespace
|
29
|
+
@context.namespace
|
30
|
+
end
|
31
|
+
|
32
|
+
def container
|
33
|
+
@context.container
|
34
|
+
end
|
35
|
+
|
36
|
+
def gen_password(length = 16)
|
37
|
+
SecureRandom.urlsafe_base64(length)
|
38
|
+
end
|
39
|
+
|
40
|
+
def create_secret(name, obj)
|
41
|
+
secret = Kubeclient::Secret.new
|
42
|
+
secret.metadata = {
|
43
|
+
name: name,
|
44
|
+
namespace: namespace
|
45
|
+
}
|
46
|
+
secret.data = {}
|
47
|
+
obj.each do |key, value|
|
48
|
+
secret.data[key.to_sym] = Base64.encode64(value).delete("\n")
|
49
|
+
end
|
50
|
+
kubernetes.client.create_secret secret
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_secret(name)
|
54
|
+
obj = kubernetes.client.get_secret(name, namespace)
|
55
|
+
hash = {}
|
56
|
+
obj.data.to_hash.each do |key, value|
|
57
|
+
hash[key.to_s] = Base64.decode64(value)
|
58
|
+
end
|
59
|
+
hash
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'picsolve_docker_builder/base'
|
2
|
+
|
3
|
+
module PicsolveDockerBuilder
|
4
|
+
module Composer
|
5
|
+
module Requirements
|
6
|
+
# Base class for requirements
|
7
|
+
class Context
|
8
|
+
include PicsolveDockerBuilder::Base
|
9
|
+
attr_reader :kubernetes, :container
|
10
|
+
def initialize(kubernetes, container)
|
11
|
+
@kubernetes = kubernetes
|
12
|
+
@container = container
|
13
|
+
end
|
14
|
+
|
15
|
+
def namespace
|
16
|
+
@container.composer.namespace
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'kubeclient'
|
3
|
+
require 'picsolve_docker_builder/composer/requirements/base'
|
4
|
+
require 'picsolve_docker_builder/helpers/ssh_connection'
|
5
|
+
require 'pg'
|
6
|
+
|
7
|
+
module PicsolveDockerBuilder
|
8
|
+
module Composer
|
9
|
+
module Requirements
|
10
|
+
# Postgres db requirements
|
11
|
+
class Postgres < Base
|
12
|
+
def ssh_forward(host, port)
|
13
|
+
@ssh = PicsolveDockerBuilder::Helpers::SshConnection.new(
|
14
|
+
ssh_host: kubernetes.host,
|
15
|
+
ssh_port: kubernetes.port
|
16
|
+
)
|
17
|
+
forward = @ssh.forward(host, port)
|
18
|
+
@ssh.start
|
19
|
+
forward
|
20
|
+
end
|
21
|
+
|
22
|
+
# get postgres secret for current container
|
23
|
+
def postgres_secret
|
24
|
+
get_secret(user_and_db_name)
|
25
|
+
rescue KubeException => e
|
26
|
+
raise e unless e.message.match(/not found/)
|
27
|
+
log.info "Secret for #{user_and_db_name} database not found"
|
28
|
+
create_postgres_secret
|
29
|
+
end
|
30
|
+
|
31
|
+
# create a new postgres secret and create the according database
|
32
|
+
def create_postgres_secret
|
33
|
+
admin_secret = admin_postgres_secret
|
34
|
+
|
35
|
+
log.info "Create secret for #{user_and_db_name} database"
|
36
|
+
container_secret = {
|
37
|
+
'name' => user_and_db_name,
|
38
|
+
'user' => user_and_db_name,
|
39
|
+
'password' => gen_password,
|
40
|
+
'host' => admin_secret['host'],
|
41
|
+
'port' => admin_secret['port']
|
42
|
+
}
|
43
|
+
create_postgres_database(
|
44
|
+
admin_secret,
|
45
|
+
container_secret
|
46
|
+
)
|
47
|
+
create_secret(
|
48
|
+
user_and_db_name,
|
49
|
+
container_secret
|
50
|
+
)
|
51
|
+
container_secret
|
52
|
+
end
|
53
|
+
|
54
|
+
# create postgres database
|
55
|
+
# rubocop:disable Metrics/MethodLength
|
56
|
+
def create_postgres_database(admin_secret, container_secret)
|
57
|
+
log.info "Create database #{user_and_db_name}"
|
58
|
+
|
59
|
+
forward = ssh_forward(admin_secret['host'], admin_secret['port'].to_i)
|
60
|
+
admin_secret['host'] = '127.0.0.1'
|
61
|
+
admin_secret['port'] = forward.local_port
|
62
|
+
|
63
|
+
conn = PG::Connection.open(admin_secret)
|
64
|
+
|
65
|
+
user = conn.escape_string(container_secret['user'])
|
66
|
+
password = conn.escape_string(container_secret['password'])
|
67
|
+
name = conn.escape_string(container_secret['name'])
|
68
|
+
|
69
|
+
# create user
|
70
|
+
conn.exec(
|
71
|
+
"CREATE USER \"#{user}\" " \
|
72
|
+
"WITH PASSWORD '#{password}'"
|
73
|
+
)
|
74
|
+
|
75
|
+
# create db
|
76
|
+
conn.exec(
|
77
|
+
"CREATE DATABASE \"#{name}\""
|
78
|
+
)
|
79
|
+
|
80
|
+
# grant all rights to the user and the aws user
|
81
|
+
conn.exec("
|
82
|
+
GRANT ALL PRIVILEGES ON DATABASE \"#{name}\" to \"#{user}\";
|
83
|
+
GRANT ALL PRIVILEGES ON DATABASE \"#{name}\" to \"rds_superuser\";
|
84
|
+
")
|
85
|
+
|
86
|
+
conn.close
|
87
|
+
|
88
|
+
admin_secret['dbname'] = name
|
89
|
+
admin_secret['user'] = user
|
90
|
+
admin_secret['password'] = password
|
91
|
+
conn = PG::Connection.open(admin_secret)
|
92
|
+
|
93
|
+
conn.exec("
|
94
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES
|
95
|
+
ON TABLES TO \"#{user}\";
|
96
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES
|
97
|
+
ON TABLES TO \"rds_superuser\";
|
98
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES
|
99
|
+
ON SEQUENCES TO \"#{user}\";
|
100
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES
|
101
|
+
ON SEQUENCES TO \"rds_superuser\";
|
102
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES
|
103
|
+
ON FUNCTIONS TO \"#{user}\";
|
104
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES
|
105
|
+
ON FUNCTIONS TO \"rds_superuser\";
|
106
|
+
")
|
107
|
+
conn.close
|
108
|
+
end
|
109
|
+
# rubocop:enable Metrics/MethodLength
|
110
|
+
|
111
|
+
# get administrative postgres secrets
|
112
|
+
def admin_postgres_secret
|
113
|
+
get_secret('postgres').update('dbname' => 'postgres')
|
114
|
+
end
|
115
|
+
|
116
|
+
def user_and_db_name
|
117
|
+
"#{stage}-#{container.name}-#{name}"
|
118
|
+
end
|
119
|
+
|
120
|
+
def postgres_secret_name
|
121
|
+
"postgres-#{user_and_db_name}"
|
122
|
+
end
|
123
|
+
|
124
|
+
def environment_secret
|
125
|
+
postgres_secret.map do |key, value|
|
126
|
+
{
|
127
|
+
'name' => "#{name.upcase}_#{key.upcase}",
|
128
|
+
'value' => value
|
129
|
+
}
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def environment
|
134
|
+
environment_secret
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'picsolve_docker_builder/composer/requirements/context'
|
2
|
+
require 'picsolve_docker_builder/composer/requirements/postgres'
|
3
|
+
module PicsolveDockerBuilder
|
4
|
+
module Composer
|
5
|
+
# Requirements are enviromental dependencies for containers
|
6
|
+
module Requirements
|
7
|
+
def self.new(name, config, context)
|
8
|
+
fail 'Requirement need to have parameter type' \
|
9
|
+
unless config.key? 'type'
|
10
|
+
object(config['type']).new(name, config, context)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.object(type)
|
14
|
+
if type == 'postgres'
|
15
|
+
Postgres
|
16
|
+
else
|
17
|
+
fail "Unknown requirement '#{type}'"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -6,6 +6,7 @@ require 'psych'
|
|
6
6
|
|
7
7
|
module PicsolveDockerBuilder
|
8
8
|
# Docker image building template
|
9
|
+
# rubocop:disable Metrics/ClassLength
|
9
10
|
class Frame
|
10
11
|
include PicsolveDockerBuilder::Base
|
11
12
|
|
@@ -138,7 +139,15 @@ module PicsolveDockerBuilder
|
|
138
139
|
@docker_build = docker_build_build
|
139
140
|
end
|
140
141
|
|
142
|
+
def asset_build
|
143
|
+
end
|
144
|
+
|
141
145
|
def build
|
146
|
+
asset_build
|
147
|
+
if dest_image_name.nil?
|
148
|
+
log.info 'Skip building docker image as no dest_image is set'
|
149
|
+
return
|
150
|
+
end
|
142
151
|
docker_build
|
143
152
|
end
|
144
153
|
|
@@ -229,13 +238,33 @@ module PicsolveDockerBuilder
|
|
229
238
|
@container ||= create_container
|
230
239
|
end
|
231
240
|
|
241
|
+
def environment
|
242
|
+
blacklist = %w(
|
243
|
+
SSH_CLIENT
|
244
|
+
SSH_CONNECTION
|
245
|
+
LD_LIBRARY_PATH
|
246
|
+
PATH
|
247
|
+
NVM_DIR
|
248
|
+
NVM_NODEJS_ORG_MIRROR)
|
249
|
+
|
250
|
+
keys = ENV.keys
|
251
|
+
keys = keys.reject do |key|
|
252
|
+
blacklist.include? key
|
253
|
+
end
|
254
|
+
|
255
|
+
keys.map do |key|
|
256
|
+
"#{key}=#{ENV[key]}"
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
232
260
|
def create_container
|
233
261
|
command = ['/bin/sleep', '3600']
|
234
262
|
c = Docker::Container.create(
|
235
|
-
'Image'
|
236
|
-
'Cmd'
|
237
|
-
'OpenStdin'
|
238
|
-
'WorkingDir' => '/_build'
|
263
|
+
'Image' => asset_image.id,
|
264
|
+
'Cmd' => command,
|
265
|
+
'OpenStdin' => false,
|
266
|
+
'WorkingDir' => '/_build',
|
267
|
+
'Env' => environment
|
239
268
|
)
|
240
269
|
at_exit do
|
241
270
|
stop
|
@@ -7,9 +7,11 @@ module PicsolveDockerBuilder
|
|
7
7
|
def self.regex_full
|
8
8
|
/^\$\{([^\}]*)\}$/
|
9
9
|
end
|
10
|
+
|
10
11
|
def self.regex
|
11
12
|
/\$\{[^\}]*\}/
|
12
13
|
end
|
14
|
+
|
13
15
|
# Checks if a string needs to be replaced by an VariableObject
|
14
16
|
def self.replace_string(obj)
|
15
17
|
return obj unless VariableObject.regex.match(obj)
|
@@ -28,7 +28,9 @@ module PicsolveDockerBuilder
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def eval_container_config(config)
|
31
|
-
|
31
|
+
# eval environment if key exits
|
32
|
+
config['environment'] = eval_container_env(config['environment']) \
|
33
|
+
if config.key? 'environment'
|
32
34
|
config
|
33
35
|
end
|
34
36
|
|
@@ -1,34 +1 @@
|
|
1
1
|
require 'kubeclient'
|
2
|
-
|
3
|
-
module Kubeclient
|
4
|
-
module Common
|
5
|
-
# Hot fix for kubeclient should be going upstream
|
6
|
-
# * https://github.com/abonas/kubeclient/pull/96
|
7
|
-
class Client
|
8
|
-
def get_entities(entity_type, klass, options)
|
9
|
-
params = {}
|
10
|
-
if options[:label_selector]
|
11
|
-
params['params'] = { labelSelector: options[:label_selector] }
|
12
|
-
end
|
13
|
-
|
14
|
-
ns_prefix = build_namespace_prefix(options[:namespace])
|
15
|
-
response = handle_exception do
|
16
|
-
rest_client[ns_prefix + resource_name(entity_type)]
|
17
|
-
.get(params.merge(@headers))
|
18
|
-
end
|
19
|
-
|
20
|
-
result = JSON.parse(response)
|
21
|
-
|
22
|
-
resource_version = result.fetch('resourceVersion', nil)
|
23
|
-
if resource_version.nil?
|
24
|
-
resource_version =
|
25
|
-
result.fetch('metadata', {}).fetch('resourceVersion', nil)
|
26
|
-
end
|
27
|
-
|
28
|
-
collection = result['items'].map { |item| new_entity(item, klass) }
|
29
|
-
|
30
|
-
EntityList.new(entity_type, resource_version, collection)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
@@ -12,10 +12,9 @@ module PicsolveDockerBuilder
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def existing_rcs
|
15
|
-
rcs = client.get_replication_controllers
|
16
|
-
|
17
|
-
|
18
|
-
rc.metadata.name.match(/^#{@image.name}/)
|
15
|
+
rcs = client.get_replication_controllers.select do |rc|
|
16
|
+
rc.metadata.name.match(/^#{@image.name}/) && \
|
17
|
+
rc.metadata.namespace == @image.composer.namespace
|
19
18
|
end
|
20
19
|
rcs.map do |rc|
|
21
20
|
Rc.new(@image, @kubernetes, rc)
|
@@ -49,7 +48,7 @@ module PicsolveDockerBuilder
|
|
49
48
|
}
|
50
49
|
# append environment variables if in place
|
51
50
|
if @image.class.instance_methods.include? :environment
|
52
|
-
env = @image.environment
|
51
|
+
env = @image.environment(@kubernetes)
|
53
52
|
c['env'] = env unless env.nil?
|
54
53
|
end
|
55
54
|
[c]
|
@@ -76,10 +75,14 @@ module PicsolveDockerBuilder
|
|
76
75
|
end
|
77
76
|
|
78
77
|
def pods_by_selector(selector)
|
79
|
-
client.get_pods(
|
78
|
+
pods = client.get_pods(
|
80
79
|
namespace: @rc.metadata.namespace,
|
81
80
|
label_selector: selector
|
82
|
-
).
|
81
|
+
).select do |pod|
|
82
|
+
pod.metadata.namespace == @rc.metadata.namespace
|
83
|
+
end
|
84
|
+
|
85
|
+
pods.map do |pod|
|
83
86
|
Pod.new(pod, @kubernetes)
|
84
87
|
end
|
85
88
|
end
|
@@ -2,7 +2,7 @@ require 'picsolve_docker_builder/helpers/kubeclient'
|
|
2
2
|
require 'picsolve_docker_builder/helpers/kubernetes/rc'
|
3
3
|
require 'picsolve_docker_builder/helpers/kubernetes/service'
|
4
4
|
require 'picsolve_docker_builder/helpers/kubernetes/pod'
|
5
|
-
require 'picsolve_docker_builder/helpers/
|
5
|
+
require 'picsolve_docker_builder/helpers/ssh_connection'
|
6
6
|
require 'picsolve_docker_builder/base'
|
7
7
|
|
8
8
|
module PicsolveDockerBuilder
|
@@ -31,6 +31,10 @@ module PicsolveDockerBuilder
|
|
31
31
|
@composer = composer
|
32
32
|
end
|
33
33
|
|
34
|
+
def user
|
35
|
+
'core'
|
36
|
+
end
|
37
|
+
|
34
38
|
def create_client
|
35
39
|
Kubeclient::Client.new kubernetes_url, 'v1'
|
36
40
|
end
|
@@ -48,8 +52,8 @@ module PicsolveDockerBuilder
|
|
48
52
|
end
|
49
53
|
|
50
54
|
def kubernetes_url_ssh_forward_stop
|
51
|
-
@
|
52
|
-
@
|
55
|
+
@ssh.stop unless @ssh_forward.nil?
|
56
|
+
@ssh = nil
|
53
57
|
end
|
54
58
|
|
55
59
|
def stop
|
@@ -57,18 +61,16 @@ module PicsolveDockerBuilder
|
|
57
61
|
end
|
58
62
|
|
59
63
|
def kubernetes_url_ssh_forward
|
60
|
-
@
|
64
|
+
@ssh = SshConnection.new(
|
61
65
|
ssh_host: host,
|
62
|
-
ssh_port: port
|
63
|
-
local_port: 10_000,
|
64
|
-
remote_host: '127.0.0.1',
|
65
|
-
remote_port: 8080
|
66
|
+
ssh_port: port
|
66
67
|
)
|
67
|
-
|
68
|
+
forward = @ssh.forward('127.0.0.1', 8080, 10_000)
|
69
|
+
@ssh.start
|
68
70
|
at_exit do
|
69
71
|
kubernetes_url_ssh_forward_stop
|
70
72
|
end
|
71
|
-
"http://127.0.0.1:#{
|
73
|
+
"http://127.0.0.1:#{forward.local_port}"
|
72
74
|
end
|
73
75
|
|
74
76
|
def client
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'picsolve_docker_builder/base'
|
2
|
+
require 'excon'
|
3
|
+
require 'json'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
module PicsolveDockerBuilder
|
7
|
+
module Helpers
|
8
|
+
# This represents a remote registry, that is queried for
|
9
|
+
# for informations about images
|
10
|
+
class Registry
|
11
|
+
include PicsolveDockerBuilder::Base
|
12
|
+
|
13
|
+
attr_reader :config
|
14
|
+
|
15
|
+
def initialize(registry)
|
16
|
+
@registry = registry
|
17
|
+
end
|
18
|
+
|
19
|
+
def api_version_unsupported
|
20
|
+
fail 'No supported version found at registry'
|
21
|
+
end
|
22
|
+
|
23
|
+
def api_version
|
24
|
+
return @api_version unless @api_version.nil?
|
25
|
+
if connection.get(path: '/v2/').status == 200
|
26
|
+
@api_version = :v2
|
27
|
+
elsif connection.get(path: '/v1/_ping').status == 200
|
28
|
+
@api_version = :v1
|
29
|
+
else
|
30
|
+
api_version_unsupported
|
31
|
+
end
|
32
|
+
log.info "Detected api version #{@api_version} on registry #{@registry}"
|
33
|
+
@api_version
|
34
|
+
end
|
35
|
+
|
36
|
+
def list_tags_v1(image)
|
37
|
+
endpoint = "/v1/repositories/#{image}/tags"
|
38
|
+
r = connection.get(path: endpoint)
|
39
|
+
JSON.parse(r.body)
|
40
|
+
end
|
41
|
+
|
42
|
+
def list_tags_v2(image)
|
43
|
+
endpoint = "/v2/#{image}/tags/list"
|
44
|
+
r = connection.get(path: endpoint)
|
45
|
+
o = {}
|
46
|
+
JSON.parse(r.body)['tags'].each do |tag|
|
47
|
+
endpoint = "/v2/#{image}/manifests/#{tag}"
|
48
|
+
r = connection.get(path: endpoint)
|
49
|
+
digest = Digest::SHA2.new(256)
|
50
|
+
JSON.parse(r.body)['fsLayers'].each do |fs_hash|
|
51
|
+
digest.update(fs_hash['blobSum'])
|
52
|
+
end
|
53
|
+
o[tag] = digest.to_s
|
54
|
+
end
|
55
|
+
o
|
56
|
+
end
|
57
|
+
|
58
|
+
def unique_tag(image, tag)
|
59
|
+
tags = list_tags(image)
|
60
|
+
fail "tag '#{tag}' not found" unless tags.include? tag
|
61
|
+
tags.each do |my_tag, hash|
|
62
|
+
# next if not right name
|
63
|
+
next unless my_tag.match(/^jenkins-/)
|
64
|
+
# next hash not matching
|
65
|
+
next if hash != tags[tag]
|
66
|
+
# return tag name now
|
67
|
+
return my_tag
|
68
|
+
end
|
69
|
+
fail "no unique tag found for tag=#{tag}"
|
70
|
+
end
|
71
|
+
|
72
|
+
def list_tags(image)
|
73
|
+
if api_version == :v1
|
74
|
+
list_tags_v1(image)
|
75
|
+
elsif api_version == :v2
|
76
|
+
list_tags_v2(image)
|
77
|
+
else
|
78
|
+
api_version_unsupported
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def http_host
|
83
|
+
URI(@registry).host
|
84
|
+
end
|
85
|
+
|
86
|
+
def headers
|
87
|
+
{
|
88
|
+
'Host' => http_host
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
def connection
|
93
|
+
@connection ||= Excon.new(
|
94
|
+
@registry,
|
95
|
+
headers: headers
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.dockercfg_path
|
100
|
+
File.expand_path '~/.dockercfg'
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.dockercfg
|
104
|
+
JSON.parse(
|
105
|
+
File.open(
|
106
|
+
dockercfg_path
|
107
|
+
).read
|
108
|
+
)
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.creds(*args)
|
112
|
+
auth = Base64.decode64(
|
113
|
+
get_login_basic(
|
114
|
+
*args
|
115
|
+
)
|
116
|
+
).split(':')
|
117
|
+
{
|
118
|
+
'username' => auth[0],
|
119
|
+
'password' => auth[1]
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.get_login_basic(registry = 'docker.picsolve.net')
|
124
|
+
dockercfg[registry]['auth']
|
125
|
+
rescue StandardError
|
126
|
+
nil
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.repo_tag_unique(image_name)
|
130
|
+
repo_tag = image_name.split(%r{/})[1..-1].join('/')
|
131
|
+
repo_tag_split = repo_tag.split(/:/)
|
132
|
+
repo = repo_tag_split[0]
|
133
|
+
tag = repo_tag_split[1] || 'latest'
|
134
|
+
|
135
|
+
headers = {}
|
136
|
+
login_basic = get_login_basic
|
137
|
+
headers['Authorization'] = "Basic #{login_basic}" \
|
138
|
+
unless login_basic.nil?
|
139
|
+
|
140
|
+
connection = Excon.new(
|
141
|
+
'https://docker.picsolve.net',
|
142
|
+
headers: headers,
|
143
|
+
persistent: true
|
144
|
+
)
|
145
|
+
response = connection.get(path: "/v1/repositories/#{repo}/tags")
|
146
|
+
|
147
|
+
tags = JSON.parse(response.body)
|
148
|
+
|
149
|
+
hash = tags[tag]
|
150
|
+
|
151
|
+
tags.each do |t, h|
|
152
|
+
next if h != hash
|
153
|
+
next if t == tag
|
154
|
+
next unless t.match(/jenkins-[0-9]+/)
|
155
|
+
return {
|
156
|
+
tag_unique: "#{image_name.split(':').first}:#{t}",
|
157
|
+
hash: hash
|
158
|
+
}
|
159
|
+
end
|
160
|
+
|
161
|
+
fail "Can not find a uniqe tag for #{image_name}"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'picsolve_docker_builder/helpers/ssh_forward'
|
2
|
+
require 'picsolve_docker_builder/base'
|
3
|
+
require 'net/ssh'
|
4
|
+
|
5
|
+
module PicsolveDockerBuilder
|
6
|
+
module Helpers
|
7
|
+
# Ruby class that can forward a remote port over SSH
|
8
|
+
class SshConnection
|
9
|
+
include PicsolveDockerBuilder::Base
|
10
|
+
attr_reader :options
|
11
|
+
def initialize(opts)
|
12
|
+
@options = opts
|
13
|
+
end
|
14
|
+
|
15
|
+
def connection
|
16
|
+
@connection ||= Net::SSH.start(
|
17
|
+
ssh_host,
|
18
|
+
ssh_user,
|
19
|
+
port: ssh_port
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def forward(*args)
|
24
|
+
SshForward.forward(connection, *args)
|
25
|
+
end
|
26
|
+
|
27
|
+
def start
|
28
|
+
log.debug "Connecting via ssh to host '#{ssh_user}@#{ssh_host}'"
|
29
|
+
# start thread with ssh
|
30
|
+
@thread = Thread.new do
|
31
|
+
connection.loop { true }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def stop
|
36
|
+
return if @thread.nil?
|
37
|
+
@thread.kill
|
38
|
+
@thread.join
|
39
|
+
log.debug \
|
40
|
+
"Disconnected ssh connection to host '#{ssh_user}@#{ssh_host}'"
|
41
|
+
end
|
42
|
+
|
43
|
+
def ssh_user
|
44
|
+
options[:ssh_user] = 'core'
|
45
|
+
end
|
46
|
+
|
47
|
+
def ssh_host
|
48
|
+
options[:ssh_host]
|
49
|
+
end
|
50
|
+
|
51
|
+
def ssh_port
|
52
|
+
options[:ssh_port] || 22
|
53
|
+
end
|
54
|
+
|
55
|
+
def remote_host
|
56
|
+
options[:remote_host]
|
57
|
+
end
|
58
|
+
|
59
|
+
def remote_port
|
60
|
+
options[:remote_port]
|
61
|
+
end
|
62
|
+
|
63
|
+
def local_port
|
64
|
+
options[:local_port] || remote_port
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -5,79 +5,36 @@ module PicsolveDockerBuilder
|
|
5
5
|
module Helpers
|
6
6
|
# Ruby class that can forward a remote port over SSH
|
7
7
|
class SshForward
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
def self.forward(*args)
|
9
|
+
f = new(*args)
|
10
|
+
f.bind_port
|
11
|
+
f
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
include PicsolveDockerBuilder::Base
|
15
|
+
attr_reader :connection, :local_port
|
16
|
+
def initialize(connection, remote_host, remote_port, local_port = nil)
|
17
|
+
@connection = connection
|
18
|
+
@remote_host = remote_host
|
19
|
+
@remote_port = remote_port
|
20
|
+
@local_port = local_port || remote_port
|
20
21
|
end
|
21
22
|
|
22
23
|
def bind_port
|
23
24
|
tries = 0
|
24
|
-
port = local_port
|
25
25
|
begin
|
26
|
-
connection.forward.local(
|
27
|
-
|
26
|
+
connection.forward.local(@local_port, @remote_host, @remote_port)
|
27
|
+
log.info "forward remote port #{@remote_host}:#{@remote_port}" \
|
28
|
+
" to local port #{@local_port}"
|
29
|
+
return @local_port
|
28
30
|
rescue Errno::EADDRINUSE => e
|
29
31
|
# raise after five failed tries
|
30
32
|
raise e if tries > 5
|
31
33
|
tries += 1
|
32
|
-
|
34
|
+
@local_port += 1
|
33
35
|
retry
|
34
36
|
end
|
35
37
|
end
|
36
|
-
|
37
|
-
def start
|
38
|
-
log.debug "Connecting via ssh to host '#{ssh_user}@#{ssh_host}'"
|
39
|
-
|
40
|
-
# bind remote service to local port
|
41
|
-
port = bind_port
|
42
|
-
|
43
|
-
# start thread with ssh
|
44
|
-
@thread = Thread.new do
|
45
|
-
connection.loop { true }
|
46
|
-
end
|
47
|
-
port
|
48
|
-
end
|
49
|
-
|
50
|
-
def stop
|
51
|
-
return if @thread.nil?
|
52
|
-
@thread.kill
|
53
|
-
@thread.join
|
54
|
-
log.debug \
|
55
|
-
"Disconnected ssh connection to host '#{ssh_user}@#{ssh_host}'"
|
56
|
-
end
|
57
|
-
|
58
|
-
def ssh_user
|
59
|
-
options[:ssh_user] = 'core'
|
60
|
-
end
|
61
|
-
|
62
|
-
def ssh_host
|
63
|
-
options[:ssh_host]
|
64
|
-
end
|
65
|
-
|
66
|
-
def ssh_port
|
67
|
-
options[:ssh_port] || 22
|
68
|
-
end
|
69
|
-
|
70
|
-
def remote_host
|
71
|
-
options[:remote_host]
|
72
|
-
end
|
73
|
-
|
74
|
-
def remote_port
|
75
|
-
options[:remote_port]
|
76
|
-
end
|
77
|
-
|
78
|
-
def local_port
|
79
|
-
options[:local_port] || remote_port
|
80
|
-
end
|
81
38
|
end
|
82
39
|
end
|
83
40
|
end
|
@@ -16,8 +16,7 @@ module PicsolveDockerBuilder
|
|
16
16
|
c['nodejs'] = {
|
17
17
|
'build_commands' => [
|
18
18
|
'npm install',
|
19
|
-
'grunt'
|
20
|
-
'tar cvzf html.tar.gz -C frontend/dist .'
|
19
|
+
'grunt jenkins'
|
21
20
|
]
|
22
21
|
}
|
23
22
|
c
|
@@ -35,8 +34,8 @@ module PicsolveDockerBuilder
|
|
35
34
|
def docker_build_files
|
36
35
|
f = []
|
37
36
|
f << Builder::File.new(
|
38
|
-
'
|
39
|
-
source: File.join(base_dir, '
|
37
|
+
'dist.tar.gz',
|
38
|
+
source: File.join(base_dir, 'dist.tar.gz'),
|
40
39
|
destination: '/var/www/html'
|
41
40
|
)
|
42
41
|
|
@@ -126,10 +125,5 @@ module PicsolveDockerBuilder
|
|
126
125
|
|
127
126
|
stop
|
128
127
|
end
|
129
|
-
|
130
|
-
def build
|
131
|
-
asset_build
|
132
|
-
docker_build
|
133
|
-
end
|
134
128
|
end
|
135
129
|
end
|
@@ -29,7 +29,8 @@ Gem::Specification.new do |spec|
|
|
29
29
|
spec.add_development_dependency 'simplecov'
|
30
30
|
spec.add_development_dependency 'simplecov-cobertura'
|
31
31
|
spec.add_dependency 'rake', '~> 10.0'
|
32
|
-
spec.add_dependency 'kubeclient', '~> 0.
|
32
|
+
spec.add_dependency 'kubeclient', '~> 0.5.1'
|
33
33
|
spec.add_dependency 'docker-api'
|
34
34
|
spec.add_dependency 'net-ssh'
|
35
|
+
spec.add_dependency 'pg'
|
35
36
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: picsolve_docker_builder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christian Simon
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-11-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -128,14 +128,14 @@ dependencies:
|
|
128
128
|
requirements:
|
129
129
|
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: 0.
|
131
|
+
version: 0.5.1
|
132
132
|
type: :runtime
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: 0.
|
138
|
+
version: 0.5.1
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: docker-api
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,6 +164,20 @@ dependencies:
|
|
164
164
|
- - ">="
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: pg
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :runtime
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
167
181
|
description:
|
168
182
|
email:
|
169
183
|
- christian.simon@picsolve.com
|
@@ -232,6 +246,10 @@ files:
|
|
232
246
|
- lib/picsolve_docker_builder/composer/container.rb
|
233
247
|
- lib/picsolve_docker_builder/composer/image.rb
|
234
248
|
- lib/picsolve_docker_builder/composer/registry.rb
|
249
|
+
- lib/picsolve_docker_builder/composer/requirements.rb
|
250
|
+
- lib/picsolve_docker_builder/composer/requirements/base.rb
|
251
|
+
- lib/picsolve_docker_builder/composer/requirements/context.rb
|
252
|
+
- lib/picsolve_docker_builder/composer/requirements/postgres.rb
|
235
253
|
- lib/picsolve_docker_builder/frame.rb
|
236
254
|
- lib/picsolve_docker_builder/helpers/config/base.rb
|
237
255
|
- lib/picsolve_docker_builder/helpers/config/secret.rb
|
@@ -241,9 +259,12 @@ files:
|
|
241
259
|
- lib/picsolve_docker_builder/helpers/kubernetes/pod.rb
|
242
260
|
- lib/picsolve_docker_builder/helpers/kubernetes/rc.rb
|
243
261
|
- lib/picsolve_docker_builder/helpers/kubernetes/resource.rb
|
262
|
+
- lib/picsolve_docker_builder/helpers/kubernetes/secret.rb
|
244
263
|
- lib/picsolve_docker_builder/helpers/kubernetes/service.rb
|
245
264
|
- lib/picsolve_docker_builder/helpers/kubernetes_manager.rb
|
265
|
+
- lib/picsolve_docker_builder/helpers/registry.rb
|
246
266
|
- lib/picsolve_docker_builder/helpers/repository.rb
|
267
|
+
- lib/picsolve_docker_builder/helpers/ssh_connection.rb
|
247
268
|
- lib/picsolve_docker_builder/helpers/ssh_forward.rb
|
248
269
|
- lib/picsolve_docker_builder/nodejs.rb
|
249
270
|
- lib/picsolve_docker_builder/scala.rb
|