mrsk 0.1.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/README.md +187 -52
- data/bin/mrsk +9 -1
- data/lib/mrsk/cli/accessory.rb +181 -0
- data/lib/mrsk/cli/app.rb +69 -29
- data/lib/mrsk/cli/base.rb +24 -3
- data/lib/mrsk/cli/build.rb +26 -10
- data/lib/mrsk/cli/main.rb +22 -1
- data/lib/mrsk/cli/prune.rb +2 -2
- data/lib/mrsk/cli/registry.rb +6 -2
- data/lib/mrsk/cli/server.rb +1 -1
- data/lib/mrsk/cli/traefik.rb +43 -10
- data/lib/mrsk/cli.rb +3 -3
- data/lib/mrsk/commander.rb +49 -6
- data/lib/mrsk/commands/accessory.rb +106 -0
- data/lib/mrsk/commands/app.rb +52 -8
- data/lib/mrsk/commands/base.rb +17 -9
- data/lib/mrsk/commands/builder/base.rb +26 -0
- data/lib/mrsk/commands/builder/multiarch/remote.rb +9 -6
- data/lib/mrsk/commands/builder/multiarch.rb +11 -8
- data/lib/mrsk/commands/builder/native/remote.rb +71 -0
- data/lib/mrsk/commands/builder/native.rb +3 -7
- data/lib/mrsk/commands/builder.rb +11 -7
- data/lib/mrsk/commands/traefik.rb +13 -3
- data/lib/mrsk/configuration/accessory.rb +116 -0
- data/lib/mrsk/configuration/role.rb +46 -14
- data/lib/mrsk/configuration.rb +87 -43
- data/lib/mrsk/sshkit_with_ext.rb +12 -0
- data/lib/mrsk/utils.rb +29 -0
- data/lib/mrsk/version.rb +1 -1
- metadata +10 -3
data/lib/mrsk/cli/app.rb
CHANGED
@@ -23,75 +23,115 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
23
23
|
option :version, desc: "Defaults to the most recent git-hash in local repository"
|
24
24
|
def start
|
25
25
|
if (version = options[:version]).present?
|
26
|
-
on(MRSK.
|
26
|
+
on(MRSK.hosts) { execute *MRSK.app.start(version: version) }
|
27
27
|
else
|
28
|
-
on(MRSK.
|
28
|
+
on(MRSK.hosts) { execute *MRSK.app.start, raise_on_non_zero_exit: false }
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
32
|
desc "stop", "Stop app on servers"
|
33
33
|
def stop
|
34
|
-
on(MRSK.
|
34
|
+
on(MRSK.hosts) { execute *MRSK.app.stop, raise_on_non_zero_exit: false }
|
35
35
|
end
|
36
36
|
|
37
37
|
desc "details", "Display details about app containers"
|
38
38
|
def details
|
39
|
-
on(MRSK.
|
39
|
+
on(MRSK.hosts) { |host| puts_by_host host, capture_with_info(*MRSK.app.info) }
|
40
40
|
end
|
41
41
|
|
42
|
-
desc "exec [CMD]", "Execute a custom
|
43
|
-
option :
|
42
|
+
desc "exec [CMD]", "Execute a custom command on servers"
|
43
|
+
option :method, aliases: "-m", default: "exec", desc: "Execution method: [exec] perform inside app container / [run] perform in new container / [ssh] perform over ssh"
|
44
44
|
def exec(cmd)
|
45
|
-
|
46
|
-
|
45
|
+
runner = \
|
46
|
+
case options[:method]
|
47
|
+
when "exec" then "exec"
|
48
|
+
when "run" then "run_exec"
|
49
|
+
when "ssh" then "exec_over_ssh"
|
50
|
+
else raise "Unknown method: #{options[:method]}"
|
51
|
+
end.inquiry
|
52
|
+
|
53
|
+
if runner.exec_over_ssh?
|
54
|
+
run_locally do
|
55
|
+
info "Launching command on #{MRSK.primary_host}"
|
56
|
+
exec MRSK.app.exec_over_ssh(cmd, host: MRSK.primary_host)
|
57
|
+
end
|
47
58
|
else
|
48
|
-
on(MRSK.
|
59
|
+
on(MRSK.hosts) { |host| puts_by_host host, capture_with_info(*MRSK.app.send(runner, cmd)) }
|
49
60
|
end
|
50
61
|
end
|
51
|
-
|
52
|
-
desc "console", "Start Rails Console on primary host"
|
53
|
-
option :host, desc: "Start console on a different host"
|
62
|
+
|
63
|
+
desc "console", "Start Rails Console on primary host (or specific host set by --hosts)"
|
54
64
|
def console
|
55
|
-
|
65
|
+
run_locally do
|
66
|
+
info "Launching Rails console on #{MRSK.primary_host}"
|
67
|
+
exec MRSK.app.console(host: MRSK.primary_host)
|
68
|
+
end
|
69
|
+
end
|
56
70
|
|
71
|
+
desc "bash", "Start a bash session on primary host (or specific host set by --hosts)"
|
72
|
+
def bash
|
57
73
|
run_locally do
|
58
|
-
|
59
|
-
exec MRSK.app.
|
74
|
+
info "Launching bash session on #{MRSK.primary_host}"
|
75
|
+
exec MRSK.app.bash(host: MRSK.primary_host)
|
60
76
|
end
|
61
77
|
end
|
62
78
|
|
63
79
|
desc "runner [EXPRESSION]", "Execute Rails runner with given expression"
|
64
|
-
option :once, type: :boolean, default: false, desc:
|
65
80
|
def runner(expression)
|
66
|
-
|
67
|
-
on(MRSK.config.primary_host) { puts capture(*MRSK.app.exec("bin/rails", "runner", "'#{expression}'"), verbosity: Logger::INFO) }
|
68
|
-
else
|
69
|
-
on(MRSK.config.hosts) { |host| puts "App Host: #{host}\n" + capture(*MRSK.app.exec("bin/rails", "runner", "'#{expression}'"), verbosity: Logger::INFO) + "\n\n" }
|
70
|
-
end
|
81
|
+
on(MRSK.hosts) { |host| puts_by_host host, capture_with_info(*MRSK.app.exec("bin/rails", "runner", "'#{expression}'")) }
|
71
82
|
end
|
72
83
|
|
73
84
|
desc "containers", "List all the app containers currently on servers"
|
74
85
|
def containers
|
75
|
-
on(MRSK.
|
86
|
+
on(MRSK.hosts) { |host| puts_by_host host, capture_with_info(*MRSK.app.list_containers) }
|
87
|
+
end
|
88
|
+
|
89
|
+
desc "current", "Return the current running container ID"
|
90
|
+
def current
|
91
|
+
on(MRSK.hosts) { |host| puts_by_host host, capture_with_info(*MRSK.app.current_container_id) }
|
76
92
|
end
|
77
93
|
|
78
|
-
desc "logs", "Show
|
94
|
+
desc "logs", "Show lines from app on servers"
|
95
|
+
option :since, aliases: "-s", desc: "Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)"
|
96
|
+
option :lines, type: :numeric, aliases: "-n", desc: "Number of log lines to pull from each server"
|
97
|
+
option :grep, aliases: "-g", desc: "Show lines with grep match only (use this to fetch specific requests by id)"
|
98
|
+
option :follow, aliases: "-f", desc: "Follow logs on primary server (or specific host set by --hosts)"
|
79
99
|
def logs
|
80
100
|
# FIXME: Catch when app containers aren't running
|
81
|
-
|
101
|
+
|
102
|
+
grep = options[:grep]
|
103
|
+
|
104
|
+
if options[:follow]
|
105
|
+
run_locally do
|
106
|
+
info "Following logs on #{MRSK.primary_host}..."
|
107
|
+
info MRSK.app.follow_logs(host: MRSK.primary_host, grep: grep)
|
108
|
+
exec MRSK.app.follow_logs(host: MRSK.primary_host, grep: grep)
|
109
|
+
end
|
110
|
+
else
|
111
|
+
since = options[:since]
|
112
|
+
lines = options[:lines]
|
113
|
+
|
114
|
+
on(MRSK.hosts) do |host|
|
115
|
+
begin
|
116
|
+
puts_by_host host, capture_with_info(*MRSK.app.logs(since: since, lines: lines, grep: grep))
|
117
|
+
rescue SSHKit::Command::Failed
|
118
|
+
puts_by_host host, "Nothing found"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
82
122
|
end
|
83
|
-
|
123
|
+
|
84
124
|
desc "remove", "Remove app containers and images from servers"
|
85
125
|
option :only, default: "", desc: "Use 'containers' or 'images'"
|
86
126
|
def remove
|
87
127
|
case options[:only]
|
88
128
|
when "containers"
|
89
|
-
on(MRSK.
|
129
|
+
on(MRSK.hosts) { execute *MRSK.app.remove_containers }
|
90
130
|
when "images"
|
91
|
-
on(MRSK.
|
131
|
+
on(MRSK.hosts) { execute *MRSK.app.remove_images }
|
92
132
|
else
|
93
|
-
on(MRSK.
|
94
|
-
on(MRSK.
|
133
|
+
on(MRSK.hosts) { execute *MRSK.app.remove_containers }
|
134
|
+
on(MRSK.hosts) { execute *MRSK.app.remove_images }
|
95
135
|
end
|
96
136
|
end
|
97
137
|
end
|
data/lib/mrsk/cli/base.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require "thor"
|
2
|
-
require "
|
3
|
-
require "sshkit/dsl"
|
2
|
+
require "mrsk/sshkit_with_ext"
|
4
3
|
|
5
4
|
module Mrsk::Cli
|
6
5
|
class Base < Thor
|
@@ -10,12 +9,34 @@ module Mrsk::Cli
|
|
10
9
|
|
11
10
|
class_option :verbose, type: :boolean, aliases: "-v", desc: "Detailed logging"
|
12
11
|
|
12
|
+
class_option :version, desc: "Run commands against a specific app version"
|
13
|
+
|
14
|
+
class_option :primary, type: :boolean, aliases: "-p", desc: "Run commands only on primary host instead of all"
|
15
|
+
class_option :hosts, aliases: "-h", desc: "Run commands on these hosts instead of all (separate by comma)"
|
16
|
+
class_option :roles, aliases: "-r", desc: "Run commands on these roles instead of all (separate by comma)"
|
17
|
+
|
18
|
+
class_option :config_file, aliases: "-c", default: "config/deploy.yml", desc: "Path to config file (default: config/deploy.yml)"
|
19
|
+
class_option :destination, aliases: "-d", desc: "Specify destination to be used for config file (west -> deploy.west.yml)"
|
20
|
+
|
13
21
|
def initialize(*)
|
14
22
|
super
|
15
|
-
|
23
|
+
initialize_commander(options)
|
16
24
|
end
|
17
25
|
|
18
26
|
private
|
27
|
+
def initialize_commander(options)
|
28
|
+
MRSK.tap do |commander|
|
29
|
+
commander.config_file = Pathname.new(File.expand_path(options[:config_file]))
|
30
|
+
commander.destination = options[:destination]
|
31
|
+
commander.verbose = options[:verbose]
|
32
|
+
commander.version = options[:version]
|
33
|
+
|
34
|
+
commander.specific_hosts = options[:hosts]&.split(",")
|
35
|
+
commander.specific_roles = options[:roles]&.split(",")
|
36
|
+
commander.specific_primary! if options[:primary]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
19
40
|
def print_runtime
|
20
41
|
started_at = Time.now
|
21
42
|
yield
|
data/lib/mrsk/cli/build.rb
CHANGED
@@ -9,29 +9,45 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|
9
9
|
|
10
10
|
desc "push", "Build locally and push app image to registry"
|
11
11
|
def push
|
12
|
+
verbose = options[:verbose]
|
13
|
+
cli = self
|
14
|
+
|
12
15
|
run_locally do
|
13
16
|
begin
|
14
|
-
debug
|
15
|
-
info "Building image may take a while (run with --verbose for progress logging)"
|
16
|
-
execute *MRSK.builder.push
|
17
|
+
MRSK.verbosity(:debug) { execute *MRSK.builder.push }
|
17
18
|
rescue SSHKit::Command::Failed => e
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
if e.message =~ /(no builder)|(no such file or directory)/
|
20
|
+
error "Missing compatible builder, so creating a new one first"
|
21
|
+
|
22
|
+
if cli.create
|
23
|
+
MRSK.verbosity(:debug) { execute *MRSK.builder.push }
|
24
|
+
end
|
25
|
+
else
|
26
|
+
raise
|
27
|
+
end
|
21
28
|
end
|
22
29
|
end
|
23
30
|
end
|
24
31
|
|
25
32
|
desc "pull", "Pull app image from the registry onto servers"
|
26
33
|
def pull
|
27
|
-
on(MRSK.
|
34
|
+
on(MRSK.hosts) { execute *MRSK.builder.pull }
|
28
35
|
end
|
29
36
|
|
30
37
|
desc "create", "Create a local build setup"
|
31
38
|
def create
|
32
39
|
run_locally do
|
33
|
-
|
34
|
-
|
40
|
+
begin
|
41
|
+
debug "Using builder: #{MRSK.builder.name}"
|
42
|
+
execute *MRSK.builder.create
|
43
|
+
rescue SSHKit::Command::Failed => e
|
44
|
+
if e.message =~ /stderr=(.*)/
|
45
|
+
error "Couldn't create remote builder: #{$1}"
|
46
|
+
false
|
47
|
+
else
|
48
|
+
raise
|
49
|
+
end
|
50
|
+
end
|
35
51
|
end
|
36
52
|
end
|
37
53
|
|
@@ -46,7 +62,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|
46
62
|
desc "details", "Show the name of the configured builder"
|
47
63
|
def details
|
48
64
|
run_locally do
|
49
|
-
puts "Builder: #{MRSK.builder.name}
|
65
|
+
puts "Builder: #{MRSK.builder.name}"
|
50
66
|
puts capture(*MRSK.builder.info)
|
51
67
|
end
|
52
68
|
end
|
data/lib/mrsk/cli/main.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "mrsk/cli/base"
|
2
2
|
|
3
|
+
require "mrsk/cli/accessory"
|
3
4
|
require "mrsk/cli/app"
|
4
5
|
require "mrsk/cli/build"
|
5
6
|
require "mrsk/cli/prune"
|
@@ -8,6 +9,15 @@ require "mrsk/cli/server"
|
|
8
9
|
require "mrsk/cli/traefik"
|
9
10
|
|
10
11
|
class Mrsk::Cli::Main < Mrsk::Cli::Base
|
12
|
+
desc "setup", "Setup all accessories and deploy the app to servers"
|
13
|
+
def setup
|
14
|
+
print_runtime do
|
15
|
+
invoke "mrsk:cli:server:bootstrap"
|
16
|
+
invoke "mrsk:cli:accessory:boot", [ "all" ]
|
17
|
+
deploy
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
11
21
|
desc "deploy", "Deploy the app to servers"
|
12
22
|
def deploy
|
13
23
|
print_runtime do
|
@@ -32,7 +42,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
32
42
|
|
33
43
|
desc "rollback [VERSION]", "Rollback the app to VERSION (that must already be on servers)"
|
34
44
|
def rollback(version)
|
35
|
-
on(MRSK.
|
45
|
+
on(MRSK.hosts) do
|
36
46
|
execute *MRSK.app.stop, raise_on_non_zero_exit: false
|
37
47
|
execute *MRSK.app.start(version: version)
|
38
48
|
end
|
@@ -42,6 +52,14 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
42
52
|
def details
|
43
53
|
invoke "mrsk:cli:traefik:details"
|
44
54
|
invoke "mrsk:cli:app:details"
|
55
|
+
invoke "mrsk:cli:accessory:details", [ "all" ]
|
56
|
+
end
|
57
|
+
|
58
|
+
desc "config", "Show combined config"
|
59
|
+
def config
|
60
|
+
run_locally do
|
61
|
+
puts MRSK.config.to_h.to_yaml
|
62
|
+
end
|
45
63
|
end
|
46
64
|
|
47
65
|
desc "install", "Create config stub in config/deploy.yml and binstub in bin/mrsk"
|
@@ -79,6 +97,9 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
79
97
|
puts Mrsk::VERSION
|
80
98
|
end
|
81
99
|
|
100
|
+
desc "accessory", "Manage the accessories"
|
101
|
+
subcommand "accessory", Mrsk::Cli::Accessory
|
102
|
+
|
82
103
|
desc "app", "Manage the application"
|
83
104
|
subcommand "app", Mrsk::Cli::App
|
84
105
|
|
data/lib/mrsk/cli/prune.rb
CHANGED
@@ -9,11 +9,11 @@ class Mrsk::Cli::Prune < Mrsk::Cli::Base
|
|
9
9
|
|
10
10
|
desc "images", "Prune unused images older than 30 days"
|
11
11
|
def images
|
12
|
-
on(MRSK.
|
12
|
+
on(MRSK.hosts) { execute *MRSK.prune.images }
|
13
13
|
end
|
14
14
|
|
15
15
|
desc "containers", "Prune stopped containers for the service older than 3 days"
|
16
16
|
def containers
|
17
|
-
on(MRSK.
|
17
|
+
on(MRSK.hosts) { execute *MRSK.prune.containers }
|
18
18
|
end
|
19
19
|
end
|
data/lib/mrsk/cli/registry.rb
CHANGED
@@ -4,11 +4,15 @@ class Mrsk::Cli::Registry < Mrsk::Cli::Base
|
|
4
4
|
desc "login", "Login to the registry locally and remotely"
|
5
5
|
def login
|
6
6
|
run_locally { execute *MRSK.registry.login }
|
7
|
-
on(MRSK.
|
7
|
+
on(MRSK.hosts) { execute *MRSK.registry.login }
|
8
|
+
rescue ArgumentError => e
|
9
|
+
puts e.message
|
8
10
|
end
|
9
11
|
|
10
12
|
desc "logout", "Logout of the registry remotely"
|
11
13
|
def logout
|
12
|
-
on(MRSK.
|
14
|
+
on(MRSK.hosts) { execute *MRSK.registry.logout }
|
15
|
+
rescue ArgumentError => e
|
16
|
+
puts e.message
|
13
17
|
end
|
14
18
|
end
|
data/lib/mrsk/cli/server.rb
CHANGED
@@ -3,6 +3,6 @@ require "mrsk/cli/base"
|
|
3
3
|
class Mrsk::Cli::Server < Mrsk::Cli::Base
|
4
4
|
desc "bootstrap", "Ensure Docker is installed on the servers"
|
5
5
|
def bootstrap
|
6
|
-
on(MRSK.
|
6
|
+
on(MRSK.hosts + MRSK.accessory_hosts) { execute "which docker || (apt-get update -y && apt-get install docker.io -y)" }
|
7
7
|
end
|
8
8
|
end
|
data/lib/mrsk/cli/traefik.rb
CHANGED
@@ -3,17 +3,24 @@ require "mrsk/cli/base"
|
|
3
3
|
class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
4
4
|
desc "boot", "Boot Traefik on servers"
|
5
5
|
def boot
|
6
|
-
on(MRSK.
|
6
|
+
on(MRSK.traefik_hosts) { execute *MRSK.traefik.run, raise_on_non_zero_exit: false }
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "reboot", "Reboot Traefik on servers (stop container, remove container, start new container)"
|
10
|
+
def reboot
|
11
|
+
invoke :stop
|
12
|
+
invoke :remove_container
|
13
|
+
invoke :boot
|
7
14
|
end
|
8
15
|
|
9
16
|
desc "start", "Start existing Traefik on servers"
|
10
17
|
def start
|
11
|
-
on(MRSK.
|
18
|
+
on(MRSK.traefik_hosts) { execute *MRSK.traefik.start, raise_on_non_zero_exit: false }
|
12
19
|
end
|
13
20
|
|
14
21
|
desc "stop", "Stop Traefik on servers"
|
15
22
|
def stop
|
16
|
-
on(MRSK.
|
23
|
+
on(MRSK.traefik_hosts) { execute *MRSK.traefik.stop, raise_on_non_zero_exit: false }
|
17
24
|
end
|
18
25
|
|
19
26
|
desc "restart", "Restart Traefik on servers"
|
@@ -24,21 +31,47 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
24
31
|
|
25
32
|
desc "details", "Display details about Traefik containers from servers"
|
26
33
|
def details
|
27
|
-
on(MRSK.
|
34
|
+
on(MRSK.traefik_hosts) { |host| puts_by_host host, capture_with_info(*MRSK.traefik.info), type: "Traefik" }
|
28
35
|
end
|
29
36
|
|
30
|
-
desc "logs", "Show
|
37
|
+
desc "logs", "Show log lines from Traefik on servers"
|
38
|
+
option :since, aliases: "-s", desc: "Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)"
|
39
|
+
option :lines, type: :numeric, aliases: "-n", desc: "Number of log lines to pull from each server"
|
40
|
+
option :grep, aliases: "-g", desc: "Show lines with grep match only (use this to fetch specific requests by id)"
|
41
|
+
option :follow, aliases: "-f", desc: "Follow logs on primary server (or specific host set by --hosts)"
|
31
42
|
def logs
|
32
|
-
|
43
|
+
grep = options[:grep]
|
44
|
+
|
45
|
+
if options[:follow]
|
46
|
+
run_locally do
|
47
|
+
info "Following logs on #{MRSK.primary_host}..."
|
48
|
+
info MRSK.traefik.follow_logs(host: MRSK.primary_host, grep: grep)
|
49
|
+
exec MRSK.traefik.follow_logs(host: MRSK.primary_host, grep: grep)
|
50
|
+
end
|
51
|
+
else
|
52
|
+
since = options[:since]
|
53
|
+
lines = options[:lines]
|
54
|
+
|
55
|
+
on(MRSK.traefik_hosts) do |host|
|
56
|
+
puts_by_host host, capture(*MRSK.traefik.logs(since: since, lines: lines, grep: grep)), type: "Traefik"
|
57
|
+
end
|
58
|
+
end
|
33
59
|
end
|
34
60
|
|
35
61
|
desc "remove", "Remove Traefik container and image from servers"
|
36
62
|
def remove
|
37
63
|
invoke :stop
|
64
|
+
invoke :remove_container
|
65
|
+
invoke :remove_image
|
66
|
+
end
|
38
67
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
68
|
+
desc "remove_container", "Remove Traefik container from servers"
|
69
|
+
def remove_container
|
70
|
+
on(MRSK.traefik_hosts) { execute *MRSK.traefik.remove_container }
|
71
|
+
end
|
72
|
+
|
73
|
+
desc "remove_container", "Remove Traefik image from servers"
|
74
|
+
def remove_image
|
75
|
+
on(MRSK.traefik_hosts) { execute *MRSK.traefik.remove_image }
|
43
76
|
end
|
44
77
|
end
|
data/lib/mrsk/cli.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require "mrsk"
|
2
2
|
|
3
|
-
MRSK = Mrsk::Commander.new \
|
4
|
-
config_file: Pathname.new(File.expand_path("config/deploy.yml"))
|
5
|
-
|
6
3
|
module Mrsk::Cli
|
7
4
|
end
|
8
5
|
|
6
|
+
# SSHKit uses instance eval, so we need a global const for ergonomics
|
7
|
+
MRSK = Mrsk::Commander.new
|
8
|
+
|
9
9
|
require "mrsk/cli/main"
|
data/lib/mrsk/commander.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
+
require "active_support/core_ext/enumerable"
|
2
|
+
|
1
3
|
require "mrsk/configuration"
|
4
|
+
require "mrsk/commands/accessory"
|
2
5
|
require "mrsk/commands/app"
|
3
6
|
require "mrsk/commands/builder"
|
4
7
|
require "mrsk/commands/prune"
|
@@ -6,15 +9,47 @@ require "mrsk/commands/traefik"
|
|
6
9
|
require "mrsk/commands/registry"
|
7
10
|
|
8
11
|
class Mrsk::Commander
|
9
|
-
|
10
|
-
attr_accessor :verbose
|
12
|
+
attr_accessor :config_file, :destination, :verbose, :version
|
11
13
|
|
12
|
-
def initialize(config_file:)
|
13
|
-
@config_file = config_file
|
14
|
+
def initialize(config_file: nil, destination: nil, verbose: false)
|
15
|
+
@config_file, @destination, @verbose = config_file, destination, verbose
|
14
16
|
end
|
15
17
|
|
16
18
|
def config
|
17
|
-
@config ||=
|
19
|
+
@config ||= \
|
20
|
+
Mrsk::Configuration
|
21
|
+
.create_from(config_file, destination: destination, version: cascading_version)
|
22
|
+
.tap { |config| configure_sshkit_with(config) }
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_accessor :specific_hosts
|
26
|
+
|
27
|
+
def specific_primary!
|
28
|
+
self.specific_hosts = [ config.primary_web_host ]
|
29
|
+
end
|
30
|
+
|
31
|
+
def specific_roles=(role_names)
|
32
|
+
self.specific_hosts = config.roles.select { |r| role_names.include?(r.name) }.flat_map(&:hosts) if role_names.present?
|
33
|
+
end
|
34
|
+
|
35
|
+
def primary_host
|
36
|
+
specific_hosts&.sole || config.primary_web_host
|
37
|
+
end
|
38
|
+
|
39
|
+
def hosts
|
40
|
+
specific_hosts || config.all_hosts
|
41
|
+
end
|
42
|
+
|
43
|
+
def traefik_hosts
|
44
|
+
specific_hosts || config.traefik_hosts
|
45
|
+
end
|
46
|
+
|
47
|
+
def accessory_hosts
|
48
|
+
specific_hosts || config.accessories.collect(&:host)
|
49
|
+
end
|
50
|
+
|
51
|
+
def accessory_names
|
52
|
+
config.accessories&.collect(&:name) || []
|
18
53
|
end
|
19
54
|
|
20
55
|
|
@@ -38,6 +73,10 @@ class Mrsk::Commander
|
|
38
73
|
@prune ||= Mrsk::Commands::Prune.new(config)
|
39
74
|
end
|
40
75
|
|
76
|
+
def accessory(name)
|
77
|
+
config.accessories.detect { |a| a.name == name }
|
78
|
+
end
|
79
|
+
|
41
80
|
|
42
81
|
def verbosity(level)
|
43
82
|
old_level = SSHKit.config.output_verbosity
|
@@ -48,8 +87,12 @@ class Mrsk::Commander
|
|
48
87
|
end
|
49
88
|
|
50
89
|
private
|
90
|
+
def cascading_version
|
91
|
+
version.presence || ENV["VERSION"] || `git rev-parse HEAD`.strip
|
92
|
+
end
|
93
|
+
|
51
94
|
# Lazy setup of SSHKit
|
52
|
-
def
|
95
|
+
def configure_sshkit_with(config)
|
53
96
|
SSHKit::Backend::Netssh.configure { |ssh| ssh.ssh_options = config.ssh_options }
|
54
97
|
SSHKit.config.command_map[:docker] = "docker" # No need to use /usr/bin/env, just clogs up the logs
|
55
98
|
SSHKit.config.output_verbosity = :debug if verbose
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require "mrsk/commands/base"
|
2
|
+
|
3
|
+
class Mrsk::Commands::Accessory < Mrsk::Commands::Base
|
4
|
+
attr_reader :accessory_config
|
5
|
+
delegate :service_name, :image, :host, :port, :files, :directories, :env_args, :volume_args, :label_args, to: :accessory_config
|
6
|
+
|
7
|
+
def initialize(config, name:)
|
8
|
+
super(config)
|
9
|
+
@accessory_config = config.accessory(name)
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
docker :run,
|
14
|
+
"--name", service_name,
|
15
|
+
"-d",
|
16
|
+
"--restart", "unless-stopped",
|
17
|
+
"-p", port,
|
18
|
+
*env_args,
|
19
|
+
*volume_args,
|
20
|
+
*label_args,
|
21
|
+
image
|
22
|
+
end
|
23
|
+
|
24
|
+
def start
|
25
|
+
docker :container, :start, service_name
|
26
|
+
end
|
27
|
+
|
28
|
+
def stop
|
29
|
+
docker :container, :stop, service_name
|
30
|
+
end
|
31
|
+
|
32
|
+
def info
|
33
|
+
docker :ps, *service_filter
|
34
|
+
end
|
35
|
+
|
36
|
+
def logs(since: nil, lines: nil, grep: nil)
|
37
|
+
pipe \
|
38
|
+
docker(:logs, service_name, (" --since #{since}" if since), (" -n #{lines}" if lines), "-t", "2>&1"),
|
39
|
+
("grep '#{grep}'" if grep)
|
40
|
+
end
|
41
|
+
|
42
|
+
def follow_logs(grep: nil)
|
43
|
+
run_over_ssh pipe(
|
44
|
+
docker(:logs, service_name, "-t", "-n", "10", "-f", "2>&1"),
|
45
|
+
("grep '#{grep}'" if grep)
|
46
|
+
).join(" "), host: host
|
47
|
+
end
|
48
|
+
|
49
|
+
def exec(*command, interactive: false)
|
50
|
+
docker :exec,
|
51
|
+
("-it" if interactive),
|
52
|
+
*env_args,
|
53
|
+
*volume_args,
|
54
|
+
service_name,
|
55
|
+
*command
|
56
|
+
end
|
57
|
+
|
58
|
+
def run_exec(*command, interactive: false)
|
59
|
+
docker :run,
|
60
|
+
("-it" if interactive),
|
61
|
+
"--rm",
|
62
|
+
*env_args,
|
63
|
+
*volume_args,
|
64
|
+
image,
|
65
|
+
*command
|
66
|
+
end
|
67
|
+
|
68
|
+
def bash(host:)
|
69
|
+
exec_over_ssh "bash", host: host
|
70
|
+
end
|
71
|
+
|
72
|
+
def ensure_local_file_present(local_file)
|
73
|
+
if !local_file.is_a?(StringIO) && !Pathname.new(local_file).exist?
|
74
|
+
raise "Missing file: #{local_file}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def make_directory_for(remote_file)
|
79
|
+
make_directory Pathname.new(remote_file).dirname.to_s
|
80
|
+
end
|
81
|
+
|
82
|
+
def make_directory(path)
|
83
|
+
[ :mkdir, "-p", path ]
|
84
|
+
end
|
85
|
+
|
86
|
+
def remove_service_directory
|
87
|
+
[ :rm, "-rf", service_name ]
|
88
|
+
end
|
89
|
+
|
90
|
+
def remove_container
|
91
|
+
docker :container, :prune, "-f", *service_filter
|
92
|
+
end
|
93
|
+
|
94
|
+
def remove_image
|
95
|
+
docker :image, :prune, "-a", "-f", *service_filter
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
def exec_over_ssh(*command, host:)
|
100
|
+
run_over_ssh run_exec(*command, interactive: true).join(" "), host: host
|
101
|
+
end
|
102
|
+
|
103
|
+
def service_filter
|
104
|
+
[ "--filter", "label=service=#{service_name}" ]
|
105
|
+
end
|
106
|
+
end
|