contained 0.2.0 → 0.4.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/Gemfile +4 -6
- data/README.md +89 -21
- data/exe/contained +7 -0
- data/lib/contained/cli/base.rb +28 -0
- data/lib/contained/cli/deploy.rb +47 -0
- data/lib/contained/cli/init.rb +106 -0
- data/lib/contained/cli/setup.rb +48 -0
- data/lib/contained/cli.rb +62 -0
- data/lib/contained/command/base.rb +37 -0
- data/lib/contained/command/deploy.rb +26 -0
- data/lib/contained/command/login.rb +24 -0
- data/lib/contained/command/setup.rb +34 -0
- data/lib/contained/command.rb +6 -0
- data/lib/contained/config/registry.rb +0 -0
- data/lib/contained/config.rb +56 -0
- data/lib/contained/version.rb +1 -1
- data/lib/contained.rb +6 -1
- metadata +103 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b0cb59a30f87b9f812ea82750eaf3ec2aa709646117f2bd3cd267b74a0a93f4b
|
|
4
|
+
data.tar.gz: 3f0de08deaa75360d26125eaf1c3e82a1c068a2357eb93d69f9bb15ebe9b7ac8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 20db27de1725d9aceffaa923e919315b12beb3f1ec7bedc969af903da579f491fd087ce90fa4c86edbf40a88f4898537f9b3419649159a2c3a17ab739edcc6b5
|
|
7
|
+
data.tar.gz: 3781c551e71dd8ae8e0fd81bf38d07c9e75b64d1f08eb8371359a409575c86636383fc0ec7428f1fe6fb29a41079fb315ff0aee050e5ca71acb1d593dfa561c6
|
data/Gemfile
CHANGED
|
@@ -2,12 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
source "https://rubygems.org"
|
|
4
4
|
|
|
5
|
-
# Specify your gem's dependencies in contained.gemspec
|
|
6
5
|
gemspec
|
|
7
6
|
|
|
8
7
|
gem "irb"
|
|
9
|
-
gem "rake"
|
|
10
|
-
|
|
11
|
-
gem "
|
|
12
|
-
|
|
13
|
-
gem "rubocop", "~> 1.21"
|
|
8
|
+
gem "rake"
|
|
9
|
+
gem "rspec"
|
|
10
|
+
gem "rubocop"
|
|
11
|
+
gem "rubocop-basic"
|
data/README.md
CHANGED
|
@@ -1,39 +1,107 @@
|
|
|
1
1
|
# Contained
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
## Example
|
|
4
|
+
|
|
5
|
+
**config/deploy.yml**
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
stack: example
|
|
9
|
+
|
|
10
|
+
environments:
|
|
11
|
+
production:
|
|
12
|
+
servers:
|
|
13
|
+
- 1.2.3.4
|
|
14
|
+
- 5.6.7.8
|
|
15
|
+
test:
|
|
16
|
+
servers:
|
|
17
|
+
- 4.3.2.1
|
|
18
|
+
- 8.7.6.5
|
|
19
|
+
|
|
20
|
+
config:
|
|
21
|
+
services:
|
|
22
|
+
caddy:
|
|
23
|
+
image: demo/caddy
|
|
24
|
+
ports:
|
|
25
|
+
- "80:80"
|
|
26
|
+
volumes:
|
|
27
|
+
- caddy_data:/data
|
|
28
|
+
- caddy_config:/config
|
|
29
|
+
healthcheck:
|
|
30
|
+
test: ["CMD", "curl", "-f", "http://localhost:80/up"]
|
|
31
|
+
interval: 30s
|
|
32
|
+
timeout: 10s
|
|
33
|
+
retries: 3
|
|
34
|
+
backend:
|
|
35
|
+
build: demo/backend
|
|
36
|
+
healthcheck:
|
|
37
|
+
test: ["CMD", "curl", "-f", "http://localhost:3000/up"]
|
|
38
|
+
interval: 30s
|
|
39
|
+
timeout: 10s
|
|
40
|
+
retries: 3
|
|
41
|
+
frontend:
|
|
42
|
+
build: demo/frontend
|
|
43
|
+
healthcheck:
|
|
44
|
+
test: ["CMD", "curl", "-f", "http://localhost:3000/up"]
|
|
45
|
+
interval: 30s
|
|
46
|
+
timeout: 10s
|
|
47
|
+
retries: 3
|
|
48
|
+
volumes:
|
|
49
|
+
caddy_data:
|
|
50
|
+
caddy_config:
|
|
51
|
+
```
|
|
12
52
|
|
|
13
53
|
```bash
|
|
14
|
-
|
|
54
|
+
gem install contained
|
|
15
55
|
```
|
|
16
56
|
|
|
17
|
-
If bundler is not being used to manage dependencies, install the gem by executing:
|
|
18
|
-
|
|
19
57
|
```bash
|
|
20
|
-
|
|
58
|
+
contained setup
|
|
21
59
|
```
|
|
22
60
|
|
|
23
61
|
## Usage
|
|
24
62
|
|
|
25
|
-
|
|
63
|
+
### w/ Caddy
|
|
26
64
|
|
|
27
|
-
|
|
65
|
+
**Dockerfile**
|
|
28
66
|
|
|
29
|
-
|
|
67
|
+
```dockerfile
|
|
68
|
+
# syntax = docker/dockerfile:1
|
|
30
69
|
|
|
31
|
-
|
|
70
|
+
ARG CADDY_VERSION=2.9.1
|
|
32
71
|
|
|
33
|
-
|
|
72
|
+
FROM docker.io/library/caddy:${CADDY_VERSION}-alpine
|
|
34
73
|
|
|
35
|
-
|
|
74
|
+
COPY Caddyfile /etc/caddy/Caddyfile
|
|
75
|
+
```
|
|
36
76
|
|
|
37
|
-
|
|
77
|
+
**Caddyfile**
|
|
38
78
|
|
|
39
|
-
|
|
79
|
+
```caddyfile
|
|
80
|
+
demo.com {
|
|
81
|
+
encode
|
|
82
|
+
|
|
83
|
+
handle /up {
|
|
84
|
+
respond "OK" 200 {
|
|
85
|
+
close
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
handle /backend/up {
|
|
90
|
+
uri replace /backend/ /
|
|
91
|
+
reverse_proxy backend:3000
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
handle /frontend/up {
|
|
95
|
+
uri replace /frontend/ /
|
|
96
|
+
reverse_proxy frontend:3000
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
handle /api/* {
|
|
100
|
+
reverse_proxy backend:3000
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
handle /* {
|
|
104
|
+
reverse_proxy frontend:3000
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
data/exe/contained
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Contained
|
|
4
|
+
class CLI
|
|
5
|
+
# @abstract
|
|
6
|
+
class Base
|
|
7
|
+
# @param stdin [IO]
|
|
8
|
+
# @param stdout [IO]
|
|
9
|
+
# @param argv [Array<String>]
|
|
10
|
+
def self.handle!(stdin:, stdout:, argv:)
|
|
11
|
+
new(stdin: stdin, stdout: stdout, argv: argv).handle!
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @param stdin [IO]
|
|
15
|
+
# @param stdout [IO]
|
|
16
|
+
# @param argv [Array<String>]
|
|
17
|
+
def initialize(stdin:, stdout:, argv:)
|
|
18
|
+
@stdin = stdin
|
|
19
|
+
@stdout = stdout
|
|
20
|
+
@argv = argv
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def handle!
|
|
24
|
+
raise NotImplementedError, "#handle! undefined"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Contained
|
|
4
|
+
class CLI
|
|
5
|
+
# @example
|
|
6
|
+
# Contained::CLI::Deploy.handle!(stdout: $stdout, stderr: $stderr, argv: %i[--environment demo])
|
|
7
|
+
class Deploy < Contained::CLI::Base
|
|
8
|
+
def handle!
|
|
9
|
+
arguments = arguments!
|
|
10
|
+
|
|
11
|
+
config = Contained::Config.load_file(path: arguments.fetch(:config))
|
|
12
|
+
environment = arguments.fetch(:environment)
|
|
13
|
+
|
|
14
|
+
Contained::Command::Deploy.execute!(stdin: @stdin, stdout: @stdout, environment:, config:)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
# @return [Hash]
|
|
20
|
+
def arguments!
|
|
21
|
+
{ config: "./config/deploy.yml", environment: "default" }.tap do |arguments|
|
|
22
|
+
parser(into: arguments).parse!
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @param into [Hash]
|
|
27
|
+
# @return [OptionParser]
|
|
28
|
+
def parser(into:)
|
|
29
|
+
OptionParser.new do |options|
|
|
30
|
+
options.banner = "usage: contained deploy [options] "
|
|
31
|
+
|
|
32
|
+
options.on("-h", "--help", "help") do
|
|
33
|
+
@stdout.puts(options)
|
|
34
|
+
exit
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
options.on("-c", "--config=CONFIG", "config") do |config|
|
|
38
|
+
into[:config] = config
|
|
39
|
+
end
|
|
40
|
+
options.on("-e", "--environment=ENVIRONMENT", "environment") do |environment|
|
|
41
|
+
into[:environment] = environment
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
module Contained
|
|
6
|
+
class CLI
|
|
7
|
+
# @example
|
|
8
|
+
# Contained::CLI::Init.handle!(stdout: $stdout, stderr: $stderr, argv: %i[--image example/app --stack app])
|
|
9
|
+
class Init < Base
|
|
10
|
+
Arguments = Data.define(:image, :stack)
|
|
11
|
+
|
|
12
|
+
CONFIG_DEPLOY_COMPOSE_YML = <<~YAML
|
|
13
|
+
services:
|
|
14
|
+
app:
|
|
15
|
+
image: example/app
|
|
16
|
+
ports:
|
|
17
|
+
- "80:80"
|
|
18
|
+
healthcheck:
|
|
19
|
+
test: ["CMD", "curl", "-f", "http://localhost/up"]
|
|
20
|
+
interval: 30s
|
|
21
|
+
timeout: 10s
|
|
22
|
+
retries: 3
|
|
23
|
+
YAML
|
|
24
|
+
|
|
25
|
+
CONFIG_DEPLOY_YML = <<~YAML
|
|
26
|
+
stack: example
|
|
27
|
+
|
|
28
|
+
registry:
|
|
29
|
+
server: hub.docker.com
|
|
30
|
+
username:
|
|
31
|
+
- DOCKER_REGISTRY_TOKEN
|
|
32
|
+
password:
|
|
33
|
+
- DOCKER_REGISTRY_TOKEN
|
|
34
|
+
|
|
35
|
+
environments:
|
|
36
|
+
production:
|
|
37
|
+
servers:
|
|
38
|
+
- 1.2.3.4
|
|
39
|
+
- 5.6.7.8
|
|
40
|
+
test:
|
|
41
|
+
servers:
|
|
42
|
+
- 4.3.2.1
|
|
43
|
+
- 8.7.6.5
|
|
44
|
+
YAML
|
|
45
|
+
|
|
46
|
+
def handle!
|
|
47
|
+
arguments = arguments!
|
|
48
|
+
path = arguments.fetch(:path)
|
|
49
|
+
|
|
50
|
+
setup_config_dir(path:)
|
|
51
|
+
setup_config_deploy_yml(path:)
|
|
52
|
+
setup_config_deploy_compose_yml(path:)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def setup_config_dir(path:)
|
|
58
|
+
FileUtils.mkdir_p("#{path}/config/deploy")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def setup_config_deploy_yml(path:)
|
|
62
|
+
if File.exist?("#{path}/config/deploy.yml")
|
|
63
|
+
@stdout.puts("overwrite '#{path}/config/deploy.yml' (yes/no)?")
|
|
64
|
+
@stdin.gets { |response| return unless response.strip.eql?("yes") }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
File.write("#{path}/config/deploy.yml", CONFIG_DEPLOY_YML)
|
|
68
|
+
@stdout.puts("setup '#{path}/config/deploy.yml'")
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def setup_config_deploy_compose_yml(path:)
|
|
72
|
+
if File.exist?("#{path}/config/deploy/compose.yml")
|
|
73
|
+
@stdout.puts("overwrite '#{path}/config/deploy/compose.yml' (yes/no)?")
|
|
74
|
+
@stdin.gets { |response| return unless response.strip.eql?("yes") }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
File.write("#{path}/config/deploy/compose.yml", CONFIG_DEPLOY_COMPOSE_YML)
|
|
78
|
+
@stdout.puts("setup '#{path}/config/deploy/compose.yml'")
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# @return [Arguments]
|
|
82
|
+
def arguments!
|
|
83
|
+
{ path: "." }.tap do |arguments|
|
|
84
|
+
parser(into: arguments).parse!(@argv)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# @param into [Arguments]
|
|
89
|
+
# @return [OptionParser]
|
|
90
|
+
def parser(into:)
|
|
91
|
+
OptionParser.new do |options|
|
|
92
|
+
options.banner = "usage: contained init [options]"
|
|
93
|
+
|
|
94
|
+
options.on("-p", "--path=PATH", "path") do |path|
|
|
95
|
+
into[:path] = path
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
options.on("-h", "--help", "help") do
|
|
99
|
+
@stdout.puts(options)
|
|
100
|
+
exit
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Contained
|
|
4
|
+
class CLI
|
|
5
|
+
# @example
|
|
6
|
+
# Contained::CLI::Setup.handle!(stdout: $stdout, stderr: $stderr, argv: %i[--environment demo])
|
|
7
|
+
class Setup < Contained::CLI::Base
|
|
8
|
+
def handle!
|
|
9
|
+
arguments = arguments!
|
|
10
|
+
|
|
11
|
+
config = Contained::Config.load_file(path: arguments.fetch(:config))
|
|
12
|
+
environment = arguments.fetch(:environment)
|
|
13
|
+
|
|
14
|
+
Contained::Command::Setup.execute!(stdin: @stdin, stdout: @stdout, environment:, config:)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
# @return [Hash]
|
|
20
|
+
def arguments!
|
|
21
|
+
{ config: "./config/deploy.yml", environment: "default" }.tap do |arguments|
|
|
22
|
+
parser(into: arguments).parse!
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @param into [Hash]
|
|
27
|
+
# @return [OptionParser]
|
|
28
|
+
def parser(into:)
|
|
29
|
+
OptionParser.new do |options|
|
|
30
|
+
options.banner = "usage: contained setup [options] "
|
|
31
|
+
|
|
32
|
+
options.on("-h", "--help", "help") do
|
|
33
|
+
@stdout.puts(options)
|
|
34
|
+
exit
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
options.on("-c", "--config=CONFIG", "config") do |config|
|
|
38
|
+
into[:config] = config
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
options.on("-e", "--environment=ENVIRONMENT", "environment") do |environment|
|
|
42
|
+
into[:environment] = environment
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "optparse"
|
|
4
|
+
|
|
5
|
+
module Contained
|
|
6
|
+
# @example
|
|
7
|
+
#
|
|
8
|
+
# cli = Contained::CLI.new
|
|
9
|
+
# cli.parse
|
|
10
|
+
class CLI
|
|
11
|
+
# @param input [IO]
|
|
12
|
+
# @param output [IO]
|
|
13
|
+
# @param argv [Array<String>] the arguments (e.g. ['deploy', '--help'])
|
|
14
|
+
def initialize(stdin: $stdin, stdout: $stdout, argv: ARGV)
|
|
15
|
+
@stdin = stdin
|
|
16
|
+
@stdout = stdout
|
|
17
|
+
@argv = argv
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def parse
|
|
21
|
+
parser.order!(@argv)
|
|
22
|
+
command = @argv.shift
|
|
23
|
+
return if command.nil?
|
|
24
|
+
|
|
25
|
+
handler =
|
|
26
|
+
case command
|
|
27
|
+
when "init" then ::Contained::CLI::Init
|
|
28
|
+
when "setup" then ::Contained::CLI::Setup
|
|
29
|
+
when "deploy" then ::Contained::CLI::Deploy
|
|
30
|
+
else raise ::Contained::Error, "unsupported command=#{command.inspect}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
handler.handle!(stdin: @stdin, stdout: @stdout, argv: @argv)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
# @return [OptionParser]
|
|
39
|
+
def parser
|
|
40
|
+
@parser ||= OptionParser.new do |options|
|
|
41
|
+
options.banner = "usage: contained [options] <command> [<args>]"
|
|
42
|
+
|
|
43
|
+
options.on("-h", "--help", "help") do
|
|
44
|
+
@stdout.puts(options)
|
|
45
|
+
exit
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
options.on("-v", "--version", "version") do
|
|
49
|
+
@stdout.puts(Contained::VERSION)
|
|
50
|
+
exit
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
options.separator <<~COMMANDS
|
|
54
|
+
commands:
|
|
55
|
+
init
|
|
56
|
+
setup
|
|
57
|
+
deploy
|
|
58
|
+
COMMANDS
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "sshkit"
|
|
4
|
+
|
|
5
|
+
module Contained
|
|
6
|
+
module Command
|
|
7
|
+
# @abstract
|
|
8
|
+
class Base
|
|
9
|
+
include SSHKit::DSL
|
|
10
|
+
|
|
11
|
+
def self.execute!(...)
|
|
12
|
+
new(...).execute!
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @param stdin [IO]
|
|
16
|
+
# @param stdout [IO]
|
|
17
|
+
# @environment [String]
|
|
18
|
+
# @config [Contained::Config]
|
|
19
|
+
def initialize(stdin:, stdout:, environment:, config:)
|
|
20
|
+
@stdin = stdin
|
|
21
|
+
@stdout = stdout
|
|
22
|
+
@environment = environment
|
|
23
|
+
@config = config
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def execute!
|
|
27
|
+
raise NotImplementedError, "#execute! undefined"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
protected
|
|
31
|
+
|
|
32
|
+
def servers
|
|
33
|
+
@config.dig("environments", @environment, "servers")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# .contained/itva/compose.yml
|
|
4
|
+
# .contained/itva/.env
|
|
5
|
+
module Contained
|
|
6
|
+
module Command
|
|
7
|
+
# @example
|
|
8
|
+
# Contained::Command::Deploy.execute(environment: "production", config: {})
|
|
9
|
+
class Deploy < Base
|
|
10
|
+
def execute!
|
|
11
|
+
stack = @config.fetch("stack")
|
|
12
|
+
|
|
13
|
+
@stdout.puts("[deploying] stack=#{stack} environment=#{@environment}")
|
|
14
|
+
|
|
15
|
+
on servers, in: :sequence do |host|
|
|
16
|
+
puts("[starting] host=#{host.hostname}")
|
|
17
|
+
execute(<<~BASH)
|
|
18
|
+
source ./.contained/#{stack}/.env
|
|
19
|
+
docker stack deploy -c ./.contained/#{stack}/compose.yml #{stack} --with-registry-auth
|
|
20
|
+
BASH
|
|
21
|
+
puts("[done] host=#{host.hostname}")
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
class Contained::Command::Login
|
|
2
|
+
# @param stdin [IO]
|
|
3
|
+
# @param stdout [IO]
|
|
4
|
+
# @param argv [Array<String>]
|
|
5
|
+
def initialize(stdin:, stdout:, argv:)
|
|
6
|
+
@stdin = stdin
|
|
7
|
+
@stdout = stdout
|
|
8
|
+
@argv = argv
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def execute!
|
|
12
|
+
raise NotImplementedError, "#handle! undefined"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @return [String]
|
|
16
|
+
def username!
|
|
17
|
+
@options.fetch(:username)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @return [String]
|
|
21
|
+
def password
|
|
22
|
+
@options.fetch(:password)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Contained
|
|
4
|
+
module Command
|
|
5
|
+
# @example
|
|
6
|
+
# Contained::Command::Setup.execute(environment: "production", config: {})
|
|
7
|
+
class Setup < Base
|
|
8
|
+
def execute!
|
|
9
|
+
stack = @config.fetch("stack")
|
|
10
|
+
|
|
11
|
+
deploy_compose_yml = File.open("./config/deploy/compose.yml")
|
|
12
|
+
deploy_env = File.open("./config/deploy/.#{@environment}.env")
|
|
13
|
+
|
|
14
|
+
@stdout.puts("[setup] stack=#{stack} environment=#{@environment} servers=#{servers}")
|
|
15
|
+
|
|
16
|
+
on servers, in: :sequence do |host|
|
|
17
|
+
puts("[starting] host=#{host.hostname}")
|
|
18
|
+
|
|
19
|
+
execute("mkdir -p ./.contained/#{stack}")
|
|
20
|
+
|
|
21
|
+
upload!(deploy_compose_yml, "./.contained/#{stack}/compose.yml")
|
|
22
|
+
upload!(deploy_env, "./.contained/#{stack}/.env")
|
|
23
|
+
|
|
24
|
+
execute(<<~BASH)
|
|
25
|
+
source ./.contained/#{stack}/.env
|
|
26
|
+
docker stack deploy -c ./.contained/#{stack}/compose.yml #{stack} --with-registry-auth
|
|
27
|
+
BASH
|
|
28
|
+
|
|
29
|
+
puts("[done] host=#{host.hostname}")
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
File without changes
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
|
|
5
|
+
module Contained
|
|
6
|
+
# @example
|
|
7
|
+
#
|
|
8
|
+
# Contained::Config.load_file(path: "./config/deploy.yml")
|
|
9
|
+
class Config
|
|
10
|
+
# @param path [String]
|
|
11
|
+
#
|
|
12
|
+
# @return [Contained::Config]
|
|
13
|
+
def self.load_file(path: "./config/deploy.yml")
|
|
14
|
+
YAML.load_file(path)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# # @param data [Hash]
|
|
18
|
+
|
|
19
|
+
# # @return [Contained::Config]
|
|
20
|
+
# def self.parse!(data:)
|
|
21
|
+
# new(
|
|
22
|
+
# stack: data.fetch("stack"),
|
|
23
|
+
# registry: data.fetch("registry"),
|
|
24
|
+
# environments: data.fetch("environments"),
|
|
25
|
+
# config: data.fetch("config")
|
|
26
|
+
# )
|
|
27
|
+
# end
|
|
28
|
+
|
|
29
|
+
# # @!attribute [rw] stack
|
|
30
|
+
# # @return [String]
|
|
31
|
+
# attr_accessor :stack
|
|
32
|
+
|
|
33
|
+
# # @!attribute [rw] registry
|
|
34
|
+
# # @return [Hash]
|
|
35
|
+
# attr_accessor :registry
|
|
36
|
+
|
|
37
|
+
# # @!attribute [rw] environments
|
|
38
|
+
# # @return [Hash]
|
|
39
|
+
# attr_accessor :environments
|
|
40
|
+
|
|
41
|
+
# # @!attribute [rw] config
|
|
42
|
+
# # @return [Hash]
|
|
43
|
+
# attr_accessor :config
|
|
44
|
+
|
|
45
|
+
# # @param stack [String]
|
|
46
|
+
# # @param registry [Hash]
|
|
47
|
+
# # @param environments [Hash]
|
|
48
|
+
# # @param config [Hash]
|
|
49
|
+
# def initialize(stack:, registry:, environments:, config:)
|
|
50
|
+
# @stack = stack
|
|
51
|
+
# @registry = registry
|
|
52
|
+
# @environments = environments
|
|
53
|
+
# @config = config
|
|
54
|
+
# end
|
|
55
|
+
end
|
|
56
|
+
end
|
data/lib/contained/version.rb
CHANGED
data/lib/contained.rb
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require 'zeitwerk'
|
|
4
|
+
require 'optparse'
|
|
5
|
+
|
|
6
|
+
loader = Zeitwerk::Loader.for_gem
|
|
7
|
+
loader.inflector.inflect "cli" => "CLI"
|
|
8
|
+
loader.setup
|
|
4
9
|
|
|
5
10
|
module Contained
|
|
6
11
|
class Error < StandardError; end
|
metadata
CHANGED
|
@@ -1,18 +1,103 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: contained
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kevin Sylvestre
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2025-03-
|
|
11
|
-
dependencies:
|
|
10
|
+
date: 2025-03-21 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: erb
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: fileutils
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: optparse
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: sshkit
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: yaml
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0'
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '0'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: zeitwerk
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0'
|
|
89
|
+
type: :runtime
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0'
|
|
12
96
|
description: Makes releasing docker based containers via SSH using swarm a breeze
|
|
13
97
|
email:
|
|
14
98
|
- kevin@ksylvest.com
|
|
15
|
-
executables:
|
|
99
|
+
executables:
|
|
100
|
+
- contained
|
|
16
101
|
extensions: []
|
|
17
102
|
extra_rdoc_files: []
|
|
18
103
|
files:
|
|
@@ -20,7 +105,20 @@ files:
|
|
|
20
105
|
- README.md
|
|
21
106
|
- bin/console
|
|
22
107
|
- bin/setup
|
|
108
|
+
- exe/contained
|
|
23
109
|
- lib/contained.rb
|
|
110
|
+
- lib/contained/cli.rb
|
|
111
|
+
- lib/contained/cli/base.rb
|
|
112
|
+
- lib/contained/cli/deploy.rb
|
|
113
|
+
- lib/contained/cli/init.rb
|
|
114
|
+
- lib/contained/cli/setup.rb
|
|
115
|
+
- lib/contained/command.rb
|
|
116
|
+
- lib/contained/command/base.rb
|
|
117
|
+
- lib/contained/command/deploy.rb
|
|
118
|
+
- lib/contained/command/login.rb
|
|
119
|
+
- lib/contained/command/setup.rb
|
|
120
|
+
- lib/contained/config.rb
|
|
121
|
+
- lib/contained/config/registry.rb
|
|
24
122
|
- lib/contained/version.rb
|
|
25
123
|
homepage: https://github.com/ksylvest/contained
|
|
26
124
|
licenses:
|
|
@@ -29,6 +127,7 @@ metadata:
|
|
|
29
127
|
homepage_uri: https://github.com/ksylvest/contained
|
|
30
128
|
source_code_uri: https://github.com/ksylvest/contained
|
|
31
129
|
changelog_uri: https://github.com/ksylvest/contained
|
|
130
|
+
rubygems_mfa_required: 'true'
|
|
32
131
|
rdoc_options: []
|
|
33
132
|
require_paths:
|
|
34
133
|
- lib
|