rascal 0.1.0 → 0.3.2
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/.travis.yml +1 -1
- data/CHANGELOG.md +26 -0
- data/Gemfile.lock +2 -2
- data/README.md +31 -7
- data/lib/rascal/cli.rb +5 -4
- data/lib/rascal/cli/base.rb +28 -9
- data/lib/rascal/cli/clean.rb +9 -2
- data/lib/rascal/cli/main.rb +25 -3
- data/lib/rascal/cli/update.rb +24 -0
- data/lib/rascal/docker/container.rb +54 -17
- data/lib/rascal/docker/interface.rb +21 -53
- data/lib/rascal/docker/volume.rb +16 -2
- data/lib/rascal/environment.rb +15 -7
- data/lib/rascal/environments_definition/gitlab.rb +38 -13
- data/lib/rascal/service.rb +9 -3
- data/lib/rascal/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cd535de9c0092cf4ab69d5e279e8cb0e19ec682d74e915844a781db8e7e2bb53
|
|
4
|
+
data.tar.gz: ce84b237c9302f56f810a03b3bf95711b279f3a931687d5d29bd21bbb4f325ca
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d2d95fdaf90bab2efc7e3987d142f37653526f30e6026a8a2d056cf85c8bb780b47251d85e8102faac8a0d4b715459c1dcec2d7e5f20d8fb413150778872844d
|
|
7
|
+
data.tar.gz: c5f35334569ea8be40d4f474aef6734c52289f33517dc7c96cbae432a2c02fccbb5bd906c1273ff077ddb21be89dcbd29b3a729c96853f26afb07dea9439e8a0
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,32 @@ All notable changes to this project will be documented here.
|
|
|
4
4
|
|
|
5
5
|
Rascal follows semantic versioning. This has little consequence pre 1.0, so expect breaking changes.
|
|
6
6
|
|
|
7
|
+
## 0.3.2 (2020-10-23)
|
|
8
|
+
|
|
9
|
+
- Fix broken `rascal shell` command.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## 0.3.1 (2020-10-01)
|
|
13
|
+
|
|
14
|
+
- Pass env variables to service containers.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## 0.3.0 (2020-09-23)
|
|
18
|
+
|
|
19
|
+
- Mount /repo into all service volumes
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## 0.2.1 (2019-04-10)
|
|
23
|
+
|
|
24
|
+
- Prefix names with directory name, to avoid conflicts between projects.
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## 0.2.0 (2019-04-10)
|
|
28
|
+
|
|
29
|
+
- Add `--all` flag for `rascal clean`.
|
|
30
|
+
- Add `rascal update` command.
|
|
31
|
+
- Allow rascal specific per job config.
|
|
32
|
+
|
|
7
33
|
|
|
8
34
|
## 0.1.0 (2019-03-21)
|
|
9
35
|
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -30,14 +30,18 @@ You need to add some extra information to your `.gitlab-ci.yml`. A working versi
|
|
|
30
30
|
|
|
31
31
|
```
|
|
32
32
|
# settings here override job settings
|
|
33
|
-
.rascal:
|
|
34
|
-
repo_dir: /repo
|
|
35
|
-
variables:
|
|
33
|
+
.rascal: # add rascal specific config here
|
|
34
|
+
repo_dir: /repo # /repo is the default
|
|
35
|
+
variables: # extra env variables
|
|
36
36
|
BUNDLE_PATH: /cache/bundle
|
|
37
|
-
volumes:
|
|
38
|
-
cache: /cache
|
|
37
|
+
volumes: # mount these volumes
|
|
38
|
+
cache: /cache # we will always mount a /builds volume
|
|
39
39
|
before_shell:
|
|
40
|
-
- bundle check
|
|
40
|
+
- bundle check # run this when starting a shell
|
|
41
|
+
jobs:
|
|
42
|
+
rspec: # override settings for a specific job
|
|
43
|
+
variables:
|
|
44
|
+
BUNDLE_GEMFILE = /repo/Gemfile
|
|
41
45
|
|
|
42
46
|
.environment: &environment
|
|
43
47
|
image: registry.makandra.de/makandra/ci-images/test-env:2.5
|
|
@@ -53,7 +57,6 @@ You need to add some extra information to your `.gitlab-ci.yml`. A working versi
|
|
|
53
57
|
variables:
|
|
54
58
|
BUNDLE_PATH: ./bundle/vendor
|
|
55
59
|
DATABASE_URL: postgresql://pg_user@pg-db/test-db
|
|
56
|
-
DATABASE_CLEANER_ALLOW_REMOTE_DATABASE_URL: "true"
|
|
57
60
|
REDIS_URL: redis://redis
|
|
58
61
|
PROMPT: CI env
|
|
59
62
|
cache:
|
|
@@ -73,6 +76,27 @@ rspec:
|
|
|
73
76
|
Then, in your project root, run `rascal shell rspec`.
|
|
74
77
|
|
|
75
78
|
|
|
79
|
+
### Commands
|
|
80
|
+
|
|
81
|
+
#### rascal shell <job>
|
|
82
|
+
|
|
83
|
+
Start a docker container (plus required services) and open an interactive shell.
|
|
84
|
+
|
|
85
|
+
Currently requires a "bash" to exist.
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
#### rascal clean <job> | --all [--volumes]
|
|
89
|
+
|
|
90
|
+
Stop and remove all created containers, services, networks for either the given or all jobs.
|
|
91
|
+
|
|
92
|
+
If `--volumes` is given, also remove all cached volumes.
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
#### rascal update <job> | --all
|
|
96
|
+
|
|
97
|
+
Update all images for the given job, or all jobs.
|
|
98
|
+
|
|
99
|
+
|
|
76
100
|
## License
|
|
77
101
|
|
|
78
102
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/lib/rascal/cli.rb
CHANGED
|
@@ -2,9 +2,10 @@ require 'thor'
|
|
|
2
2
|
|
|
3
3
|
module Rascal
|
|
4
4
|
module CLI
|
|
5
|
-
autoload :Base,
|
|
6
|
-
autoload :Clean,
|
|
7
|
-
autoload :Main,
|
|
8
|
-
autoload :Shell,
|
|
5
|
+
autoload :Base, 'rascal/cli/base'
|
|
6
|
+
autoload :Clean, 'rascal/cli/clean'
|
|
7
|
+
autoload :Main, 'rascal/cli/main'
|
|
8
|
+
autoload :Shell, 'rascal/cli/shell'
|
|
9
|
+
autoload :Update, 'rascal/cli/update'
|
|
9
10
|
end
|
|
10
11
|
end
|
data/lib/rascal/cli/base.rb
CHANGED
|
@@ -19,19 +19,38 @@ module Rascal
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def find_environment(environment_name)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
definition = environment_definition
|
|
23
|
+
if (environment = definition.environment(environment_name))
|
|
24
|
+
return environment
|
|
25
|
+
else
|
|
26
|
+
available_environments = definition.available_environment_names.join(', ')
|
|
27
|
+
if environment_name
|
|
28
|
+
fail_with_error("Unknown environment #{environment_name}. Available: #{available_environments}.")
|
|
25
29
|
else
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
fail_with_error("Missing environment. Available: #{available_environments}.")
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def each_environment(name, &block)
|
|
36
|
+
return enum_for(:each_environment) unless block_given?
|
|
37
|
+
|
|
38
|
+
if name == :all
|
|
39
|
+
definition = environment_definition
|
|
40
|
+
definition.available_environment_names.each do |environment_name|
|
|
41
|
+
yield definition.environment(environment_name)
|
|
32
42
|
end
|
|
43
|
+
else
|
|
44
|
+
yield find_environment(name)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def environment_definition
|
|
49
|
+
if (definition = EnvironmentsDefinition.detect(config_location))
|
|
50
|
+
definition
|
|
33
51
|
else
|
|
34
52
|
fail_with_error("Could not find an environment definition in current working directory.")
|
|
53
|
+
nil
|
|
35
54
|
end
|
|
36
55
|
end
|
|
37
56
|
end
|
data/lib/rascal/cli/clean.rb
CHANGED
|
@@ -4,12 +4,19 @@ module Rascal
|
|
|
4
4
|
module CLI
|
|
5
5
|
class Clean < Base
|
|
6
6
|
def initialize(thor, options, environment_name)
|
|
7
|
-
@environment_name =
|
|
7
|
+
@environment_name = if options[:all]
|
|
8
|
+
fail_with_error('Cannot give --all and an environment name!') if environment_name
|
|
9
|
+
:all
|
|
10
|
+
else
|
|
11
|
+
environment_name
|
|
12
|
+
end
|
|
8
13
|
super(thor, options)
|
|
9
14
|
end
|
|
10
15
|
|
|
11
16
|
def run
|
|
12
|
-
|
|
17
|
+
each_environment(@environment_name) do |environment|
|
|
18
|
+
environment.clean(clean_volumes: @options[:volumes])
|
|
19
|
+
end
|
|
13
20
|
end
|
|
14
21
|
end
|
|
15
22
|
end
|
data/lib/rascal/cli/main.rb
CHANGED
|
@@ -31,15 +31,37 @@ module Rascal
|
|
|
31
31
|
map "shell" => "_shell"
|
|
32
32
|
desc 'shell ENVIRONMENT', 'Start a docker shell for the given environment'
|
|
33
33
|
def _shell(environment_name = nil)
|
|
34
|
-
|
|
34
|
+
handle_error do
|
|
35
|
+
Shell.new(self, options, environment_name).run
|
|
36
|
+
end
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
desc 'clean ENVIRONMENT', 'Stop and remove docker containers for the given environment'
|
|
40
|
+
method_option :volumes, type: :boolean, default: false, desc: 'Remove (cache) volumes'
|
|
41
|
+
method_option :all, type: :boolean, default: false, desc: 'Clean all environments'
|
|
38
42
|
def clean(environment_name = nil)
|
|
39
|
-
|
|
43
|
+
handle_error do
|
|
44
|
+
Clean.new(self, options, environment_name).run
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
desc 'update ENVIRONMENT', 'Update all docker images'
|
|
49
|
+
method_option :all, type: :boolean, default: false, desc: 'update all available environments'
|
|
50
|
+
def update(environment_name = nil)
|
|
51
|
+
handle_error do
|
|
52
|
+
Update.new(self, options, environment_name).run
|
|
53
|
+
end
|
|
40
54
|
end
|
|
41
55
|
|
|
42
|
-
class_option :config_file, aliases: ['-c'], default: '.', required: true,
|
|
56
|
+
class_option :config_file, aliases: ['-c'], default: '.', required: true, desc: 'path to configuration file or directory containing it'
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def handle_error
|
|
61
|
+
yield
|
|
62
|
+
rescue Rascal::Error => e
|
|
63
|
+
raise Thor::Error, e.message
|
|
64
|
+
end
|
|
43
65
|
end
|
|
44
66
|
end
|
|
45
67
|
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'rascal'
|
|
2
|
+
|
|
3
|
+
module Rascal
|
|
4
|
+
module CLI
|
|
5
|
+
class Update < Base
|
|
6
|
+
def initialize(thor, options, environment_name)
|
|
7
|
+
@environment_name = if options[:all]
|
|
8
|
+
fail_with_error('Cannot give --all and an environment name!') if environment_name
|
|
9
|
+
:all
|
|
10
|
+
else
|
|
11
|
+
environment_name
|
|
12
|
+
end
|
|
13
|
+
super(thor, options)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def run
|
|
17
|
+
images = []
|
|
18
|
+
each_environment(@environment_name) do |environment|
|
|
19
|
+
images += environment.update(skip: images)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -3,6 +3,8 @@ module Rascal
|
|
|
3
3
|
class Container
|
|
4
4
|
include IOHelper
|
|
5
5
|
|
|
6
|
+
attr_reader :image
|
|
7
|
+
|
|
6
8
|
def initialize(name, image)
|
|
7
9
|
@name = name
|
|
8
10
|
@prefixed_name = "#{NAME_PREFIX}#{name}"
|
|
@@ -26,7 +28,7 @@ module Rascal
|
|
|
26
28
|
'container',
|
|
27
29
|
'inspect',
|
|
28
30
|
id,
|
|
29
|
-
output: :json
|
|
31
|
+
output: :json,
|
|
30
32
|
).first
|
|
31
33
|
!!container_info.dig('State', 'Running')
|
|
32
34
|
else
|
|
@@ -38,9 +40,9 @@ module Rascal
|
|
|
38
40
|
!!id
|
|
39
41
|
end
|
|
40
42
|
|
|
41
|
-
def start(network: nil, network_alias: nil)
|
|
43
|
+
def start(network: nil, network_alias: nil, volumes: [], env: {})
|
|
42
44
|
say "Starting container for #{@name}"
|
|
43
|
-
create(network: network, network_alias: network_alias) unless exists?
|
|
45
|
+
create(network: network, network_alias: network_alias, volumes: volumes, env: env) unless exists?
|
|
44
46
|
Docker.interface.run(
|
|
45
47
|
'container',
|
|
46
48
|
'start',
|
|
@@ -48,34 +50,65 @@ module Rascal
|
|
|
48
50
|
)
|
|
49
51
|
end
|
|
50
52
|
|
|
51
|
-
def create(network: nil, network_alias: nil)
|
|
53
|
+
def create(network: nil, network_alias: nil, volumes: [], env: {})
|
|
52
54
|
@id = Docker.interface.run(
|
|
53
55
|
'container',
|
|
54
56
|
'create',
|
|
55
57
|
'--name', @prefixed_name,
|
|
58
|
+
*(volumes.flat_map { |v| ['-v', v.to_param] }),
|
|
59
|
+
*env_args(env),
|
|
56
60
|
*(['--network', network.id] if network),
|
|
57
61
|
*(['--network-alias', network_alias] if network_alias),
|
|
58
62
|
@image,
|
|
59
|
-
output: :id
|
|
63
|
+
output: :id,
|
|
60
64
|
)
|
|
61
65
|
end
|
|
62
66
|
|
|
63
67
|
def run_and_attach(*command, env: {}, network: nil, volumes: [], working_dir: nil, allow_failure: false)
|
|
64
|
-
Docker.interface.run_and_attach(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
Docker.interface.run_and_attach(
|
|
69
|
+
'container',
|
|
70
|
+
'run',
|
|
71
|
+
'--rm',
|
|
72
|
+
'-a', 'STDOUT',
|
|
73
|
+
'-a', 'STDERR',
|
|
74
|
+
'-a', 'STDIN',
|
|
75
|
+
'--interactive',
|
|
76
|
+
'--tty',
|
|
77
|
+
*(['-w', working_dir] if working_dir),
|
|
78
|
+
*(volumes.flat_map { |v| ['-v', v.to_param] }),
|
|
79
|
+
*env_args(env),
|
|
80
|
+
*(['--network', network.id] if network),
|
|
81
|
+
@image,
|
|
82
|
+
*command,
|
|
83
|
+
redirect_io: {
|
|
84
|
+
out: stdout,
|
|
85
|
+
err: stderr,
|
|
86
|
+
in: stdin,
|
|
87
|
+
},
|
|
88
|
+
allow_failure: allow_failure,
|
|
73
89
|
)
|
|
74
90
|
end
|
|
75
91
|
|
|
76
92
|
def clean
|
|
77
|
-
|
|
78
|
-
|
|
93
|
+
if running?
|
|
94
|
+
say "Stopping container for #{@name}"
|
|
95
|
+
stop_container
|
|
96
|
+
end
|
|
97
|
+
if exists?
|
|
98
|
+
say "Removing container for #{@name}"
|
|
99
|
+
remove_container
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def update(skip: [])
|
|
104
|
+
return if skip.include?(@image)
|
|
105
|
+
say "Updating image #{@image}"
|
|
106
|
+
Docker.interface.run(
|
|
107
|
+
'pull',
|
|
108
|
+
@image,
|
|
109
|
+
stdout: stdout,
|
|
110
|
+
)
|
|
111
|
+
@image
|
|
79
112
|
end
|
|
80
113
|
|
|
81
114
|
private
|
|
@@ -87,7 +120,7 @@ module Rascal
|
|
|
87
120
|
'--all',
|
|
88
121
|
'--quiet',
|
|
89
122
|
'--filter', "name=^/#{@prefixed_name}$",
|
|
90
|
-
output: :id
|
|
123
|
+
output: :id,
|
|
91
124
|
)
|
|
92
125
|
end
|
|
93
126
|
|
|
@@ -116,6 +149,10 @@ module Rascal
|
|
|
116
149
|
id,
|
|
117
150
|
)
|
|
118
151
|
end
|
|
152
|
+
|
|
153
|
+
def env_args(env)
|
|
154
|
+
env.flat_map { |key, value| ['-e', "#{key}=#{value}"] }
|
|
155
|
+
end
|
|
119
156
|
end
|
|
120
157
|
end
|
|
121
158
|
end
|
|
@@ -6,7 +6,7 @@ module Rascal
|
|
|
6
6
|
class Interface
|
|
7
7
|
class Error < Rascal::Error; end
|
|
8
8
|
|
|
9
|
-
def run(*command, output: :ignore, stdout: nil,
|
|
9
|
+
def run(*command, output: :ignore, stdout: nil, allow_failure: false)
|
|
10
10
|
save_stdout = ''
|
|
11
11
|
save_stderr = ''
|
|
12
12
|
exit_status = nil
|
|
@@ -22,60 +22,11 @@ module Rascal
|
|
|
22
22
|
unless allow_failure || exit_status.success?
|
|
23
23
|
raise Error, "docker command '#{command.join(' ')}' failed with error:\n#{save_stderr}"
|
|
24
24
|
end
|
|
25
|
-
|
|
26
|
-
when :json
|
|
27
|
-
begin
|
|
28
|
-
JSON.parse(save_stdout)
|
|
29
|
-
rescue JSON::ParserError
|
|
30
|
-
raise Error, "could not parse output of docker command '#{command.join(' ')}':\n#{save_stdout}"
|
|
31
|
-
end
|
|
32
|
-
when :id
|
|
33
|
-
save_stdout[/[0-9a-f]+/]
|
|
34
|
-
when :ignore
|
|
35
|
-
nil
|
|
36
|
-
else
|
|
37
|
-
raise ArgumentError, 'unknown option for :output'
|
|
38
|
-
end
|
|
25
|
+
parse_output(output, save_stdout, save_stderr, command: command.join(' '))
|
|
39
26
|
end
|
|
40
27
|
|
|
41
|
-
def run_and_attach(
|
|
42
|
-
|
|
43
|
-
args = []
|
|
44
|
-
if stdout
|
|
45
|
-
process_redirections[:out] = stdout
|
|
46
|
-
args += ['-a', 'STDOUT']
|
|
47
|
-
end
|
|
48
|
-
if stderr
|
|
49
|
-
process_redirections[:err] = stderr
|
|
50
|
-
args += ['-a', 'STDERR']
|
|
51
|
-
end
|
|
52
|
-
if stdin
|
|
53
|
-
process_redirections[:in] = stdin
|
|
54
|
-
args += ['-a', 'STDIN', '--interactive', '--tty']
|
|
55
|
-
end
|
|
56
|
-
if working_dir
|
|
57
|
-
args += ['-w', working_dir.to_s]
|
|
58
|
-
end
|
|
59
|
-
volumes.each do |volume|
|
|
60
|
-
args += ['-v', volume.to_param]
|
|
61
|
-
end
|
|
62
|
-
env.each do |key, value|
|
|
63
|
-
args += ['-e', "#{key}=#{value}"]
|
|
64
|
-
end
|
|
65
|
-
if network
|
|
66
|
-
args += ['--network', network.to_s]
|
|
67
|
-
end
|
|
68
|
-
exit_status = spawn(
|
|
69
|
-
env,
|
|
70
|
-
'docker',
|
|
71
|
-
'container',
|
|
72
|
-
'run',
|
|
73
|
-
'--rm',
|
|
74
|
-
*args,
|
|
75
|
-
image.to_s,
|
|
76
|
-
*stringify_command(command),
|
|
77
|
-
process_redirections,
|
|
78
|
-
)
|
|
28
|
+
def run_and_attach(*command, stdout: nil, stderr: nil, stdin: nil, env: {}, network: nil, volumes: [], working_dir: nil, redirect_io: {}, allow_failure: false)
|
|
29
|
+
exit_status = spawn(env, 'docker', *stringify_command(command), redirect_io)
|
|
79
30
|
unless allow_failure || exit_status.success?
|
|
80
31
|
raise Error, "docker container run failed"
|
|
81
32
|
end
|
|
@@ -106,6 +57,23 @@ module Rascal
|
|
|
106
57
|
rescue IOError
|
|
107
58
|
end
|
|
108
59
|
end
|
|
60
|
+
|
|
61
|
+
def parse_output(output, stdout, stderr, command:)
|
|
62
|
+
case output
|
|
63
|
+
when :json
|
|
64
|
+
begin
|
|
65
|
+
JSON.parse(stdout)
|
|
66
|
+
rescue JSON::ParserError
|
|
67
|
+
raise Error, "could not parse output of docker command '#{command}':\n#{stdout}"
|
|
68
|
+
end
|
|
69
|
+
when :id
|
|
70
|
+
stdout[/[0-9a-f]+/]
|
|
71
|
+
when :ignore
|
|
72
|
+
nil
|
|
73
|
+
else
|
|
74
|
+
raise ArgumentError, 'unknown option for :output'
|
|
75
|
+
end
|
|
76
|
+
end
|
|
109
77
|
end
|
|
110
78
|
end
|
|
111
79
|
end
|
data/lib/rascal/docker/volume.rb
CHANGED
|
@@ -2,6 +2,7 @@ module Rascal
|
|
|
2
2
|
module Docker
|
|
3
3
|
module Volume
|
|
4
4
|
class Base
|
|
5
|
+
include IOHelper
|
|
5
6
|
end
|
|
6
7
|
|
|
7
8
|
class Named < Base
|
|
@@ -15,10 +16,23 @@ module Rascal
|
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
def clean
|
|
19
|
+
if exists?
|
|
20
|
+
say "Removing volume #{@prefixed_name}"
|
|
21
|
+
Docker.interface.run(
|
|
22
|
+
'volume',
|
|
23
|
+
'rm',
|
|
24
|
+
@prefixed_name,
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def exists?
|
|
18
30
|
Docker.interface.run(
|
|
19
31
|
'volume',
|
|
20
|
-
'
|
|
21
|
-
|
|
32
|
+
'ls',
|
|
33
|
+
'--quiet',
|
|
34
|
+
'--filter', "name=^#{@prefixed_name}$",
|
|
35
|
+
output: :id,
|
|
22
36
|
)
|
|
23
37
|
end
|
|
24
38
|
end
|
data/lib/rascal/environment.rb
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
module Rascal
|
|
2
2
|
class Environment
|
|
3
|
-
attr_reader :name
|
|
3
|
+
attr_reader :name, :network, :container, :env_variables, :services, :volumes, :working_dir, :before_shell
|
|
4
4
|
|
|
5
|
-
def initialize(
|
|
5
|
+
def initialize(full_name, name:, image:, env_variables: {}, services: [], volumes: [], before_shell: [], after_shell: [], working_dir: nil)
|
|
6
6
|
@name = name
|
|
7
|
-
@network = Docker::Network.new(
|
|
8
|
-
@container = Docker::Container.new(
|
|
7
|
+
@network = Docker::Network.new(full_name)
|
|
8
|
+
@container = Docker::Container.new(full_name, image)
|
|
9
9
|
@env_variables = env_variables
|
|
10
10
|
@services = services
|
|
11
11
|
@volumes = volumes
|
|
12
12
|
@working_dir = working_dir
|
|
13
13
|
@before_shell = before_shell
|
|
14
|
+
@after_shell = after_shell
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def run_shell
|
|
17
18
|
download_missing
|
|
18
19
|
start_services
|
|
19
|
-
command = [*@before_shell, 'bash'].join(';')
|
|
20
|
+
command = [*@before_shell, 'bash', *@after_shell].join(';')
|
|
20
21
|
@container.run_and_attach('bash', '-c', command,
|
|
21
22
|
env: @env_variables,
|
|
22
23
|
network: @network,
|
|
@@ -26,10 +27,17 @@ module Rascal
|
|
|
26
27
|
)
|
|
27
28
|
end
|
|
28
29
|
|
|
29
|
-
def clean
|
|
30
|
+
def clean(clean_volumes: false)
|
|
30
31
|
@services.each(&:clean)
|
|
31
32
|
@network.clean
|
|
32
|
-
@volumes.each(&:clean)
|
|
33
|
+
@volumes.each(&:clean) if clean_volumes
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def update(skip: [])
|
|
37
|
+
[
|
|
38
|
+
*@services.collect { |s| s.update(skip: skip) },
|
|
39
|
+
*@container.update(skip: skip),
|
|
40
|
+
]
|
|
33
41
|
end
|
|
34
42
|
|
|
35
43
|
private
|
|
@@ -35,7 +35,9 @@ module Rascal
|
|
|
35
35
|
def initialize(config_path)
|
|
36
36
|
@info = parse_definition(config_path.read)
|
|
37
37
|
@repo_dir = config_path.parent
|
|
38
|
+
@base_name = @repo_dir.basename
|
|
38
39
|
@rascal_config = @info.fetch('.rascal', {})
|
|
40
|
+
@rascal_environment_config = @rascal_config.delete('jobs') || {}
|
|
39
41
|
end
|
|
40
42
|
|
|
41
43
|
def environment(name)
|
|
@@ -51,21 +53,34 @@ module Rascal
|
|
|
51
53
|
private
|
|
52
54
|
|
|
53
55
|
def parse_definition(yaml)
|
|
54
|
-
|
|
56
|
+
if Psych::VERSION >= '3.1'
|
|
57
|
+
YAML.safe_load(yaml, aliases: true)
|
|
58
|
+
else
|
|
59
|
+
YAML.safe_load(yaml, [], [], true)
|
|
60
|
+
end
|
|
55
61
|
end
|
|
56
62
|
|
|
57
63
|
def environments
|
|
58
64
|
@environments ||= begin
|
|
59
65
|
@info.collect do |key, environment_config|
|
|
60
|
-
config = Config.new(deep_merge(environment_config, @rascal_config), key)
|
|
61
|
-
docker_repo_dir = config.get('repo_dir')
|
|
62
|
-
unless key.start_with?('.')
|
|
63
|
-
|
|
66
|
+
config = Config.new(deep_merge(environment_config, @rascal_config, @rascal_environment_config[key] || {}), key)
|
|
67
|
+
docker_repo_dir = config.get('repo_dir', '/repo')
|
|
68
|
+
unless key.start_with?('.') || config.get('hide', false)
|
|
69
|
+
name = config.get('name', key)
|
|
70
|
+
full_name = "#{@base_name}-#{name}"
|
|
71
|
+
shared_volumes = [build_repo_volume(docker_repo_dir), build_builds_volume(full_name)]
|
|
72
|
+
env_variables = (config.get('variables', {}))
|
|
73
|
+
Environment.new(full_name,
|
|
74
|
+
name: name,
|
|
64
75
|
image: config.get('image'),
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
76
|
+
volumes: [
|
|
77
|
+
*shared_volumes,
|
|
78
|
+
*build_volumes(full_name, config.get('volumes', {}))
|
|
79
|
+
],
|
|
80
|
+
env_variables: env_variables,
|
|
81
|
+
services: build_services(full_name, config.get('services', []), volumes: shared_volumes, env_variables: env_variables),
|
|
68
82
|
before_shell: config.get('before_shell', []),
|
|
83
|
+
after_shell: config.get('after_shell', []),
|
|
69
84
|
working_dir: docker_repo_dir,
|
|
70
85
|
)
|
|
71
86
|
end
|
|
@@ -73,9 +88,9 @@ module Rascal
|
|
|
73
88
|
end
|
|
74
89
|
end
|
|
75
90
|
|
|
76
|
-
def deep_merge(hash1, hash2)
|
|
91
|
+
def deep_merge(hash1, hash2, *other)
|
|
92
|
+
result = {}
|
|
77
93
|
if hash1.is_a?(Hash) && hash2.is_a?(Hash)
|
|
78
|
-
result = {}
|
|
79
94
|
hash1.each do |key1, value1|
|
|
80
95
|
if hash2.has_key?(key1)
|
|
81
96
|
result[key1] = deep_merge(value1, hash2[key1])
|
|
@@ -86,18 +101,24 @@ module Rascal
|
|
|
86
101
|
hash2.each do |key2, value2|
|
|
87
102
|
result[key2] ||= value2
|
|
88
103
|
end
|
|
89
|
-
result
|
|
90
104
|
else
|
|
91
|
-
hash2
|
|
105
|
+
result = hash2
|
|
106
|
+
end
|
|
107
|
+
if other.any?
|
|
108
|
+
deep_merge(result, *other)
|
|
109
|
+
else
|
|
110
|
+
result
|
|
92
111
|
end
|
|
93
112
|
end
|
|
94
113
|
|
|
95
|
-
def build_services(name, services)
|
|
114
|
+
def build_services(name, services, volumes: [], env_variables: {})
|
|
96
115
|
services.collect do |service_config|
|
|
97
116
|
service_alias = service_config['alias']
|
|
98
117
|
Service.new("#{name}_#{service_alias}",
|
|
99
118
|
alias_name: service_config['alias'],
|
|
100
119
|
image: service_config['name'],
|
|
120
|
+
volumes: volumes,
|
|
121
|
+
env_variables: env_variables,
|
|
101
122
|
)
|
|
102
123
|
end
|
|
103
124
|
end
|
|
@@ -106,6 +127,10 @@ module Rascal
|
|
|
106
127
|
Docker::Volume::Bind.new(@repo_dir, docker_repo_dir)
|
|
107
128
|
end
|
|
108
129
|
|
|
130
|
+
def build_builds_volume(name)
|
|
131
|
+
Docker::Volume::Named.new("#{name}-builds", '/builds')
|
|
132
|
+
end
|
|
133
|
+
|
|
109
134
|
def build_volumes(name, volume_config)
|
|
110
135
|
volume_config.collect do |volume_name, docker_path|
|
|
111
136
|
Docker::Volume::Named.new("#{name}-#{volume_name}", docker_path)
|
data/lib/rascal/service.rb
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
module Rascal
|
|
2
2
|
class Service
|
|
3
|
-
attr_reader :name
|
|
3
|
+
attr_reader :name, :container, :alias, :env_variables
|
|
4
4
|
|
|
5
|
-
def initialize(name, image:, alias_name:)
|
|
5
|
+
def initialize(name, env_variables: {}, image:, alias_name:, volumes: [])
|
|
6
6
|
@name = name
|
|
7
7
|
@container = Docker::Container.new(name, image)
|
|
8
8
|
@alias = alias_name
|
|
9
|
+
@volumes = volumes
|
|
10
|
+
@env_variables = env_variables
|
|
9
11
|
end
|
|
10
12
|
|
|
11
13
|
def download_missing
|
|
@@ -14,12 +16,16 @@ module Rascal
|
|
|
14
16
|
|
|
15
17
|
def start_if_stopped(network: nil)
|
|
16
18
|
unless @container.running?
|
|
17
|
-
@container.start(network: network, network_alias: @alias)
|
|
19
|
+
@container.start(network: network, network_alias: @alias, volumes: @volumes, env: @env_variables)
|
|
18
20
|
end
|
|
19
21
|
end
|
|
20
22
|
|
|
21
23
|
def clean
|
|
22
24
|
@container.clean
|
|
23
25
|
end
|
|
26
|
+
|
|
27
|
+
def update(**args)
|
|
28
|
+
@container.update(**args)
|
|
29
|
+
end
|
|
24
30
|
end
|
|
25
31
|
end
|
data/lib/rascal/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rascal
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tobias Kraze
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-10-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: thor
|
|
@@ -56,6 +56,7 @@ files:
|
|
|
56
56
|
- lib/rascal/cli/clean.rb
|
|
57
57
|
- lib/rascal/cli/main.rb
|
|
58
58
|
- lib/rascal/cli/shell.rb
|
|
59
|
+
- lib/rascal/cli/update.rb
|
|
59
60
|
- lib/rascal/docker.rb
|
|
60
61
|
- lib/rascal/docker/container.rb
|
|
61
62
|
- lib/rascal/docker/interface.rb
|
|
@@ -90,7 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
90
91
|
- !ruby/object:Gem::Version
|
|
91
92
|
version: '0'
|
|
92
93
|
requirements: []
|
|
93
|
-
rubygems_version: 3.
|
|
94
|
+
rubygems_version: 3.1.4
|
|
94
95
|
signing_key:
|
|
95
96
|
specification_version: 4
|
|
96
97
|
summary: Spin up CI environments locally.
|