floe 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -1
- data/Gemfile +3 -0
- data/README.md +57 -3
- data/exe/floe +3 -3
- data/floe.gemspec +0 -4
- data/lib/floe/null_logger.rb +1 -1
- data/lib/floe/version.rb +1 -1
- data/lib/floe/workflow/choice_rule/and.rb +13 -0
- data/lib/floe/workflow/choice_rule/data.rb +6 -6
- data/lib/floe/workflow/choice_rule/not.rb +14 -0
- data/lib/floe/workflow/choice_rule/or.rb +13 -0
- data/lib/floe/workflow/choice_rule.rb +16 -12
- data/lib/floe/workflow/context.rb +51 -3
- data/lib/floe/workflow/payload_template.rb +56 -15
- data/lib/floe/workflow/runner/docker.rb +84 -15
- data/lib/floe/workflow/runner/kubernetes.rb +47 -15
- data/lib/floe/workflow/runner/podman.rb +119 -14
- data/lib/floe/workflow/runner.rb +21 -1
- data/lib/floe/workflow/state.rb +60 -0
- data/lib/floe/workflow/states/choice.rb +6 -4
- data/lib/floe/workflow/states/fail.rb +8 -4
- data/lib/floe/workflow/states/map.rb +1 -0
- data/lib/floe/workflow/states/parallel.rb +1 -0
- data/lib/floe/workflow/states/pass.rb +6 -4
- data/lib/floe/workflow/states/succeed.rb +6 -4
- data/lib/floe/workflow/states/task.rb +61 -16
- data/lib/floe/workflow/states/wait.rb +13 -6
- data/lib/floe/workflow.rb +60 -36
- data/lib/floe.rb +3 -1
- metadata +5 -45
- data/lib/floe/workflow/choice_rule/boolean.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7a1c4e51ef3ec3052e8c3c150070464ed1a8b463a3b1237774b8f1b2ff416dc
|
4
|
+
data.tar.gz: 8cd6fd6e8c7bed14635d906699f13dde1f5e666018f0c62709972dbddc1999cc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 73bf61a41d240922786746b0ace87c616311c4bd3f63ac59883096e53b0fce766ef791db3270a29cf257cd5315f5764d69c91bcfd4817efcadfaae72be4c226b
|
7
|
+
data.tar.gz: 3cb8f1ad58c7b1b7895363f46871b38b300a66eb40ebb69d2fc8ffd1b5ec303c982c0f49f89f9575b209b5e0671c2570e54670078da4e4916a3718050c9e0b9d
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,15 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
4
4
|
|
5
5
|
## [Unreleased]
|
6
6
|
|
7
|
+
## [0.4.0] - 2023-09-26
|
8
|
+
### Added
|
9
|
+
- Add ability to run workflows asynchronously ([#52](https://github.com/ManageIQ/floe/pull/92))
|
10
|
+
- Add Workflow.wait, Workflow#step_nonblock, Workflow#step_nonblock_wait ([#92](https://github.com/ManageIQ/floe/pull/92))
|
11
|
+
|
12
|
+
## [0.3.1] - 2023-08-29
|
13
|
+
### Added
|
14
|
+
- Add more global podman runner options ([#90])(https://github.com/ManageIQ/floe/pull/90)
|
15
|
+
|
7
16
|
## [0.3.0] - 2023-08-07
|
8
17
|
### Added
|
9
18
|
- Add --network=host option to Docker/Podman runners ([#81])(https://github.com/ManageIQ/floe/pull/81)
|
@@ -54,7 +63,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
54
63
|
### Added
|
55
64
|
- Initial release
|
56
65
|
|
57
|
-
[Unreleased]: https://github.com/ManageIQ/floe/compare/v0.
|
66
|
+
[Unreleased]: https://github.com/ManageIQ/floe/compare/v0.4.0...HEAD
|
67
|
+
[0.4.0]: https://github.com/ManageIQ/floe/compare/v0.3.1...v0.4.0
|
68
|
+
[0.3.1]: https://github.com/ManageIQ/floe/compare/v0.3.0...v0.3.1
|
58
69
|
[0.3.0]: https://github.com/ManageIQ/floe/compare/v0.2.3...v0.3.0
|
59
70
|
[0.2.3]: https://github.com/ManageIQ/floe/compare/v0.2.2...v0.2.3
|
60
71
|
[0.2.2]: https://github.com/ManageIQ/floe/compare/v0.2.1...v0.2.2
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -56,7 +56,7 @@ bundle exec ruby exe/floe --workflow my-workflow.asl --credentials='{"roleArn":
|
|
56
56
|
```ruby
|
57
57
|
require 'floe'
|
58
58
|
|
59
|
-
workflow = Floe::Workflow.load(
|
59
|
+
workflow = Floe::Workflow.load("workflow.asl")
|
60
60
|
workflow.run!
|
61
61
|
```
|
62
62
|
|
@@ -68,10 +68,51 @@ Floe::Workflow::Runner.docker_runner = Floe::Workflow::Runner::Podman.new
|
|
68
68
|
# Or
|
69
69
|
Floe::Workflow::Runner.docker_runner = Floe::Workflow::Runner::Kubernetes.new("namespace" => "default", "server" => "https://k8s.example.com:6443", "token" => "my-token")
|
70
70
|
|
71
|
-
workflow = Floe::Workflow.load(
|
71
|
+
workflow = Floe::Workflow.load("workflow.asl")
|
72
72
|
workflow.run!
|
73
73
|
```
|
74
74
|
|
75
|
+
### Non-Blocking Workflow Execution
|
76
|
+
|
77
|
+
It is also possible to step through a workflow without blocking, and any state which
|
78
|
+
would block will return `Errno::EAGAIN`.
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
require 'floe'
|
82
|
+
|
83
|
+
workflow = Floe::Workflow.load("workflow.asl")
|
84
|
+
|
85
|
+
# Step through the workflow while it would not block
|
86
|
+
workflow.run_nonblock
|
87
|
+
|
88
|
+
# Go off and do some other task
|
89
|
+
|
90
|
+
# Continue stepping until the workflow is finished
|
91
|
+
workflow.run_nonblock
|
92
|
+
```
|
93
|
+
|
94
|
+
You can also use the `Floe::Workflow.wait` class method to wait on multiple workflows
|
95
|
+
and return all that are ready to be stepped through.
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
require 'floe'
|
99
|
+
|
100
|
+
workflow1 = Floe::Workflow.load("workflow1.asl")
|
101
|
+
workflow2 = Floe::Workflow.load("workflow2.asl")
|
102
|
+
|
103
|
+
running_workflows = [workflow1, workflow2]
|
104
|
+
until running_workflows.empty?
|
105
|
+
# Wait for any of the running workflows to be ready (up to the timeout)
|
106
|
+
ready_workflows = Floe::Workflow.wait(running_workflows)
|
107
|
+
# Step through the ready workflows until they would block
|
108
|
+
ready_workflows.each do |workflow|
|
109
|
+
loop while workflow.step_nonblock == 0
|
110
|
+
end
|
111
|
+
# Remove any finished workflows from the list of running_workflows
|
112
|
+
running_workflows.reject!(&:end?)
|
113
|
+
end
|
114
|
+
```
|
115
|
+
|
75
116
|
### Docker Runner Options
|
76
117
|
|
77
118
|
#### Docker
|
@@ -84,7 +125,20 @@ Options supported by the Docker docker runner are:
|
|
84
125
|
|
85
126
|
Options supported by the podman docker runner are:
|
86
127
|
|
87
|
-
* `
|
128
|
+
* `identity=string` - path to SSH identity file, (CONTAINER_SSHKEY)
|
129
|
+
* `log-level=string` - Log messages above specified level (trace, debug, info, warn, warning, error, fatal, panic)
|
130
|
+
* `network=string` - What docker to connect the container to, defaults to `"bridge"`. If you need access to host resources for development you can pass `network=host`.
|
131
|
+
* `noout=boolean` - do not output to stdout
|
132
|
+
* `root=string` - Path to the root directory in which data, including images, is stored
|
133
|
+
* `runroot=string` - Path to the 'run directory' where all state information is stored
|
134
|
+
* `runtime=string` - Path to the OCI-compatible binary used to run containers
|
135
|
+
* `runtime-flag=stringArray` - add global flags for the container runtime
|
136
|
+
* `storage-driver=string` - Select which storage driver is used to manage storage of images and containers
|
137
|
+
* `storage-opt=stringArray` - Used to pass an option to the storage driver
|
138
|
+
* `syslog=boolean` - Output logging information to syslog as well as the console
|
139
|
+
* `tmpdir=string` - Path to the tmp directory for libpod state content
|
140
|
+
* `transient-store=boolean` - Enable transient container storage
|
141
|
+
* `volumepath=string` - Path to the volume directory in which volume data is stored
|
88
142
|
|
89
143
|
#### Kubernetes
|
90
144
|
|
data/exe/floe
CHANGED
@@ -18,9 +18,6 @@ Optimist.die(:docker_runner, "must be one of #{Floe::Workflow::Runner::TYPES.joi
|
|
18
18
|
require "logger"
|
19
19
|
Floe.logger = Logger.new($stdout)
|
20
20
|
|
21
|
-
context = Floe::Workflow::Context.new(input: opts[:input])
|
22
|
-
workflow = Floe::Workflow.load(opts[:workflow], context, opts[:credentials])
|
23
|
-
|
24
21
|
runner_klass = case opts[:docker_runner]
|
25
22
|
when "docker"
|
26
23
|
Floe::Workflow::Runner::Docker
|
@@ -34,6 +31,9 @@ runner_options = opts[:docker_runner_options].to_h { |opt| opt.split("=", 2) }
|
|
34
31
|
|
35
32
|
Floe::Workflow::Runner.docker_runner = runner_klass.new(runner_options)
|
36
33
|
|
34
|
+
context = Floe::Workflow::Context.new(:input => opts[:input])
|
35
|
+
workflow = Floe::Workflow.load(opts[:workflow], context, opts[:credentials])
|
36
|
+
|
37
37
|
workflow.run!
|
38
38
|
|
39
39
|
puts workflow.output.inspect
|
data/floe.gemspec
CHANGED
@@ -34,8 +34,4 @@ Gem::Specification.new do |spec|
|
|
34
34
|
spec.add_dependency "kubeclient", "~>4.7"
|
35
35
|
spec.add_dependency "more_core_extensions"
|
36
36
|
spec.add_dependency "optimist", "~>3.0"
|
37
|
-
|
38
|
-
spec.add_development_dependency "manageiq-style"
|
39
|
-
spec.add_development_dependency "rspec"
|
40
|
-
spec.add_development_dependency "rubocop"
|
41
37
|
end
|
data/lib/floe/null_logger.rb
CHANGED
data/lib/floe/version.rb
CHANGED
@@ -51,27 +51,27 @@ module Floe
|
|
51
51
|
raise "No such variable [#{variable}]" if value.nil? && !%w[IsNull IsPresent].include?(compare_key)
|
52
52
|
end
|
53
53
|
|
54
|
-
def is_null?(value)
|
54
|
+
def is_null?(value) # rubocop:disable Naming/PredicateName
|
55
55
|
value.nil?
|
56
56
|
end
|
57
57
|
|
58
|
-
def is_present?(value)
|
58
|
+
def is_present?(value) # rubocop:disable Naming/PredicateName
|
59
59
|
!value.nil?
|
60
60
|
end
|
61
61
|
|
62
|
-
def is_numeric?(value)
|
62
|
+
def is_numeric?(value) # rubocop:disable Naming/PredicateName
|
63
63
|
value.kind_of?(Integer) || value.kind_of?(Float)
|
64
64
|
end
|
65
65
|
|
66
|
-
def is_string?(value)
|
66
|
+
def is_string?(value) # rubocop:disable Naming/PredicateName
|
67
67
|
value.kind_of?(String)
|
68
68
|
end
|
69
69
|
|
70
|
-
def is_boolean?(value)
|
70
|
+
def is_boolean?(value) # rubocop:disable Naming/PredicateName
|
71
71
|
[true, false].include?(value)
|
72
72
|
end
|
73
73
|
|
74
|
-
def is_timestamp?(value)
|
74
|
+
def is_timestamp?(value) # rubocop:disable Naming/PredicateName
|
75
75
|
require "date"
|
76
76
|
|
77
77
|
DateTime.rfc3339(value)
|
@@ -4,24 +4,28 @@ module Floe
|
|
4
4
|
class Workflow
|
5
5
|
class ChoiceRule
|
6
6
|
class << self
|
7
|
-
def true?(payload, context, input)
|
8
|
-
build(payload).true?(context, input)
|
9
|
-
end
|
10
|
-
|
11
7
|
def build(payload)
|
12
|
-
|
13
|
-
|
14
|
-
|
8
|
+
if (sub_payloads = payload["Not"])
|
9
|
+
Floe::Workflow::ChoiceRule::Not.new(payload, build_children([sub_payloads]))
|
10
|
+
elsif (sub_payloads = payload["And"])
|
11
|
+
Floe::Workflow::ChoiceRule::And.new(payload, build_children(sub_payloads))
|
12
|
+
elsif (sub_payloads = payload["Or"])
|
13
|
+
Floe::Workflow::ChoiceRule::Or.new(payload, build_children(sub_payloads))
|
15
14
|
else
|
16
|
-
Floe::Workflow::ChoiceRule::
|
15
|
+
Floe::Workflow::ChoiceRule::Data.new(payload)
|
17
16
|
end
|
18
17
|
end
|
18
|
+
|
19
|
+
def build_children(sub_payloads)
|
20
|
+
sub_payloads.map { |payload| build(payload) }
|
21
|
+
end
|
19
22
|
end
|
20
23
|
|
21
|
-
attr_reader :next, :payload, :variable
|
24
|
+
attr_reader :next, :payload, :variable, :children
|
22
25
|
|
23
|
-
def initialize(payload)
|
24
|
-
@payload
|
26
|
+
def initialize(payload, children = nil)
|
27
|
+
@payload = payload
|
28
|
+
@children = children
|
25
29
|
|
26
30
|
@next = payload["Next"]
|
27
31
|
@variable = payload["Variable"]
|
@@ -34,7 +38,7 @@ module Floe
|
|
34
38
|
private
|
35
39
|
|
36
40
|
def variable_value(context, input)
|
37
|
-
|
41
|
+
Path.value(variable, context, input)
|
38
42
|
end
|
39
43
|
end
|
40
44
|
end
|
@@ -11,7 +11,7 @@ module Floe
|
|
11
11
|
"Input" => input
|
12
12
|
},
|
13
13
|
"State" => {},
|
14
|
-
"
|
14
|
+
"StateHistory" => [],
|
15
15
|
"StateMachine" => {},
|
16
16
|
"Task" => {}
|
17
17
|
}
|
@@ -21,16 +21,64 @@ module Floe
|
|
21
21
|
@context["Execution"]
|
22
22
|
end
|
23
23
|
|
24
|
+
def started?
|
25
|
+
execution.key?("StartTime")
|
26
|
+
end
|
27
|
+
|
28
|
+
def running?
|
29
|
+
started? && !ended?
|
30
|
+
end
|
31
|
+
|
32
|
+
def ended?
|
33
|
+
execution.key?("EndTime")
|
34
|
+
end
|
35
|
+
|
24
36
|
def state
|
25
37
|
@context["State"]
|
26
38
|
end
|
27
39
|
|
40
|
+
def input
|
41
|
+
state["Input"]
|
42
|
+
end
|
43
|
+
|
44
|
+
def output
|
45
|
+
state["Output"]
|
46
|
+
end
|
47
|
+
|
48
|
+
def output=(val)
|
49
|
+
state["Output"] = val
|
50
|
+
end
|
51
|
+
|
52
|
+
def state_name
|
53
|
+
state["Name"]
|
54
|
+
end
|
55
|
+
|
56
|
+
def next_state
|
57
|
+
state["NextState"]
|
58
|
+
end
|
59
|
+
|
60
|
+
def next_state=(val)
|
61
|
+
state["NextState"] = val
|
62
|
+
end
|
63
|
+
|
64
|
+
def status
|
65
|
+
if !started?
|
66
|
+
"pending"
|
67
|
+
elsif running?
|
68
|
+
"running"
|
69
|
+
elsif state["Error"]
|
70
|
+
"failure"
|
71
|
+
else
|
72
|
+
"success"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
28
76
|
def state=(val)
|
29
77
|
@context["State"] = val
|
30
78
|
end
|
31
79
|
|
32
|
-
def
|
33
|
-
@context["
|
80
|
+
def state_history
|
81
|
+
@context["StateHistory"]
|
34
82
|
end
|
35
83
|
|
36
84
|
def state_machine
|
@@ -4,35 +4,76 @@ module Floe
|
|
4
4
|
class Workflow
|
5
5
|
class PayloadTemplate
|
6
6
|
def initialize(payload)
|
7
|
-
@
|
7
|
+
@payload_template = parse_payload(payload)
|
8
8
|
end
|
9
9
|
|
10
10
|
def value(context, inputs = {})
|
11
|
-
|
11
|
+
interpolate_value(payload_template, context, inputs)
|
12
12
|
end
|
13
13
|
|
14
14
|
private
|
15
15
|
|
16
|
-
attr_reader :
|
16
|
+
attr_reader :payload_template
|
17
17
|
|
18
|
-
def
|
18
|
+
def parse_payload(value)
|
19
19
|
case value
|
20
|
-
when Array
|
21
|
-
|
22
|
-
when
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
20
|
+
when Array then parse_payload_array(value)
|
21
|
+
when Hash then parse_payload_hash(value)
|
22
|
+
when String then parse_payload_string(value)
|
23
|
+
else
|
24
|
+
value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse_payload_array(value)
|
29
|
+
value.map { |val| parse_payload(val) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def parse_payload_hash(value)
|
33
|
+
value.to_h do |key, val|
|
34
|
+
if key.end_with?(".$")
|
35
|
+
check_key_conflicts(key, value)
|
36
|
+
|
37
|
+
[key, parse_payload(val)]
|
38
|
+
else
|
39
|
+
[key, val]
|
29
40
|
end
|
30
|
-
|
31
|
-
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def parse_payload_string(value)
|
45
|
+
value.start_with?("$") ? Path.new(value) : value
|
46
|
+
end
|
47
|
+
|
48
|
+
def interpolate_value(value, context, inputs)
|
49
|
+
case value
|
50
|
+
when Array then interpolate_value_array(value, context, inputs)
|
51
|
+
when Hash then interpolate_value_hash(value, context, inputs)
|
52
|
+
when Path then value.value(context, inputs)
|
32
53
|
else
|
33
54
|
value
|
34
55
|
end
|
35
56
|
end
|
57
|
+
|
58
|
+
def interpolate_value_array(value, context, inputs)
|
59
|
+
value.map { |val| interpolate_value(val, context, inputs) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def interpolate_value_hash(value, context, inputs)
|
63
|
+
value.to_h do |key, val|
|
64
|
+
if key.end_with?(".$")
|
65
|
+
[key.chomp(".$"), interpolate_value(val, context, inputs)]
|
66
|
+
else
|
67
|
+
[key, val]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def check_key_conflicts(key, value)
|
73
|
+
if value.key?(key.chomp(".$"))
|
74
|
+
raise Floe::InvalidWorkflowError, "both #{key} and #{key.chomp(".$")} present"
|
75
|
+
end
|
76
|
+
end
|
36
77
|
end
|
37
78
|
end
|
38
79
|
end
|
@@ -18,34 +18,103 @@ module Floe
|
|
18
18
|
|
19
19
|
image = resource.sub("docker://", "")
|
20
20
|
|
21
|
-
params = ["run", :rm]
|
22
|
-
params += [[:net, "host"]] if network == "host"
|
23
|
-
params += env.map { |k, v| [:e, "#{k}=#{v}"] } if env
|
24
|
-
|
25
21
|
secrets_file = nil
|
22
|
+
if secrets && !secrets.empty?
|
23
|
+
secrets_file = create_secret(secrets)
|
24
|
+
env["_CREDENTIALS"] = "/run/secrets"
|
25
|
+
end
|
26
|
+
|
27
|
+
output = run_container(image, env, secrets_file)
|
28
|
+
|
29
|
+
{"exit_code" => 0, "output" => output}
|
30
|
+
ensure
|
31
|
+
cleanup({"secrets_ref" => secrets_file})
|
32
|
+
end
|
33
|
+
|
34
|
+
def run_async!(resource, env = {}, secrets = {})
|
35
|
+
raise ArgumentError, "Invalid resource" unless resource&.start_with?("docker://")
|
36
|
+
|
37
|
+
image = resource.sub("docker://", "")
|
38
|
+
|
39
|
+
runner_context = {}
|
26
40
|
|
27
41
|
if secrets && !secrets.empty?
|
28
|
-
|
29
|
-
|
30
|
-
|
42
|
+
runner_context["secrets_ref"] = create_secret(secrets)
|
43
|
+
env["_CREDENTIALS"] = "/run/secrets"
|
44
|
+
end
|
31
45
|
|
32
|
-
|
33
|
-
|
46
|
+
begin
|
47
|
+
runner_context["container_ref"] = run_container(image, env, runner_context["secrets_ref"], :detached => true)
|
48
|
+
rescue
|
49
|
+
cleanup(runner_context)
|
50
|
+
raise
|
34
51
|
end
|
35
52
|
|
36
|
-
|
53
|
+
runner_context
|
54
|
+
end
|
37
55
|
|
38
|
-
|
39
|
-
|
56
|
+
def cleanup(runner_context)
|
57
|
+
container_id, secrets_file = runner_context.values_at("container_ref", "secrets_ref")
|
40
58
|
|
41
|
-
|
42
|
-
|
43
|
-
|
59
|
+
delete_container(container_id) if container_id
|
60
|
+
File.unlink(secrets_file) if secrets_file && File.exist?(secrets_file)
|
61
|
+
end
|
62
|
+
|
63
|
+
def status!(runner_context)
|
64
|
+
runner_context["container_state"] = inspect_container(runner_context["container_ref"]).first&.dig("State")
|
65
|
+
end
|
66
|
+
|
67
|
+
def running?(runner_context)
|
68
|
+
runner_context.dig("container_state", "Running")
|
69
|
+
end
|
70
|
+
|
71
|
+
def success?(runner_context)
|
72
|
+
runner_context.dig("container_state", "ExitCode") == 0
|
73
|
+
end
|
74
|
+
|
75
|
+
def output(runner_context)
|
76
|
+
output = docker!("logs", runner_context["container_ref"]).output
|
77
|
+
runner_context["output"] = output
|
44
78
|
end
|
45
79
|
|
46
80
|
private
|
47
81
|
|
48
82
|
attr_reader :network
|
83
|
+
|
84
|
+
def run_container(image, env, secrets_file, detached: false)
|
85
|
+
params = ["run"]
|
86
|
+
params << (detached ? :detach : :rm)
|
87
|
+
params += env.map { |k, v| [:e, "#{k}=#{v}"] }
|
88
|
+
params << [:net, "host"] if @network == "host"
|
89
|
+
params << [:v, "#{secrets_file}:/run/secrets:z"] if secrets_file
|
90
|
+
params << image
|
91
|
+
|
92
|
+
logger.debug("Running docker: #{AwesomeSpawn.build_command_line("docker", params)}")
|
93
|
+
|
94
|
+
result = docker!(*params)
|
95
|
+
result.output
|
96
|
+
end
|
97
|
+
|
98
|
+
def inspect_container(container_id)
|
99
|
+
JSON.parse(docker!("inspect", container_id).output)
|
100
|
+
end
|
101
|
+
|
102
|
+
def delete_container(container_id)
|
103
|
+
docker!("rm", container_id)
|
104
|
+
rescue
|
105
|
+
nil
|
106
|
+
end
|
107
|
+
|
108
|
+
def create_secret(secrets)
|
109
|
+
secrets_file = Tempfile.new
|
110
|
+
secrets_file.write(secrets.to_json)
|
111
|
+
secrets_file.close
|
112
|
+
secrets_file.path
|
113
|
+
end
|
114
|
+
|
115
|
+
def docker!(*params, **kwargs)
|
116
|
+
AwesomeSpawn.run!("docker", :params => params, **kwargs)
|
117
|
+
end
|
49
118
|
end
|
50
119
|
end
|
51
120
|
end
|