contained 0.6.0 → 0.8.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 +5 -0
- data/README.md +40 -12
- data/lib/contained/cli/deploy.rb +1 -1
- data/lib/contained/cli/init.rb +4 -2
- data/lib/contained/cli/setup.rb +1 -1
- data/lib/contained/command/base.rb +6 -9
- data/lib/contained/command/docker_login.rb +46 -0
- data/lib/contained/command/docker_logout.rb +32 -0
- data/lib/contained/task/base.rb +40 -0
- data/lib/contained/task/deploy.rb +31 -0
- data/lib/contained/{command → task}/setup.rb +12 -7
- data/lib/contained/version.rb +1 -1
- data/lib/contained.rb +2 -2
- data/templates/config/deploy/compose.yml +10 -0
- data/templates/config/deploy.yml +21 -0
- metadata +9 -7
- data/lib/contained/command/deploy.rb +0 -26
- data/lib/contained/command/login.rb +0 -24
- data/lib/contained/command.rb +0 -6
- data/lib/contained/config/registry.rb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de44c4a9646409b70c951f868cf1f60d753f72f6f14f9c68df242a493033b285
|
4
|
+
data.tar.gz: e460580a09f0fc0c18261b32d2d3cbaa7420324b938ea1f7a64325cfcaef0759
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3f08b9ae3eb66e9ca3cf22e2daf4d3091c2ad1b010453b8b6f328151a1d67c0f0174016350a06757f43fc9f2d32b07d198e2967a38648857c52965c50aa3a4c
|
7
|
+
data.tar.gz: 2c5351508f495084d7b303216047452fd2cd8b13c017ca2b9fedccbe7e26c59bd3f5d955e1124f46f73de3da8c063d6e8044a1961bab1b6fc3ecde92e0531beb
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,28 @@
|
|
1
1
|
# Contained
|
2
2
|
|
3
|
-
|
3
|
+
[](https://github.com/ksylvest/contained/blob/main/LICENSE)
|
4
|
+
[](https://rubygems.org/gems/contained)
|
5
|
+
[](https://github.com/ksylvest/contained)
|
6
|
+
[](https://contained.ksylvest.com)
|
7
|
+
[](https://circleci.com/gh/ksylvest/contained)
|
8
|
+
|
9
|
+
Deploy using [Docker Swarm](https://docs.docker.com/engine/swarm/) via SSH across multiple and environments.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
```bash
|
14
|
+
gem install contained
|
15
|
+
```
|
16
|
+
|
17
|
+
## Commands
|
18
|
+
|
19
|
+
### Init
|
20
|
+
|
21
|
+
The `contained init` command copies / pastes the following into your project:
|
22
|
+
|
23
|
+
```bash
|
24
|
+
contained init
|
25
|
+
```
|
4
26
|
|
5
27
|
**config/deploy.yml**
|
6
28
|
|
@@ -8,13 +30,19 @@
|
|
8
30
|
stack: example
|
9
31
|
|
10
32
|
environments:
|
11
|
-
production:
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
33
|
+
production:
|
34
|
+
hosts:
|
35
|
+
- 1.2.3.4
|
36
|
+
- 5.6.7.8
|
37
|
+
test:
|
38
|
+
hosts:
|
39
|
+
- 4.3.2.1
|
40
|
+
- 8.7.6.5
|
41
|
+
```
|
17
42
|
|
43
|
+
**config/deploy/compose.yml**
|
44
|
+
|
45
|
+
```yaml
|
18
46
|
config:
|
19
47
|
services:
|
20
48
|
caddy:
|
@@ -48,15 +76,15 @@ config:
|
|
48
76
|
caddy_config:
|
49
77
|
```
|
50
78
|
|
51
|
-
|
52
|
-
|
53
|
-
|
79
|
+
### Setup
|
80
|
+
|
81
|
+
The `contained setup` command connects to all the hosts within an environment via SSH and configures docker swarm using a stack defined in the **config/deploy/compose.yml** file.
|
54
82
|
|
55
83
|
```bash
|
56
|
-
contained setup
|
84
|
+
contained setup --environment production
|
57
85
|
```
|
58
86
|
|
59
|
-
##
|
87
|
+
## Examples
|
60
88
|
|
61
89
|
### w/ Caddy
|
62
90
|
|
data/lib/contained/cli/deploy.rb
CHANGED
@@ -11,7 +11,7 @@ module Contained
|
|
11
11
|
config = Contained::Config.load_file(path: arguments.fetch(:config))
|
12
12
|
environment = arguments.fetch(:environment)
|
13
13
|
|
14
|
-
Contained::
|
14
|
+
Contained::Task::Deploy.execute!(stdin: @stdin, stdout: @stdout, environment:, config:)
|
15
15
|
end
|
16
16
|
|
17
17
|
private
|
data/lib/contained/cli/init.rb
CHANGED
@@ -61,7 +61,8 @@ module Contained
|
|
61
61
|
def setup_config_deploy_yml(path:)
|
62
62
|
if File.exist?("#{path}/config/deploy.yml")
|
63
63
|
@stdout.puts("overwrite '#{path}/config/deploy.yml' (yes/no)?")
|
64
|
-
@stdin.gets
|
64
|
+
response = @stdin.gets
|
65
|
+
return unless response.strip.eql?("yes")
|
65
66
|
end
|
66
67
|
|
67
68
|
File.write("#{path}/config/deploy.yml", CONFIG_DEPLOY_YML)
|
@@ -71,7 +72,8 @@ module Contained
|
|
71
72
|
def setup_config_deploy_compose_yml(path:)
|
72
73
|
if File.exist?("#{path}/config/deploy/compose.yml")
|
73
74
|
@stdout.puts("overwrite '#{path}/config/deploy/compose.yml' (yes/no)?")
|
74
|
-
|
75
|
+
response = @stdin.get
|
76
|
+
return unless response.strip.eql?("yes")
|
75
77
|
end
|
76
78
|
|
77
79
|
File.write("#{path}/config/deploy/compose.yml", CONFIG_DEPLOY_COMPOSE_YML)
|
data/lib/contained/cli/setup.rb
CHANGED
@@ -11,7 +11,7 @@ module Contained
|
|
11
11
|
config = Contained::Config.load_file(path: arguments.fetch(:config))
|
12
12
|
environment = arguments.fetch(:environment)
|
13
13
|
|
14
|
-
Contained::
|
14
|
+
Contained::Task::Setup.execute!(stdin: @stdin, stdout: @stdout, environment:, config:)
|
15
15
|
end
|
16
16
|
|
17
17
|
private
|
@@ -8,28 +8,25 @@ module Contained
|
|
8
8
|
class Base
|
9
9
|
include SSHKit::DSL
|
10
10
|
|
11
|
-
def self.
|
12
|
-
new(...).
|
11
|
+
def self.command(...)
|
12
|
+
new(...).command
|
13
13
|
end
|
14
14
|
|
15
|
-
# @param stdin [IO]
|
16
|
-
# @param stdout [IO]
|
17
15
|
# @environment [String]
|
18
16
|
# @config [Contained::Config]
|
19
|
-
def initialize(
|
20
|
-
@stdin = stdin
|
21
|
-
@stdout = stdout
|
17
|
+
def initialize(environment:, config:)
|
22
18
|
@environment = environment
|
23
19
|
@config = config
|
24
20
|
end
|
25
21
|
|
26
|
-
|
22
|
+
# @return [Array<Symbol,String>] e.g. [:docker, :login, "hub.docker.com", "-u", "root", "-p", "secret"]
|
23
|
+
def command
|
27
24
|
raise NotImplementedError, "#execute! undefined"
|
28
25
|
end
|
29
26
|
|
30
27
|
protected
|
31
28
|
|
32
|
-
# @return [Array<Hash
|
29
|
+
# @return [Array<Hash<{ hostname: String }>>]
|
33
30
|
def hosts
|
34
31
|
@config.dig("environments", @environment, "hosts").map do |hostname|
|
35
32
|
@config.fetch("ssh", {}).merge({ hostname: })
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Contained
|
4
|
+
module Command
|
5
|
+
# User to `docker login` to a registry using a provided username and password.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# Contained::Task::Login.command(environment: "production", config: {
|
9
|
+
# registry: {
|
10
|
+
# server: "hub.docker.com",
|
11
|
+
# username: "root",
|
12
|
+
# password: "secret",
|
13
|
+
# }) # => [:docker, :login, "hub.docker.com", "-u", "root", "-p", "secret"]
|
14
|
+
class DockerLogin < Base
|
15
|
+
DEFAULT_SERVER = "hub.docker.com"
|
16
|
+
|
17
|
+
# @return [Array<Symbol,String>]
|
18
|
+
def command
|
19
|
+
[
|
20
|
+
:docker,
|
21
|
+
:login,
|
22
|
+
server,
|
23
|
+
"-u", username,
|
24
|
+
"-p", password,
|
25
|
+
]
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# @return [String, nil]
|
31
|
+
def username
|
32
|
+
@config.dig("registry", "username")
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [String, nil]
|
36
|
+
def password
|
37
|
+
@config.dig("registry", "username")
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [String]
|
41
|
+
def server
|
42
|
+
@config.dig("registry", "server") || DEFAULT_SERVER
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Contained
|
4
|
+
module Command
|
5
|
+
# User to `docker login` to a registry using a provided username and password.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# Contained::Task::Login.command(environment: "production", config: {
|
9
|
+
# registry: {
|
10
|
+
# server: "hub.docker.com",
|
11
|
+
# }) # => [:docker, :login, "hub.docker.com"]
|
12
|
+
class DockerLogout < Base
|
13
|
+
DEFAULT_SERVER = "hub.docker.com"
|
14
|
+
|
15
|
+
# @return [Array<Symbol,String>]
|
16
|
+
def command
|
17
|
+
[
|
18
|
+
:docker,
|
19
|
+
:logout,
|
20
|
+
server,
|
21
|
+
]
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# @return [String]
|
27
|
+
def server
|
28
|
+
@config.dig("registry", "server") || DEFAULT_SERVER
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "sshkit"
|
4
|
+
|
5
|
+
module Contained
|
6
|
+
module Task
|
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
|
+
# @return [Array<Hash<{ hostname: String }>>]
|
33
|
+
def hosts
|
34
|
+
@config.dig("environments", @environment, "hosts").map do |hostname|
|
35
|
+
@config.fetch("ssh", {}).merge({ hostname: })
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# .contained/itva/compose.yml
|
4
|
+
# .contained/itva/.env
|
5
|
+
module Contained
|
6
|
+
module Task
|
7
|
+
# @example
|
8
|
+
# Contained::Task::Deploy.execute(environment: "production", config: {})
|
9
|
+
class Deploy < Base
|
10
|
+
def execute!
|
11
|
+
stack = @config.fetch("stack")
|
12
|
+
registry = @config.fetch("registry", { "server" => "hub.docker.com" })
|
13
|
+
username = ENV["DOCKER_LOGIN"]
|
14
|
+
password = ENV["DOCKER_PASSWORD"]
|
15
|
+
|
16
|
+
@stdout.puts("[deploy] stack=#{stack} environment=#{@environment}")
|
17
|
+
|
18
|
+
on hosts, in: :sequence do
|
19
|
+
if username && password
|
20
|
+
execute(:docker, :login, registry.fetch("server", "hub.docker.com"), "-u", username, "-p", password)
|
21
|
+
end
|
22
|
+
|
23
|
+
execute(<<~BASH)
|
24
|
+
source ./.contained/#{stack}/.env
|
25
|
+
docker stack deploy -c ./.contained/#{stack}/compose.yml #{stack} --with-registry-auth
|
26
|
+
BASH
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -1,20 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Contained
|
4
|
-
module
|
4
|
+
module Task
|
5
5
|
# @example
|
6
|
-
# Contained::
|
6
|
+
# Contained::Task::Setup.execute(environment: "production", config: {})
|
7
7
|
class Setup < Base
|
8
8
|
def execute!
|
9
9
|
stack = @config.fetch("stack")
|
10
|
+
registry = @config.fetch("registry", { "server" => "hub.docker.com" })
|
11
|
+
username = ENV["DOCKER_LOGIN"]
|
12
|
+
password = ENV["DOCKER_PASSWORD"]
|
10
13
|
|
11
14
|
deploy_compose_yml = File.open("./config/deploy/compose.yml")
|
12
15
|
deploy_env = File.open("./config/deploy/.env.#{@environment}")
|
13
16
|
|
14
|
-
@stdout.puts("[setup] stack=#{stack} environment=#{@environment}
|
17
|
+
@stdout.puts("[setup] stack=#{stack} environment=#{@environment}")
|
15
18
|
|
16
|
-
on hosts, in: :sequence do
|
17
|
-
|
19
|
+
on hosts, in: :sequence do
|
20
|
+
execute(:docker, :swarm, :init) unless capture(:docker, :info).include?("Swarm: active")
|
21
|
+
|
22
|
+
if username && password
|
23
|
+
execute(:docker, :login, registry.fetch("server", "hub.docker.com"), "-u", username, "-p", password)
|
24
|
+
end
|
18
25
|
|
19
26
|
execute("mkdir -p ./.contained/#{stack}")
|
20
27
|
|
@@ -25,8 +32,6 @@ module Contained
|
|
25
32
|
source ./.contained/#{stack}/.env
|
26
33
|
docker stack deploy -c ./.contained/#{stack}/compose.yml #{stack} --with-registry-auth
|
27
34
|
BASH
|
28
|
-
|
29
|
-
puts("[done] host=#{host.hostname}")
|
30
35
|
end
|
31
36
|
end
|
32
37
|
end
|
data/lib/contained/version.rb
CHANGED
data/lib/contained.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
stack: example
|
2
|
+
|
3
|
+
registry:
|
4
|
+
server: hub.docker.com
|
5
|
+
username:
|
6
|
+
- DOCKER_USERNAME
|
7
|
+
password:
|
8
|
+
- DOCKER_PASSWORD
|
9
|
+
|
10
|
+
ssh:
|
11
|
+
user: root
|
12
|
+
|
13
|
+
environments:
|
14
|
+
production:
|
15
|
+
hosts:
|
16
|
+
- 1.2.3.4
|
17
|
+
- 5.6.7.8
|
18
|
+
test:
|
19
|
+
hosts:
|
20
|
+
- 4.3.2.1
|
21
|
+
- 8.7.6.5
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: contained
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Sylvestre
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-03-
|
10
|
+
date: 2025-03-24 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: erb
|
@@ -112,14 +112,16 @@ files:
|
|
112
112
|
- lib/contained/cli/deploy.rb
|
113
113
|
- lib/contained/cli/init.rb
|
114
114
|
- lib/contained/cli/setup.rb
|
115
|
-
- lib/contained/command.rb
|
116
115
|
- lib/contained/command/base.rb
|
117
|
-
- lib/contained/command/
|
118
|
-
- lib/contained/command/
|
119
|
-
- lib/contained/command/setup.rb
|
116
|
+
- lib/contained/command/docker_login.rb
|
117
|
+
- lib/contained/command/docker_logout.rb
|
120
118
|
- lib/contained/config.rb
|
121
|
-
- lib/contained/
|
119
|
+
- lib/contained/task/base.rb
|
120
|
+
- lib/contained/task/deploy.rb
|
121
|
+
- lib/contained/task/setup.rb
|
122
122
|
- lib/contained/version.rb
|
123
|
+
- templates/config/deploy.yml
|
124
|
+
- templates/config/deploy/compose.yml
|
123
125
|
homepage: https://github.com/ksylvest/contained
|
124
126
|
licenses:
|
125
127
|
- MIT
|
@@ -1,26 +0,0 @@
|
|
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 hosts, 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
|
@@ -1,24 +0,0 @@
|
|
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
|
data/lib/contained/command.rb
DELETED
File without changes
|