docker-armada 2.0.53
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 +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +5 -0
- data/Gemfile +5 -0
- data/LICENSE +20 -0
- data/README.md +293 -0
- data/Rakefile +9 -0
- data/Thorfile +1 -0
- data/armada.gemspec +37 -0
- data/bin/armada +5 -0
- data/lib/armada.rb +37 -0
- data/lib/armada/clean.rb +2 -0
- data/lib/armada/clean/containers.rb +30 -0
- data/lib/armada/clean/images.rb +29 -0
- data/lib/armada/cli.rb +40 -0
- data/lib/armada/cli/clean.rb +23 -0
- data/lib/armada/cli/deploy.rb +32 -0
- data/lib/armada/cli/inspect.rb +28 -0
- data/lib/armada/configuration.rb +33 -0
- data/lib/armada/connection.rb +3 -0
- data/lib/armada/connection/docker.rb +22 -0
- data/lib/armada/connection/health_check.rb +66 -0
- data/lib/armada/connection/remote.rb +26 -0
- data/lib/armada/deploy.rb +2 -0
- data/lib/armada/deploy/parallel.rb +58 -0
- data/lib/armada/deploy/rolling.rb +60 -0
- data/lib/armada/deploy_dsl.rb +156 -0
- data/lib/armada/docker.rb +6 -0
- data/lib/armada/docker/config.rb +55 -0
- data/lib/armada/docker/container.rb +120 -0
- data/lib/armada/docker/host.rb +68 -0
- data/lib/armada/docker/image.rb +86 -0
- data/lib/armada/thor.rb +1 -0
- data/lib/armada/ui.rb +15 -0
- data/lib/armada/utils.rb +2 -0
- data/lib/armada/utils/array.rb +9 -0
- data/lib/armada/utils/time.rb +23 -0
- data/lib/armada/version.rb +3 -0
- data/spec/connection/health_check_spec.rb +36 -0
- data/spec/deploy_dsl_spec.rb +84 -0
- data/spec/docker/container_spec.rb +124 -0
- data/spec/docker/image_spec.rb +110 -0
- data/spec/spec_helper.rb +12 -0
- metadata +289 -0
data/lib/armada/clean.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Armada
|
2
|
+
module Clean
|
3
|
+
class Containers
|
4
|
+
def initialize(options)
|
5
|
+
@hosts = options[:hosts]
|
6
|
+
@force = options[:force]
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
Armada.ui.info "******* DRY RUN *******" unless @force
|
12
|
+
@hosts.each_in_parallel do |host|
|
13
|
+
docker_host = Armada::Host.create(host, @options)
|
14
|
+
docker_host.get_all_containers.each do |container|
|
15
|
+
running = container.json["State"]["Running"]
|
16
|
+
paused = container.json["State"]["Paused"]
|
17
|
+
unless running || paused
|
18
|
+
begin
|
19
|
+
Armada.ui.info "#{docker_host.host} -- #{container.json["Name"]} with id [#{container.id[0..11]}]"
|
20
|
+
container.remove if @force
|
21
|
+
rescue Exception => e
|
22
|
+
Armada.ui.error "#{docker_host.host} -- unable to remove container #{container.json["Name"]} with id [#{container.id[0..11]}] because of the following error - #{e.message}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Armada
|
2
|
+
module Clean
|
3
|
+
class Images
|
4
|
+
def initialize(options)
|
5
|
+
@hosts = options[:hosts]
|
6
|
+
@force = options[:force]
|
7
|
+
@noprune = !options[:prune]
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
Armada.ui.info "******* DRY RUN *******" unless @force
|
13
|
+
@hosts.each_in_parallel do |host|
|
14
|
+
docker_host = Armada::Host.create(host, @options)
|
15
|
+
docker_host.get_all_images.each do |image|
|
16
|
+
if image.info["RepoTags"].include?("<none>:<none>")
|
17
|
+
begin
|
18
|
+
Armada.ui.info "#{docker_host.host} -- #{image.id[0..11]} is an abandoned image and will be removed"
|
19
|
+
image.remove({:force => true, :noprune => @noprune}) if @force
|
20
|
+
rescue Exception => e
|
21
|
+
Armada.ui.error "#{docker_host.host} -- unable to remove image #{image.id[0..11]} because of the following error - #{e.message}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/armada/cli.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require_relative 'cli/deploy'
|
2
|
+
require_relative 'cli/inspect'
|
3
|
+
require_relative 'cli/clean'
|
4
|
+
|
5
|
+
module Armada
|
6
|
+
class Cli < Thor
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
super(*args)
|
10
|
+
|
11
|
+
if @options[:quiet]
|
12
|
+
Aramda.ui.mute!
|
13
|
+
end
|
14
|
+
|
15
|
+
@options = options.dup
|
16
|
+
end
|
17
|
+
|
18
|
+
class_option :quiet,
|
19
|
+
desc: "Silence all informational output",
|
20
|
+
type: :boolean,
|
21
|
+
aliases: "-q",
|
22
|
+
default: false
|
23
|
+
|
24
|
+
desc "version", "Display Aramda version."
|
25
|
+
def version
|
26
|
+
Armada.ui.info "#{Armada.executable_name} #{Armada::VERSION}"
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
desc "deploy SUBCOMMAND ...ARGS", "Deploy a docker container"
|
31
|
+
subcommand "deploy", Armada::DeployCli
|
32
|
+
|
33
|
+
desc "inspect SUBCOMMAND ...ARGS", "Info about the state of a docker host"
|
34
|
+
subcommand "inspect", Armada::InspectCli
|
35
|
+
|
36
|
+
desc "clean SUBCOMMAND ...ARGS", "Clean a docker host(s)"
|
37
|
+
subcommand "clean", Armada::CleanCli
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Armada
|
2
|
+
class CleanCli < Thor
|
3
|
+
|
4
|
+
desc "containers", "Remove all exited containers from a host(s)"
|
5
|
+
option :hosts, :type => :array, :aliases => :h, :desc => "The docker host(s) to deploy to. This can be a comma sepearted list."
|
6
|
+
option :ssh_gateway, :type => :string, :aliases => :G, :desc => "SSH Gateway Host"
|
7
|
+
option :ssh_gateway_user, :type => :string, :aliases => :U, :desc => "SSH Gateway User"
|
8
|
+
option :force, :type => :boolean, :aliases => :f, :desc => "Must specify the force option if you want the containers removed", :default => false, :lazy_default => true
|
9
|
+
def containers
|
10
|
+
Armada::Clean::Containers.new(options).run
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "images", "Remove all untagged images from a host(s)"
|
14
|
+
option :hosts, :type => :array, :aliases => :h, :desc => "The docker host(s) to deploy to. This can be a comma sepearted list."
|
15
|
+
option :ssh_gateway, :type => :string, :aliases => :G, :desc => "SSH Gateway Host"
|
16
|
+
option :ssh_gateway_user, :type => :string, :aliases => :U, :desc => "SSH Gateway User"
|
17
|
+
option :force, :type => :boolean, :aliases => :f, :desc => "Must specify the force option if you want the containers removed", :default => false, :lazy_default => true
|
18
|
+
option :prune, :type => :boolean, :aliases => :p, :desc => "Whether to prune child images or not"
|
19
|
+
def images
|
20
|
+
Armada::Clean::Images.new(options).run
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Armada
|
2
|
+
class DeployCli < Thor
|
3
|
+
|
4
|
+
no_commands {
|
5
|
+
def self.common_options
|
6
|
+
option :hosts, :type => :array, :aliases => :h, :desc => "The docker host(s) to deploy to. This can be a comma sepearted list."
|
7
|
+
option :image, :type => :string, :aliases => :i, :desc => "The image to use when deploying"
|
8
|
+
option :tag, :type => :string, :aliases => :t, :desc => "Which version of the image to use", :lazy_default => "latest"
|
9
|
+
option :username, :type => :string, :aliases => :u, :desc => "Docker registry username, if specified, --password must also be specified"
|
10
|
+
option :password, :type => :string, :aliases => :p, :desc => "Docker registry password, if specified, --username must also be specified"
|
11
|
+
option :health_check, :type => :boolean, :aliases => :c, :desc => "Perform health check of container. Default is true", :default => true
|
12
|
+
option :env_vars, :type => :hash, :aliases => :e, :desc => "Environment Variables to pass into the container"
|
13
|
+
option :pull, :type => :boolean, :desc => "Whether to pull the image from the docker registry", :default => true
|
14
|
+
option :ssh_gateway, :type => :string, :aliases => :G, :desc => "SSH Gateway Host"
|
15
|
+
option :ssh_gateway_user, :type => :string, :aliases => :U, :desc => "SSH Gateway User"
|
16
|
+
option :dockercfg, :type => :string, :desc => "Path to dockercfg file, used to authenticate against the docker registry", :default => '~/.dockercfg'
|
17
|
+
end
|
18
|
+
}
|
19
|
+
|
20
|
+
desc "parallel <project> <environment>", "Deploy the specified project to a set of hosts in parallel."
|
21
|
+
DeployCli.common_options
|
22
|
+
def parallel(project, environment)
|
23
|
+
Armada::Deploy::Parallel.new(project, environment, options).run
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "rolling <project> <environment>", "Perform a rolling deploy across a set of hosts."
|
27
|
+
DeployCli.common_options
|
28
|
+
def rolling(project, environment)
|
29
|
+
Armada::Deploy::Rolling.new(project, environment, options).run
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Armada
|
2
|
+
class InspectCli < Thor
|
3
|
+
|
4
|
+
desc "short", "Return basic information on all running containers for a given host"
|
5
|
+
option :hosts, :type => :array, :aliases => :h, :desc => "The Docker host to inspect"
|
6
|
+
def short
|
7
|
+
# Armada.ui.info "Gathering information for #{@options[:hosts].join(', ')}..."
|
8
|
+
|
9
|
+
# hosts = Armada::.new(@options[:hosts])
|
10
|
+
# hosts.each do |connection|
|
11
|
+
# running_containers = []
|
12
|
+
# Armada::Container.all(connection).each do |container|
|
13
|
+
# state = container.json["State"]
|
14
|
+
# if state["Running"]
|
15
|
+
# running_containers <<
|
16
|
+
# {
|
17
|
+
# :container_id => container.id[0..10],
|
18
|
+
# :container_name => container.json["Name"],
|
19
|
+
# :pid => state["Pid"],
|
20
|
+
# :uptime => Time.seconds_to_string(Time.now - Time.parse(state["StartedAt"])),
|
21
|
+
# }
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
# tp running_containers
|
25
|
+
# end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
module Armada
|
4
|
+
module Configuration
|
5
|
+
def self.load!(project, environment, cli_options = {})
|
6
|
+
projects_dir = Dir.getwd()
|
7
|
+
config_file = "#{project}.rake"
|
8
|
+
|
9
|
+
if File.exists?(File.join(projects_dir, config_file))
|
10
|
+
Rake.load_rakefile File.join(File.join(projects_dir, config_file))
|
11
|
+
elsif File.exists?(config_file)
|
12
|
+
Rake.load_rakefile config_file
|
13
|
+
else
|
14
|
+
Armada.ui.error "Can't find '#{config_file}'!"
|
15
|
+
end
|
16
|
+
|
17
|
+
Object.send :include, Armada::DeployDSL
|
18
|
+
task = Rake::Task["environment:#{environment}"]
|
19
|
+
task.set_current_environment environment.to_sym
|
20
|
+
task.invoke
|
21
|
+
|
22
|
+
task_options = Thor::CoreExt::HashWithIndifferentAccess.new(env[environment.to_sym])
|
23
|
+
env_vars = task_options[:env_vars]
|
24
|
+
env_vars.merge!(cli_options[:env_vars]) if cli_options[:env_vars] # If I try and merge cli_options into task_options it overrides the task_options[:env_vars] hash.
|
25
|
+
options = task_options.merge(cli_options)
|
26
|
+
options[:env_vars] = env_vars
|
27
|
+
options[:tag] = 'latest' unless options[:tag]
|
28
|
+
options[:health_check_endpoint] = '/' unless options[:health_check_endpoint]
|
29
|
+
options[:dockercfg] = Armada::Docker::Config.load options[:dockercfg]
|
30
|
+
options
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Armada
|
2
|
+
module Connection
|
3
|
+
class Docker < Remote
|
4
|
+
|
5
|
+
attr_reader :connection
|
6
|
+
def initialize(host, gateway_host = nil, gateway_user = nil)
|
7
|
+
super(host, nil, gateway_host, gateway_user)
|
8
|
+
@connection = create_connection
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
"#{@host}:#{@port}"
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def create_connection
|
17
|
+
return ::Docker::Connection.new("http://localhost:#{@tunneled_port}", {}) if @gateway
|
18
|
+
return ::Docker::Connection.new("http://#{@host}:#{@port}", {})
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Armada
|
2
|
+
module Connection
|
3
|
+
class HealthCheck < Remote
|
4
|
+
|
5
|
+
attr_reader :endpoint
|
6
|
+
def initialize(host, port, endpoint = nil, delay = nil, retries = nil, gateway_host = nil, gateway_user = nil)
|
7
|
+
super(host, port, gateway_host, gateway_user)
|
8
|
+
@endpoint = endpoint ||= '/'
|
9
|
+
@delay = delay ||= 1
|
10
|
+
@retries = retries ||= 60
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
"#{@host}:#{@port}#{@endpoint}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def run
|
18
|
+
info "Performing health check at - :#{@port}#{@endpoint}. Will retry every #{@delay} second(s) for #{@retries} times."
|
19
|
+
1.upto(@retries) do |i|
|
20
|
+
initialize_gateway!
|
21
|
+
unless healthy?
|
22
|
+
info "Still waiting for health check to pass at - :#{@port}#{@endpoint} endpoint..." if i % (@retries/10) == 0
|
23
|
+
sleep(@delay)
|
24
|
+
else
|
25
|
+
info "Health check succeeded!"
|
26
|
+
return true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
return false
|
30
|
+
end
|
31
|
+
|
32
|
+
def healthy?
|
33
|
+
response = begin
|
34
|
+
Excon.get("http://#{health_check_host}:#{health_check_port}#{@endpoint}")
|
35
|
+
rescue Exception => e
|
36
|
+
return false
|
37
|
+
end
|
38
|
+
|
39
|
+
return false unless response
|
40
|
+
return true if response.status >= 200 && response.status < 300
|
41
|
+
|
42
|
+
warn "Got HTTP status: #{response.status}"
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def health_check_host
|
49
|
+
return "localhost" if @gateway
|
50
|
+
return @host
|
51
|
+
end
|
52
|
+
|
53
|
+
def health_check_port
|
54
|
+
return @tunneled_port ||= port
|
55
|
+
end
|
56
|
+
|
57
|
+
def info(message)
|
58
|
+
Armada.ui.info "#{@host} -- #{message}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def warn(message)
|
62
|
+
Armada.ui.warn "#{@host} -- #{message}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Armada
|
2
|
+
module Connection
|
3
|
+
class Remote
|
4
|
+
|
5
|
+
attr_reader :host, :port, :gateway
|
6
|
+
def initialize(host, port = nil, gateway_host = nil, gateway_user = nil)
|
7
|
+
@host, @port = host.split(":")
|
8
|
+
@port = port ||= @port
|
9
|
+
@gateway_host = gateway_host
|
10
|
+
@gateway_user = gateway_user
|
11
|
+
initialize_gateway!
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
"#{@host}:#{@port}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize_gateway!
|
19
|
+
if @gateway_host
|
20
|
+
@gateway = Net::SSH::Gateway.new(@gateway_host, @gateway_user)
|
21
|
+
@tunneled_port = @gateway.open(@host, @port)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Armada
|
2
|
+
module Deploy
|
3
|
+
class Parallel
|
4
|
+
|
5
|
+
attr_reader :project, :environment, :options
|
6
|
+
def initialize(project, environment, options)
|
7
|
+
@project = project
|
8
|
+
@environment = environment
|
9
|
+
@options = Armada::Configuration.load!(project, environment, options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
Armada.ui.info "Deploying the following image [#{@options[:image]}:#{@options[:tag]}] to these host(s) #{@options[:hosts].join(', ')} in PARALLEL"
|
14
|
+
|
15
|
+
begin
|
16
|
+
@options[:hosts].each_in_parallel do |host|
|
17
|
+
docker_host = Armada::Host.create(host, options)
|
18
|
+
image = docker_host.get_image @options[:image], @options[:tag], @options
|
19
|
+
image.pull
|
20
|
+
|
21
|
+
container = Armada::Container.new(image, docker_host, @options)
|
22
|
+
container.stop
|
23
|
+
container.start
|
24
|
+
|
25
|
+
if @options[:health_check] && @options[:health_check_port]
|
26
|
+
ports = container.ports
|
27
|
+
|
28
|
+
if ports.empty?
|
29
|
+
raise "No ports exposed for this container. Please expose a port for the health check or use the --no-health-check option!"
|
30
|
+
end
|
31
|
+
|
32
|
+
begin
|
33
|
+
health_check_port = ports["#{@options[:health_check_port]}/tcp"][0]["HostPort"]
|
34
|
+
rescue Exception => e
|
35
|
+
raise "Could not find the host port for [#{health_check_port}]. Make sure you put the container port as the :health_check_port."
|
36
|
+
end
|
37
|
+
|
38
|
+
health_check = Armada::Connection::HealthCheck.new(
|
39
|
+
host,
|
40
|
+
health_check_port,
|
41
|
+
@options[:health_check_endpoint],
|
42
|
+
@options[:health_check_delay],
|
43
|
+
@options[:health_check_retries],
|
44
|
+
@options[:ssh_gateway],
|
45
|
+
@options[:ssh_gateway_user]
|
46
|
+
)
|
47
|
+
|
48
|
+
raise "Health check failed! - #{host}" unless health_check.run
|
49
|
+
end
|
50
|
+
end
|
51
|
+
rescue Exception => e
|
52
|
+
Armada.ui.error "#{e.message} \n\n #{e.backtrace.join("\n")}"
|
53
|
+
exit(1)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Armada
|
2
|
+
module Deploy
|
3
|
+
class Rolling
|
4
|
+
|
5
|
+
attr_reader :project, :environment, :options
|
6
|
+
def initialize(project, environment, options)
|
7
|
+
@project = project
|
8
|
+
@environment = environment
|
9
|
+
@options = Armada::Configuration.load!(project, environment, options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
Armada.ui.info "Deploying the following image [#{@options[:image]}:#{@options[:tag]}] to these host(s) #{@options[:hosts].join(', ')}"
|
14
|
+
begin
|
15
|
+
@options[:hosts].each_in_parallel do |host|
|
16
|
+
docker_host = Armada::Host.create(host, options)
|
17
|
+
docker_host.get_image(@options[:image], @options[:tag], @options).pull
|
18
|
+
end
|
19
|
+
|
20
|
+
@options[:hosts].each do |host|
|
21
|
+
docker_host = Armada::Host.create(host, options)
|
22
|
+
image = docker_host.get_image @options[:image], @options[:tag], @options
|
23
|
+
container = Armada::Container.new(image, docker_host, @options)
|
24
|
+
container.stop
|
25
|
+
container.start
|
26
|
+
|
27
|
+
if @options[:health_check] && @options[:health_check_port]
|
28
|
+
ports = container.ports
|
29
|
+
|
30
|
+
if ports.empty?
|
31
|
+
raise "No ports exposed for this container. Please expose a port for the health check or use the --no-health-check option!"
|
32
|
+
end
|
33
|
+
|
34
|
+
begin
|
35
|
+
health_check_port = ports["#{@options[:health_check_port]}/tcp"][0]["HostPort"]
|
36
|
+
rescue Exception => e
|
37
|
+
raise "Could not find the host port for [#{health_check_port}]. Make sure you put the container port as the :health_check_port."
|
38
|
+
end
|
39
|
+
|
40
|
+
health_check = Armada::Connection::HealthCheck.new(
|
41
|
+
host,
|
42
|
+
health_check_port,
|
43
|
+
@options[:health_check_endpoint],
|
44
|
+
@options[:health_check_delay],
|
45
|
+
@options[:health_check_retries],
|
46
|
+
@options[:ssh_gateway],
|
47
|
+
@options[:ssh_gateway_user]
|
48
|
+
)
|
49
|
+
|
50
|
+
raise "Health check failed! - #{host}" unless health_check.run
|
51
|
+
end
|
52
|
+
end
|
53
|
+
rescue Exception => e
|
54
|
+
Armada.ui.error "#{e.message} \n\n #{e.backtrace.join("\n")}"
|
55
|
+
exit(1)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|