sonoma-remote 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/README.md +67 -0
- data/Rakefile +1 -0
- data/lib/sonoma-remote.rb +1 -0
- data/lib/sonoma/remote.rb +62 -0
- data/lib/sonoma/remote/tasks/all.rake +3 -0
- data/lib/sonoma/remote/tasks/deploy.rake +79 -0
- data/lib/sonoma/remote/tasks/info.rake +39 -0
- data/lib/sonoma/remote/tasks/tasks.rake +45 -0
- data/lib/sonoma/remote/version.rb +5 -0
- data/sonoma-remote.gemspec +25 -0
- metadata +114 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 87225981319ceaea4ec4f194245eb3676403b40d
|
4
|
+
data.tar.gz: c7fad6b2023e33a16525c0379e3c1aa7c5ae0ca3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 79549301b61850497f95822955eb568579bc3c9c7eba18fefe2d52a3ccb473e2d42fe15137b7cebe276c8cd67b68d6cd6a48ad5ce6bb26ba68a4827a497faaff
|
7
|
+
data.tar.gz: 247980a8b93d59c66d030bc96a01753854dd8e39ebe195ac1dc2222fbf40da04f8be08b75ab6d9a3b414fc5e43f679cd21dfdab7fa3cec91350727a35dd3c0be
|
data/.gitignore
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
sonoma-remote
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.2.0
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# Sonoma::Remote
|
2
|
+
|
3
|
+
Manage Docker-based apps on remote servers.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
# gemfile
|
9
|
+
gem "sonoma-remote"
|
10
|
+
|
11
|
+
# Capfile
|
12
|
+
load "sonoma/remote/tasks/all.rake" # or specific ones
|
13
|
+
|
14
|
+
Sonoma::Deploy.configure!({
|
15
|
+
application: "your_app_name",
|
16
|
+
docker_registry: "host_of_registry",
|
17
|
+
docker_repository: "path_in_docker_registry",
|
18
|
+
user: "your_deploy_user"
|
19
|
+
})
|
20
|
+
|
21
|
+
# config/deploy.rb
|
22
|
+
lock "{capistrano-version}"
|
23
|
+
|
24
|
+
# config/uat.rb
|
25
|
+
server("localhost", { roles: %W{app db worker}, user: fetch(:user), primary: true })
|
26
|
+
```
|
27
|
+
|
28
|
+
## Configuration
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
load "sonoma/remote/tasks/all.rake" # or specific ones
|
32
|
+
Sonoma::Deploy.required_config # see required config for loaded tasks
|
33
|
+
Sonoma::Deploy.optional_config # see optional config for loaded tasks
|
34
|
+
```
|
35
|
+
|
36
|
+
## Sample
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
load "sonoma/remote/tasks/all.rake"
|
40
|
+
|
41
|
+
verify = ->(host) do
|
42
|
+
deployed_correct_version = false;
|
43
|
+
on(host) do
|
44
|
+
response = capture("curl", "localhost/status")
|
45
|
+
json_response = JSON.parse(response)
|
46
|
+
deployed_correct_version = (json_response["revision"] == fetch(:revision))
|
47
|
+
end
|
48
|
+
deployed_correct_version
|
49
|
+
end
|
50
|
+
|
51
|
+
docker_options = -> {[
|
52
|
+
"-e HOSTS_IN_ENVIRONMENT=[#{roles(:app).collect(&:hostname).join(",")}]",
|
53
|
+
"-v /var/groupon/#{fetch(:application)}/log:/usr/src/app/log",
|
54
|
+
"-v /var/groupon/#{fetch(:application)}/config/local:/usr/src/app/config/local"
|
55
|
+
]}
|
56
|
+
|
57
|
+
Sonoma::Remote.configure!({
|
58
|
+
application: "gstream",
|
59
|
+
docker_options: docker_options,
|
60
|
+
docker_registry: "docker.groupondev.com",
|
61
|
+
docker_repository: "grpn_stream/gstream",
|
62
|
+
load_balancer_delay: 3,
|
63
|
+
server_boot_time: 3,
|
64
|
+
user: "gstream_deploy",
|
65
|
+
verify: verify
|
66
|
+
})
|
67
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "sonoma/remote"
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require "capistrano/setup"
|
2
|
+
require "hipchat"
|
3
|
+
|
4
|
+
module Sonoma
|
5
|
+
module Remote
|
6
|
+
@required_config = {}
|
7
|
+
@optional_config = {
|
8
|
+
hipchat_rooms: "Array of HipChat room ids, e.g. `[1,2]`",
|
9
|
+
hipchat_token: "Token to make requests against their API",
|
10
|
+
log_level: "Defaults to :info"
|
11
|
+
}
|
12
|
+
|
13
|
+
class << self
|
14
|
+
attr_accessor :required_config,
|
15
|
+
:optional_config
|
16
|
+
|
17
|
+
def add_required_config!(options)
|
18
|
+
self.required_config.merge!(symbolize(options))
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_optional_config!(options)
|
22
|
+
self.optional_config.merge!(symbolize(options))
|
23
|
+
end
|
24
|
+
|
25
|
+
def configure!(config = {})
|
26
|
+
config = symbolize(config)
|
27
|
+
|
28
|
+
missing_values = required_config.dup.delete_if { |(key, value)| config.key?(key) }
|
29
|
+
fail_task("Missing required config: #{missing_values}") if missing_values.any?
|
30
|
+
|
31
|
+
set(:log_level, (config[:log_level] || ENV["LOG_LEVEL"] || :info))
|
32
|
+
|
33
|
+
config.each do |(key, value)|
|
34
|
+
set(key, value)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def notify(message, color = "green")
|
39
|
+
return unless fetch(:hipchat_token)
|
40
|
+
options = { api_version: "v2" }.merge(fetch(:hipchat_options, {}))
|
41
|
+
hipchat_client = HipChat::Client.new(fetch(:hipchat_token), options)
|
42
|
+
user = fetch(:user)[0...10]
|
43
|
+
|
44
|
+
fetch(:hipchat_rooms, []).each do |room|
|
45
|
+
hipchat_client[room].send(user, message, { color: color })
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def fail_task(message)
|
50
|
+
run_locally { fatal(message) }
|
51
|
+
notify(message, "red")
|
52
|
+
fail
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def symbolize(hash)
|
58
|
+
hash.each_with_object({}) { |(key, value), memo| memo[key.to_sym] = value }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
namespace :deploy do
|
2
|
+
Sonoma::Remote.add_required_config!({
|
3
|
+
application: "e.g. `my_app`",
|
4
|
+
docker_registry: "e.g. `docker.groupondev.com`",
|
5
|
+
docker_repository: "e.g. `my-org/my-repo`"
|
6
|
+
})
|
7
|
+
|
8
|
+
Sonoma::Remote.add_optional_config!({
|
9
|
+
docker_options: "Can take an array or proc returning an array, e.g. `[\"-e FOO=BAR\"]`",
|
10
|
+
load_balancer_delay: "Wait time in seconds, e.g. `1`",
|
11
|
+
server_boot_time: "Wait time in seconds, e.g. `3`"
|
12
|
+
})
|
13
|
+
|
14
|
+
desc "Stop app"
|
15
|
+
task :stop, %w(hosts) do |_task, args|
|
16
|
+
hosts = args[:hosts]
|
17
|
+
hosts = env.filter(roles(:all)) unless hosts
|
18
|
+
|
19
|
+
on(hosts) do
|
20
|
+
execute(:sudo, "docker exec #{fetch(:application)} rm tmp/heartbeat.txt", { raise_on_non_zero_exit: false })
|
21
|
+
sleep(fetch(:load_balancer_delay).to_i)
|
22
|
+
execute(:sudo, "docker stop #{fetch(:application)}", { raise_on_non_zero_exit: false })
|
23
|
+
execute(:sudo, "docker rm #{fetch(:application)}", { raise_on_non_zero_exit: false })
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Start app"
|
28
|
+
task :start, %w(hosts) => %w(verify_revision) do |_task, args|
|
29
|
+
hosts = args[:hosts]
|
30
|
+
hosts = env.filter(roles(:all)) unless hosts
|
31
|
+
|
32
|
+
on(hosts) do
|
33
|
+
repo = "#{fetch(:docker_registry)}/#{fetch(:docker_repository)}:#{fetch(:revision)}"
|
34
|
+
default_options = ["-e RACK_ENV=#{fetch(:stage)}", "--name #{fetch(:application)}"]
|
35
|
+
options = (default_options + fetch(:docker_options, [])).join(" ")
|
36
|
+
|
37
|
+
execute(:sudo, "docker run -d -p 80:9292 #{options} #{repo}")
|
38
|
+
execute(:sudo, "docker exec #{fetch(:application)} /bin/bash -c \"echo #{fetch(:revision)} > REVISION\"")
|
39
|
+
sleep(fetch(:server_boot_time).to_i)
|
40
|
+
|
41
|
+
%w(verify warmup).each do |check|
|
42
|
+
check_proc = fetch(check.to_sym, ->(host) { true })
|
43
|
+
Sonoma::Remote.fail_task("#{check} failed") if !check_proc.call(host)
|
44
|
+
end
|
45
|
+
|
46
|
+
execute(:sudo, "docker exec #{fetch(:application)} touch tmp/heartbeat.txt")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "Deploy to docker container"
|
51
|
+
task app: %w(verify_revision) do
|
52
|
+
on(roles(:app), { in: :sequence, wait: 1 }) do |host|
|
53
|
+
%w(deploy:stop deploy:start).each do |task_name|
|
54
|
+
task = Rake::Task[task_name]
|
55
|
+
task.invoke(host)
|
56
|
+
task.reenable
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
task(:notify_deploy_start) do
|
62
|
+
@start_time = Time.now
|
63
|
+
user = %x(whoami).chomp
|
64
|
+
deploy_ticket = ENV["DEPLOY_TICKET"]
|
65
|
+
regarding = deploy_ticket ? "regarding #{deploy_ticket}" : "manually"
|
66
|
+
|
67
|
+
message = "#{user} deploying #{fetch(:application)}@#{fetch(:revision)[0...10]} to #{fetch(:stage)}"
|
68
|
+
message += " (#{deploy_ticket})" if deploy_ticket
|
69
|
+
Sonoma::Remote.notify(message, :purple)
|
70
|
+
end
|
71
|
+
|
72
|
+
task(:notify_deploy_end) do
|
73
|
+
duration = (Time.now - @start_time)
|
74
|
+
Sonoma::Remote.notify("Deployed #{fetch(:application)}@#{fetch(:revision)[0...10]} to #{fetch(:stage)} in #{duration}")
|
75
|
+
end
|
76
|
+
|
77
|
+
before("deploy:app", :notify_deploy_start)
|
78
|
+
after("deploy:app", :notify_deploy_end)
|
79
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
namespace :info do
|
2
|
+
DEFAULT_STATUS_ENDPOINT = "http://localhost/status"
|
3
|
+
Sonoma::Remote.add_optional_config!({
|
4
|
+
status_endpoint: "Defaults to #{DEFAULT_STATUS_ENDPOINT}"
|
5
|
+
})
|
6
|
+
|
7
|
+
task(:run, [:command]) do |_task, args|
|
8
|
+
command = args[:command]
|
9
|
+
on roles(:all), in: :sequence do |host|
|
10
|
+
execute(*command) # fails unless ok
|
11
|
+
info(capture(*command))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Show docker version"
|
16
|
+
task(:docker) do
|
17
|
+
command = "sudo docker version"
|
18
|
+
Rake::Task["info:run"].invoke(command)
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "Show docker processes"
|
22
|
+
task(:docker_ps) do
|
23
|
+
command = "sudo docker ps"
|
24
|
+
Rake::Task["info:run"].invoke(command)
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Show roller stats"
|
28
|
+
task(:roller) do
|
29
|
+
roller_endpoint = "http://localhost:1875/host/rollinfo"
|
30
|
+
Rake::Task["info:run"].invoke("curl -s #{roller_endpoint}")
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Show host status"
|
34
|
+
task(:status) do
|
35
|
+
status_endpoint = fetch(:status_endpoint, DEFAULT_STATUS_ENDPOINT)
|
36
|
+
Rake::Task["info:run"].invoke("curl -sv #{status_endpoint}")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
Sonoma::Remote.add_required_config!({
|
2
|
+
docker_registry: "e.g. `docker.groupondev.com`",
|
3
|
+
docker_repository: "e.g. `my-org/my-repo`"
|
4
|
+
})
|
5
|
+
|
6
|
+
Sonoma::Remote.add_optional_config!({
|
7
|
+
console_command: "e.g. `bin/console`"
|
8
|
+
})
|
9
|
+
|
10
|
+
desc "Verifies that REVISION is set and it is available in the docker registry"
|
11
|
+
task(:verify_revision) do
|
12
|
+
environmental_ref = (ENV["REVISION"] || ENV["VERSION"] || ENV["BRANCH"] || ENV["TAG"] || ENV["REF"] || ENV["SHA"])
|
13
|
+
revision = `git rev-parse #{environmental_ref || "HEAD"}`.chomp
|
14
|
+
run_locally { info("REVISION set to #{revision}") }
|
15
|
+
|
16
|
+
set(:revision, revision)
|
17
|
+
|
18
|
+
on(roles(:app, { primary: true })) do
|
19
|
+
uri = URI("http://#{fetch(:docker_registry)}/v1/repositories/#{fetch(:docker_repository)}/tags/#{fetch(:revision)}")
|
20
|
+
if (Net::HTTP.get_response(uri).code != "200")
|
21
|
+
Sonoma::Remote.fail_task("#{fetch(:revision)} not present in registry.")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "Load console in env"
|
27
|
+
task :console do
|
28
|
+
on(roles(:utility, { primary: true })) do |host|
|
29
|
+
command = "sudo docker exec -it #{fetch(:application)} #{fetch(:console_command, "bin/console")}"
|
30
|
+
exec(%Q(ssh #{fetch(:user)}@#{host.hostname} -t '#{command}'))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "Run rake task from a docker container (`RAKE_TASK=db:migrate cap uat rake`)"
|
35
|
+
task :rake, %w(as_dba) => %w(verify_revision) do |_task, args|
|
36
|
+
docker_repo = "#{fetch(:docker_registry)}/#{fetch(:docker_repository)}:#{fetch(:revision)}"
|
37
|
+
|
38
|
+
options = ["-e RACK_ENV=#{fetch(:stage)}"]
|
39
|
+
options << "-e AS_DBA=#{args["as_dba"]}" if args["as_dba"]
|
40
|
+
options.concat(fetch(:docker_options, []))
|
41
|
+
|
42
|
+
on(roles(:worker, { primary: true })) do
|
43
|
+
puts capture("sudo docker run --rm #{options.join(" ")} --name rake_task #{docker_repo} bin/rake #{ENV["RAKE_TASK"]}")
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sonoma/remote/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sonoma-remote"
|
8
|
+
spec.version = Sonoma::Remote::VERSION
|
9
|
+
spec.authors = ["Sonoma"]
|
10
|
+
spec.email = ["goods-cim-orders-dev@groupon.com"]
|
11
|
+
spec.summary = %q{Deploy/Manage/Monitor Remote Servers Running Docker}
|
12
|
+
spec.description = %q{}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "capistrano", ">= 3.4.0"
|
22
|
+
spec.add_dependency "hipchat"
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sonoma-remote
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sonoma
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-05-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: capistrano
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.4.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.4.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: hipchat
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.5'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: ''
|
70
|
+
email:
|
71
|
+
- goods-cim-orders-dev@groupon.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".ruby-gemset"
|
78
|
+
- ".ruby-version"
|
79
|
+
- Gemfile
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- lib/sonoma-remote.rb
|
83
|
+
- lib/sonoma/remote.rb
|
84
|
+
- lib/sonoma/remote/tasks/all.rake
|
85
|
+
- lib/sonoma/remote/tasks/deploy.rake
|
86
|
+
- lib/sonoma/remote/tasks/info.rake
|
87
|
+
- lib/sonoma/remote/tasks/tasks.rake
|
88
|
+
- lib/sonoma/remote/version.rb
|
89
|
+
- sonoma-remote.gemspec
|
90
|
+
homepage: ''
|
91
|
+
licenses:
|
92
|
+
- MIT
|
93
|
+
metadata: {}
|
94
|
+
post_install_message:
|
95
|
+
rdoc_options: []
|
96
|
+
require_paths:
|
97
|
+
- lib
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
requirements: []
|
109
|
+
rubyforge_project:
|
110
|
+
rubygems_version: 2.4.5
|
111
|
+
signing_key:
|
112
|
+
specification_version: 4
|
113
|
+
summary: Deploy/Manage/Monitor Remote Servers Running Docker
|
114
|
+
test_files: []
|