sonoma-remote 0.0.1

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 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
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ sonoma-remote
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.2.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sonoma-remote.gemspec
4
+ gemspec
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,3 @@
1
+ load "sonoma/remote/tasks/deploy.rake"
2
+ load "sonoma/remote/tasks/info.rake"
3
+ load "sonoma/remote/tasks/tasks.rake"
@@ -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,5 @@
1
+ module Sonoma
2
+ module Remote
3
+ VERSION = "0.0.1"
4
+ end
5
+ 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: []