Bootstrap: use multi-platform installer
* Limit auto-install to root users; otherwise, give manual install guidance * Support non-Debian/Ubuntu with the multi-OS get.docker.com installer
This commit is contained in:
@@ -17,9 +17,6 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|||||||
invoke_options = deploy_options
|
invoke_options = deploy_options
|
||||||
|
|
||||||
runtime = print_runtime do
|
runtime = print_runtime do
|
||||||
say "Ensure curl and Docker are installed...", :magenta
|
|
||||||
invoke "mrsk:cli:server:bootstrap", [], invoke_options
|
|
||||||
|
|
||||||
say "Log into image registry...", :magenta
|
say "Log into image registry...", :magenta
|
||||||
invoke "mrsk:cli:registry:login", [], invoke_options
|
invoke "mrsk:cli:registry:login", [], invoke_options
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
class Mrsk::Cli::Server < Mrsk::Cli::Base
|
class Mrsk::Cli::Server < Mrsk::Cli::Base
|
||||||
desc "bootstrap", "Ensure curl and Docker are installed on servers"
|
desc "bootstrap", "Set up Docker to run MRSK apps"
|
||||||
def bootstrap
|
def bootstrap
|
||||||
with_lock do
|
missing = []
|
||||||
on(MRSK.hosts + MRSK.accessory_hosts) do
|
|
||||||
dependencies_to_install = Array.new.tap do |dependencies|
|
|
||||||
dependencies << "curl" unless execute "which curl", raise_on_non_zero_exit: false
|
|
||||||
dependencies << "docker.io" unless execute "which docker", raise_on_non_zero_exit: false
|
|
||||||
end
|
|
||||||
|
|
||||||
if dependencies_to_install.any?
|
on(MRSK.hosts | MRSK.accessory_hosts) do |host|
|
||||||
execute "apt-get update -y && apt-get install #{dependencies_to_install.join(" ")} -y"
|
unless execute(*MRSK.docker.installed?, raise_on_non_zero_exit: false)
|
||||||
|
if execute(*MRSK.docker.superuser?, raise_on_non_zero_exit: false)
|
||||||
|
info "Missing Docker on #{host}. Installing…"
|
||||||
|
execute *MRSK.docker.install
|
||||||
|
else
|
||||||
|
missing << host
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if missing.any?
|
||||||
|
raise "Docker is not installed on #{missing.join(", ")} and can't be automatically installed without having root access and the `curl` command available. Install Docker manually: https://docs.docker.com/engine/install/"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -84,6 +84,10 @@ class Mrsk::Commander
|
|||||||
@builder ||= Mrsk::Commands::Builder.new(config)
|
@builder ||= Mrsk::Commands::Builder.new(config)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def docker
|
||||||
|
@docker ||= Mrsk::Commands::Docker.new(config)
|
||||||
|
end
|
||||||
|
|
||||||
def healthcheck
|
def healthcheck
|
||||||
@healthcheck ||= Mrsk::Commands::Healthcheck.new(config)
|
@healthcheck ||= Mrsk::Commands::Healthcheck.new(config)
|
||||||
end
|
end
|
||||||
|
|||||||
21
lib/mrsk/commands/docker.rb
Normal file
21
lib/mrsk/commands/docker.rb
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
class Mrsk::Commands::Docker < Mrsk::Commands::Base
|
||||||
|
# Install Docker using the https://github.com/docker/docker-install convenience script.
|
||||||
|
def install
|
||||||
|
pipe [ :curl, "-fsSL", "https://get.docker.com" ], :sh
|
||||||
|
end
|
||||||
|
|
||||||
|
# Checks the Docker client version. Fails if Docker is not installed.
|
||||||
|
def installed?
|
||||||
|
docker "-v"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Checks the Docker server version. Fails if Docker is not running.
|
||||||
|
def running?
|
||||||
|
docker :version
|
||||||
|
end
|
||||||
|
|
||||||
|
# Do we have superuser access to install Docker and start system services?
|
||||||
|
def superuser?
|
||||||
|
[ '[ "${EUID:-$(id -u)}" -eq 0 ]' ]
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -12,7 +12,6 @@ class CliMainTest < CliTestCase
|
|||||||
test "deploy" do
|
test "deploy" do
|
||||||
invoke_options = { "config_file" => "test/fixtures/deploy_simple.yml", "skip_broadcast" => false, "version" => "999" }
|
invoke_options = { "config_file" => "test/fixtures/deploy_simple.yml", "skip_broadcast" => false, "version" => "999" }
|
||||||
|
|
||||||
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:server:bootstrap", [], invoke_options)
|
|
||||||
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:registry:login", [], invoke_options)
|
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:registry:login", [], invoke_options)
|
||||||
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:build:deliver", [], invoke_options)
|
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:build:deliver", [], invoke_options)
|
||||||
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:traefik:boot", [], invoke_options)
|
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:traefik:boot", [], invoke_options)
|
||||||
@@ -22,7 +21,6 @@ class CliMainTest < CliTestCase
|
|||||||
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:prune:all", [], invoke_options)
|
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:prune:all", [], invoke_options)
|
||||||
|
|
||||||
run_command("deploy").tap do |output|
|
run_command("deploy").tap do |output|
|
||||||
assert_match /Ensure curl and Docker are installed/, output
|
|
||||||
assert_match /Log into image registry/, output
|
assert_match /Log into image registry/, output
|
||||||
assert_match /Build and push app image/, output
|
assert_match /Build and push app image/, output
|
||||||
assert_match /Ensure Traefik is running/, output
|
assert_match /Ensure Traefik is running/, output
|
||||||
@@ -35,7 +33,6 @@ class CliMainTest < CliTestCase
|
|||||||
test "deploy with skip_push" do
|
test "deploy with skip_push" do
|
||||||
invoke_options = { "config_file" => "test/fixtures/deploy_simple.yml", "skip_broadcast" => false, "version" => "999" }
|
invoke_options = { "config_file" => "test/fixtures/deploy_simple.yml", "skip_broadcast" => false, "version" => "999" }
|
||||||
|
|
||||||
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:server:bootstrap", [], invoke_options)
|
|
||||||
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:registry:login", [], invoke_options)
|
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:registry:login", [], invoke_options)
|
||||||
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:build:pull", [], invoke_options)
|
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:build:pull", [], invoke_options)
|
||||||
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:traefik:boot", [], invoke_options)
|
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:traefik:boot", [], invoke_options)
|
||||||
@@ -46,7 +43,6 @@ class CliMainTest < CliTestCase
|
|||||||
|
|
||||||
run_command("deploy", "--skip_push").tap do |output|
|
run_command("deploy", "--skip_push").tap do |output|
|
||||||
assert_match /Acquiring the deploy lock/, output
|
assert_match /Acquiring the deploy lock/, output
|
||||||
assert_match /Ensure curl and Docker are installed/, output
|
|
||||||
assert_match /Log into image registry/, output
|
assert_match /Log into image registry/, output
|
||||||
assert_match /Pull app image/, output
|
assert_match /Pull app image/, output
|
||||||
assert_match /Ensure Traefik is running/, output
|
assert_match /Ensure Traefik is running/, output
|
||||||
@@ -87,7 +83,6 @@ class CliMainTest < CliTestCase
|
|||||||
test "deploy errors during critical section leave lock in place" do
|
test "deploy errors during critical section leave lock in place" do
|
||||||
invoke_options = { "config_file" => "test/fixtures/deploy_simple.yml", "skip_broadcast" => false, "version" => "999" }
|
invoke_options = { "config_file" => "test/fixtures/deploy_simple.yml", "skip_broadcast" => false, "version" => "999" }
|
||||||
|
|
||||||
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:server:bootstrap", [], invoke_options)
|
|
||||||
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:registry:login", [], invoke_options)
|
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:registry:login", [], invoke_options)
|
||||||
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:build:deliver", [], invoke_options)
|
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:build:deliver", [], invoke_options)
|
||||||
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:app:stale_containers", [], invoke_options)
|
Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:app:stale_containers", [], invoke_options)
|
||||||
@@ -106,7 +101,7 @@ class CliMainTest < CliTestCase
|
|||||||
invoke_options = { "config_file" => "test/fixtures/deploy_simple.yml", "skip_broadcast" => false, "version" => "999" }
|
invoke_options = { "config_file" => "test/fixtures/deploy_simple.yml", "skip_broadcast" => false, "version" => "999" }
|
||||||
|
|
||||||
Mrsk::Cli::Main.any_instance.expects(:invoke)
|
Mrsk::Cli::Main.any_instance.expects(:invoke)
|
||||||
.with("mrsk:cli:server:bootstrap", [], invoke_options)
|
.with("mrsk:cli:registry:login", [], invoke_options)
|
||||||
.raises(RuntimeError)
|
.raises(RuntimeError)
|
||||||
|
|
||||||
assert !MRSK.holding_lock?
|
assert !MRSK.holding_lock?
|
||||||
|
|||||||
@@ -1,11 +1,30 @@
|
|||||||
require_relative "cli_test_case"
|
require_relative "cli_test_case"
|
||||||
|
|
||||||
class CliServerTest < CliTestCase
|
class CliServerTest < CliTestCase
|
||||||
test "bootstrap" do
|
test "bootstrap already installed" do
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:docker, "-v", raise_on_non_zero_exit: false).returns(true).at_least_once
|
||||||
|
|
||||||
|
assert_equal "", run_command("bootstrap")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "bootstrap install as non-root user" do
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:docker, "-v", raise_on_non_zero_exit: false).returns(false).at_least_once
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:execute).with('[ "${EUID:-$(id -u)}" -eq 0 ]', raise_on_non_zero_exit: false).returns(false).at_least_once
|
||||||
|
|
||||||
|
assert_raise RuntimeError, "Docker is not installed on 1.1.1.1, 1.1.1.3, 1.1.1.4, 1.1.1.2 and can't be automatically intalled without having root access and the `curl` command available. Install Docker manually: https://docs.docker.com/engine/install/" do
|
||||||
|
run_command("bootstrap")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "bootstrap install as root user" do
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:docker, "-v", raise_on_non_zero_exit: false).returns(false).at_least_once
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:execute).with('[ "${EUID:-$(id -u)}" -eq 0 ]', raise_on_non_zero_exit: false).returns(true).at_least_once
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:curl, "-fsSL", "https://get.docker.com", "|", :sh).at_least_once
|
||||||
|
|
||||||
run_command("bootstrap").tap do |output|
|
run_command("bootstrap").tap do |output|
|
||||||
assert_match /which curl/, output
|
("1.1.1.1".."1.1.1.4").map do |host|
|
||||||
assert_match /which docker/, output
|
assert_match "Missing Docker on #{host}. Installing…", output
|
||||||
assert_match /apt-get update -y && apt-get install curl docker.io -y/, output
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
26
test/commands/docker_test.rb
Normal file
26
test/commands/docker_test.rb
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class CommandsDockerTest < ActiveSupport::TestCase
|
||||||
|
setup do
|
||||||
|
@config = {
|
||||||
|
service: "app", image: "dhh/app", registry: { "username" => "dhh", "password" => "secret" }, servers: [ "1.1.1.1" ]
|
||||||
|
}
|
||||||
|
@docker = Mrsk::Commands::Docker.new(Mrsk::Configuration.new(@config))
|
||||||
|
end
|
||||||
|
|
||||||
|
test "install" do
|
||||||
|
assert_equal "curl -fsSL https://get.docker.com | sh", @docker.install.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "installed?" do
|
||||||
|
assert_equal "docker -v", @docker.installed?.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "running?" do
|
||||||
|
assert_equal "docker version", @docker.running?.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "superuser?" do
|
||||||
|
assert_equal '[ "${EUID:-$(id -u)}" -eq 0 ]', @docker.superuser?.join(" ")
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user