Skip to content

Commit

Permalink
Merge pull request #47 from didip/dogestry
Browse files Browse the repository at this point in the history
Dogestry
  • Loading branch information
jessedearing committed Oct 17, 2014
2 parents 3586624 + 4061f62 commit 68ea724
Show file tree
Hide file tree
Showing 15 changed files with 287 additions and 71 deletions.
20 changes: 20 additions & 0 deletions README.md
Expand Up @@ -228,6 +228,26 @@ Returns a list of all the images for this project in the registry.
$ bundle exec centurion -p radio-radio -e staging -a list
````

###Changing docker registry

Centurion have the ability to use different registry.

The current alternative registry is `dogestry`. Dogestry allows centurion to push/pull docker images on S3.

See example below to use `dogestry`:

```ruby
namespace :environment do
task :common do
registry :dogestry # Required
set :aws_access_key_id, 'abc123' # Required
set :aws_secret_key, 'xyz' # Required
set :s3_bucket, 'docker-images-bucket' # Required
set :s3_region, 'us-east-1' # Optional
end
end
```

Future Additions
----------------

Expand Down
17 changes: 17 additions & 0 deletions bin/centurionize
Expand Up @@ -24,6 +24,20 @@ unless File.exists?(project_file)
set :image, '#{opts[:registry_base]}#{opts[:project]}'
# Point this to an appropriate health check endpoint for rolling deploys (defaults to '/')
# set :status_endpoint, '/status/check'
# Example on how to change docker registry to Dogestry.
# This requires:
# - aws_access_key_id
# - aws_secret_key
# - s3_bucket
#
# And optionally:
# - s3_region (default to us-east-1)
#
# registry :dogestry
# set :aws_access_key_id, 'abc123'
# set :aws_secret_key, 'xyz'
# set :s3_bucket, 'bucket-for-docker-images'
end
desc 'Staging environment'
Expand All @@ -33,6 +47,9 @@ unless File.exists?(project_file)
# host_port 10234, container_port: 9292
# host 'docker-server-staging-1.example.com'
# host 'docker-server-staging-2.example.com'
# You can assign different docker daemon port. Example:
# host 'docker-server-staging-3.example.com:4243'
end
desc 'Production environment'
Expand Down
6 changes: 5 additions & 1 deletion lib/centurion/deploy_dsl.rb
Expand Up @@ -27,7 +27,7 @@ def command(command)
end

def localhost
# DOCKER_HOST is like 'tcp://127.0.0.1:4243'
# DOCKER_HOST is like 'tcp://127.0.0.1:2375'
docker_host_uri = URI.parse(ENV['DOCKER_HOST'] || "tcp://127.0.0.1")
host_and_port = [docker_host_uri.host, docker_host_uri.port].compact.join(':')
host(host_and_port)
Expand Down Expand Up @@ -71,6 +71,10 @@ def get_current_tags_for(image)
end
end

def registry(type)
set(:registry, type)
end

private

def add_to_bindings(host_ip, container_port, port, type='tcp')
Expand Down
2 changes: 1 addition & 1 deletion lib/centurion/docker_server.rb
Expand Up @@ -21,7 +21,7 @@ class Centurion::DockerServer
def initialize(host, docker_path)
@docker_path = docker_path
@hostname, @port = host.split(':')
@port ||= '4243'
@port ||= '2375'
end

def current_tags_for(image)
Expand Down
45 changes: 0 additions & 45 deletions lib/centurion/docker_via_cli.rb
Expand Up @@ -24,49 +24,4 @@ def tail(container_id)
def attach(container_id)
Process.exec("#{@docker_path} -H=#{@docker_host} attach #{container_id}")
end

private

def echo(command)
if Thread.list.find_all { |t| t.status == 'run' }.count > 1
run_without_echo(command)
else
run_with_echo(command)
end
end

def run_without_echo(command)
output = Queue.new
output_thread = Thread.new do
while true do
begin
puts output.pop
rescue => e
info "Rescuing... #{e.message}"
end
end
end

IO.popen(command) do |io|
io.each_line { |line| output << line }
end

output_thread.kill
validate_status(command)
end

def run_with_echo(command)
$stdout.sync = true
$stderr.sync = true
IO.popen(command) do |io|
io.each_char { |char| print char }
end
validate_status(command)
end

def validate_status(command)
unless $?.success?
raise "The command failed with a non-zero exit status: #{$?.exitstatus}. Command: '#{command}'"
end
end
end
84 changes: 84 additions & 0 deletions lib/centurion/dogestry.rb
@@ -0,0 +1,84 @@
require_relative 'logging'

module Centurion; end

class Centurion::Dogestry
include Centurion::Logging

def initialize(options = {})
@options = options
end

# Cross-platform way of finding an executable in the $PATH.
# which('ruby') #=> /usr/bin/ruby
def which(cmd)
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
exts.each { |ext|
exe = File.join(path, "#{cmd}#{ext}")
return exe if File.executable?(exe) && !File.directory?(exe)
}
end
return nil
end

def validate_before_exec
unless which('dogestry')
message = 'Unable to find "dogestry" executable'
error message
raise message
end
end

def aws_access_key_id
@options[:aws_access_key_id]
end

def aws_secret_key
@options[:aws_secret_key]
end

def s3_bucket
@options[:s3_bucket]
end

def s3_region
@options[:s3_region] || 'us-east-1'
end

def s3_url
"s3://#{s3_bucket}/?region=#{s3_region}"
end

def docker_host
@options[:docker_host] || 'tcp://localhost:2375'
end

def set_envs
ENV['DOCKER_HOST'] = docker_host
ENV['AWS_ACCESS_KEY'] = aws_access_key_id
ENV['AWS_SECRET_KEY'] = aws_secret_key

info "Dogestry ENV: #{ENV.inspect}"
end

def exec_command(command, repo)
command = "dogestry #{command} #{s3_url} #{repo}"
info "Executing: #{command}"
command
end

def pull(repo)
validate_before_exec
set_envs

echo(exec_command('pull', repo))
end

def push(repo)
validate_before_exec
set_envs

echo(exec_command('push', repo))
end
end
43 changes: 43 additions & 0 deletions lib/centurion/logging.rb
Expand Up @@ -20,6 +20,49 @@ def debug(*args)
log.debug args.join(' ')
end

def echo(command)
if Thread.list.find_all { |t| t.status == 'run' }.count > 1
run_without_echo(command)
else
run_with_echo(command)
end
end

def run_without_echo(command)
output = Queue.new
output_thread = Thread.new do
while true do
begin
puts output.pop
rescue => e
info "Rescuing... #{e.message}"
end
end
end

IO.popen(command) do |io|
io.each_line { |line| output << line }
end

output_thread.kill
validate_status(command)
end

def run_with_echo(command)
$stdout.sync = true
$stderr.sync = true
IO.popen(command) do |io|
io.each_char { |char| print char }
end
validate_status(command)
end

def validate_status(command)
unless $?.success?
raise "The command failed with a non-zero exit status: #{$?.exitstatus}. Command: '#{command}'"
end
end

private

def log(*args)
Expand Down
43 changes: 40 additions & 3 deletions lib/tasks/deploy.rake
Expand Up @@ -27,6 +27,38 @@ task :stop => ['deploy:stop']
namespace :deploy do
include Centurion::Deploy

namespace :dogestry do
task :validate_pull_image do
['aws_access_key_id', 'aws_secret_key', 's3_bucket'].each do |env_var|
unless fetch(env_var.to_sym)
$stderr.puts "\n\n#{env_var} is not defined."
exit(1)
end
end
end

task :pull_image do
invoke 'deploy:dogestry:validate_pull_image'

target_servers = Centurion::DockerServerGroup.new(fetch(:hosts), fetch(:docker_path))
target_servers.each_in_parallel do |target_server|
dogestry_options = {
aws_access_key_id: fetch(:aws_access_key_id),
aws_secret_key: fetch(:aws_secret_key),
s3_bucket: fetch(:s3_bucket),
s3_region: fetch(:s3_region) || 'us-east-1',
docker_host: "tcp://#{target_server.hostname}:#{target_server.port}"
}

$stdout.puts "** Pulling image(#{fetch(:image)}:#{fetch(:tag)}) from Dogestry: #{dogestry_options.inspect}"

registry = Centurion::Dogestry.new(dogestry_options)

registry.pull("#{fetch(:image)}:#{fetch(:tag)}")
end
end
end

task :get_image do
invoke 'deploy:pull_image'
invoke 'deploy:determine_image_id_from_first_server'
Expand Down Expand Up @@ -126,11 +158,16 @@ namespace :deploy do
info "--no-pull option specified: skipping pull"
next
end

$stderr.puts "Fetching image #{fetch(:image)}:#{fetch(:tag)} IN PARALLEL\n"

target_servers = Centurion::DockerServerGroup.new(fetch(:hosts), fetch(:docker_path))
target_servers.each_in_parallel do |target_server|
target_server.pull(fetch(:image), fetch(:tag))
if fetch(:registry) == 'dogestry'
invoke 'deploy:dogestry:pull_image'
else
target_servers = Centurion::DockerServerGroup.new(fetch(:hosts), fetch(:docker_path))
target_servers.each_in_parallel do |target_server|
target_server.pull(fetch(:image), fetch(:tag))
end
end
end

Expand Down
6 changes: 3 additions & 3 deletions spec/capistrano_dsl_spec.rb
Expand Up @@ -27,7 +27,7 @@ class DSLTest
end
end

context 'with a current environment set' do
context 'with a current environment set' do
before do
DSLTest.set_current_environment(:test)
end
Expand All @@ -46,11 +46,11 @@ class DSLTest

it 'returns true for any? when the value exists' do
DSLTest.set(:foo, 'bar')
expect(DSLTest.any?(:foo)).to be_true
expect(DSLTest.any?(:foo)).to be_truthy
end

it 'returns false for any? when the value does not exist' do
expect(DSLTest.any?(:foo)).to be_false
expect(DSLTest.any?(:foo)).to be_falsey
end

it 'passes through the any? method to values that support it' do
Expand Down

0 comments on commit 68ea724

Please sign in to comment.