dploy 0.0.2
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.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/config/dploy.yml +11 -0
- data/dploy.gemspec +26 -0
- data/lib/dploy.rb +10 -0
- data/lib/dploy/chef_client.rb +52 -0
- data/lib/dploy/client.rb +27 -0
- data/lib/dploy/deployer.rb +68 -0
- data/lib/dploy/ec2_client.rb +26 -0
- data/lib/dploy/load_config.rb +0 -0
- data/lib/dploy/logger.rb +38 -0
- data/lib/dploy/shell_command.rb +28 -0
- data/lib/dploy/version.rb +3 -0
- metadata +81 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/config/dploy.yml
ADDED
data/dploy.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "dploy/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "dploy"
|
7
|
+
s.version = Dploy::VERSION
|
8
|
+
s.authors = ["DuckDO"]
|
9
|
+
s.email = ["james@stylesaint.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{deployment module for chef and aws}
|
12
|
+
s.description = %q{deploymoent module for chef and aws}
|
13
|
+
|
14
|
+
s.rubyforge_project = "dploy"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
# s.add_development_dependency "rspec"
|
23
|
+
# s.add_runtime_dependency "rest-client"
|
24
|
+
s.add_runtime_dependency('aws-sdk')
|
25
|
+
s.add_runtime_dependency('chef')
|
26
|
+
end
|
data/lib/dploy.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'chef'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Dploy
|
5
|
+
|
6
|
+
class ChefClient
|
7
|
+
|
8
|
+
def initialize(server_url, user, key_path = nil)
|
9
|
+
key_path ||= "#{user}.pem"
|
10
|
+
@rest_client = Chef::REST.new(server_url, user, key_path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def update_node_attributes(node_name,attributes)
|
14
|
+
node = get_node(node_name)
|
15
|
+
node[:normal] = node[:normal].merge(attributes)
|
16
|
+
symbolize(@rest_client.put_rest("nodes/#{node_name}",node))
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_nodes
|
20
|
+
@rest_client.get_rest("nodes")
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_node(name)
|
24
|
+
symbolize(@rest_client.get_rest("nodes/#{name}"))
|
25
|
+
end
|
26
|
+
|
27
|
+
def put_node(node)
|
28
|
+
symbolize(@rest_client.put_rest("nodes/#{node[:name]}",node))
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_node(node)
|
32
|
+
symbolize(@rest_client.post_rest("nodes",node.to_json))
|
33
|
+
end
|
34
|
+
|
35
|
+
def delete_node(node_name)
|
36
|
+
symbolize(@rest_client.delete_rest("nodes/#{node_name}"))
|
37
|
+
end
|
38
|
+
|
39
|
+
def symbolize(hash)
|
40
|
+
json_parse_opts = {:symbolize_names => true}
|
41
|
+
JSON.parse(hash.to_json, json_parse_opts)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
|
data/lib/dploy/client.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'rest_client'
|
3
|
+
|
4
|
+
module Dploy
|
5
|
+
|
6
|
+
module Client
|
7
|
+
|
8
|
+
def self.deploy(server, project, environment, version)
|
9
|
+
deploy_data = JSON.parse((RestClient.post "http://#{server}/deploy", { :project => project, :environment => environment, :version => version }.to_json, :content_type => :json, :accept => :json))
|
10
|
+
self.get_deploy_logs(server, deploy_data['id'])
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.get_deploy_logs(server, deploy_id)
|
14
|
+
start_id = "000000000000000000000000"
|
15
|
+
while true
|
16
|
+
logs = JSON.parse(RestClient.get( "http://#{server}/logs/#{deploy_id}/?start_id=#{start_id}", :accept => :json))
|
17
|
+
logs.each do |row|
|
18
|
+
puts row
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
Dploy::Client.deploy("dploy.internal.stylesaint.com:3000", "community", "test", ENV['VERSION'])
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "dploy/version"
|
2
|
+
require 'dploy/chef_client'
|
3
|
+
require 'dploy/ec2_client'
|
4
|
+
require 'dploy/logger'
|
5
|
+
require 'dploy/shell_command'
|
6
|
+
|
7
|
+
module Dploy
|
8
|
+
|
9
|
+
class Deployer
|
10
|
+
|
11
|
+
attr_accessor :deploy_data
|
12
|
+
|
13
|
+
def initialize(deploy_data)
|
14
|
+
|
15
|
+
@deploy_data = deploy_data
|
16
|
+
|
17
|
+
@ec2 = EC2Client.new(
|
18
|
+
DPLOY_CONFIG['ec2_access_key_id'],
|
19
|
+
DPLOY_CONFIG['ec2_access_key'],
|
20
|
+
DPLOY_CONFIG['ec2_endpoint'])
|
21
|
+
|
22
|
+
@chef = ChefClient.new(
|
23
|
+
DPLOY_CONFIG['chef_api_url'],
|
24
|
+
DPLOY_CONFIG['chef_api_user'],
|
25
|
+
DPLOY_CONFIG['chef_api_key'])
|
26
|
+
|
27
|
+
|
28
|
+
#TODO validate deploy data
|
29
|
+
|
30
|
+
@deploy_data[:id] = "#{@deploy_data[:project]}-#{@deploy_data[:environment]}-#{@deploy_data[:version].gsub('.','-')}-#{Time.now.to_i}"
|
31
|
+
@log = Dploy::Logger.new(DPLOY_CONFIG['mongo_server'], @deploy_data[:id])
|
32
|
+
end
|
33
|
+
|
34
|
+
def deploy()
|
35
|
+
begin
|
36
|
+
#Get Instances
|
37
|
+
search_tags = { 'project' => @deploy_data[:project], 'environment' => @deploy_data[:environment] }
|
38
|
+
@log.debug("Searching For EC2 Instances")
|
39
|
+
@log.debug("Search tags: #{search_tags}")
|
40
|
+
instances = @ec2.get_instances_by_tags(search_tags)
|
41
|
+
@log.debug("#{instances.count} matching instances found")
|
42
|
+
|
43
|
+
instances.each do |instance|
|
44
|
+
|
45
|
+
@log.debug("Starting deploy for instance: #{instance.id}")
|
46
|
+
#TODO Remove from LB
|
47
|
+
|
48
|
+
#update chef node
|
49
|
+
node_name = "#{@deploy_data[:environment]}-#{@deploy_data[:project]}-#{instance.id}"
|
50
|
+
@log.debug("Updating chef node: #{node_name}")
|
51
|
+
attributes = { :dploy => { :target_version => @deploy_data[:version], :status => 'deploying' } }
|
52
|
+
@chef.update_node_attributes(node_name, attributes)
|
53
|
+
|
54
|
+
#Run Chef client
|
55
|
+
@log.debug("Running Chef Client for #{node_name} (#{instance.dns_name})")
|
56
|
+
ShellCmd.run("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i #{DPLOY_CONFIG['ec2_ssh_key_path']} ubuntu@#{instance.dns_name} sudo chef-client", @deploy_data[:id])
|
57
|
+
|
58
|
+
#TODO Test
|
59
|
+
rescue Exception
|
60
|
+
@log.error($!)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
|
3
|
+
module Dploy
|
4
|
+
|
5
|
+
class EC2Client
|
6
|
+
|
7
|
+
def initialize(access_key_id, access_key, ec2_endpoint)
|
8
|
+
@ec2 = AWS::EC2.new(
|
9
|
+
:access_key_id => access_key_id,
|
10
|
+
:secret_access_key => access_key,
|
11
|
+
:ec2_endpoint => ec2_endpoint
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_instances_by_tags(search_tags = {})
|
16
|
+
search_tags.map{|k,v| {k => v}}.reduce(@ec2.instances){|filtered_result,search_tag | filtered_result.tagged(search_tag.keys).tagged_values(search_tag.values)}
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
def get_resource_attributes(resource, attributes)
|
21
|
+
attributes.map { |attribute| {attribute => resource.send(attribute)} if resource.respond_to?(attribute) }.reduce {|sum, att| sum.merge(att) }
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
File without changes
|
data/lib/dploy/logger.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'mongo'
|
2
|
+
|
3
|
+
module Dploy
|
4
|
+
|
5
|
+
class Logger
|
6
|
+
|
7
|
+
def initialize(mongo_host, deploy_id)
|
8
|
+
@log = Mongo::Connection.new(mongo_host).db('log')[deploy_id]
|
9
|
+
end
|
10
|
+
|
11
|
+
private :log
|
12
|
+
def log(message, meta_data )
|
13
|
+
@log.insert({:meta_data => meta_data, :message => message})
|
14
|
+
end
|
15
|
+
|
16
|
+
def debug(message, meta_data = {})
|
17
|
+
meta_data.merge({:log_type => 'debug' })
|
18
|
+
log(message, metadata)
|
19
|
+
end
|
20
|
+
|
21
|
+
def error(message, meta_data = {})
|
22
|
+
meta_data.merge({:log_type => 'debug' })
|
23
|
+
log(message, metadata)
|
24
|
+
end
|
25
|
+
|
26
|
+
def completion(message, meta_data = {})
|
27
|
+
meta_data.merge({:log_type => 'debug' })
|
28
|
+
log(message, metadata)
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_messages(collection, start_id = "000000000000000000000000")
|
32
|
+
@@db[collection].find(:_id => {"$gt" => BSON::ObjectId(start_id)}).map {|row| row}
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'pty'
|
2
|
+
|
3
|
+
module Dploy
|
4
|
+
|
5
|
+
class ShellCmd
|
6
|
+
|
7
|
+
def self.run(command, log)
|
8
|
+
PTY.spawn(command) do |output, input, pid|
|
9
|
+
begin
|
10
|
+
while !PTY.check(pid)
|
11
|
+
if log != nil
|
12
|
+
line = output.gets
|
13
|
+
if line.upcase.include? "ERROR"
|
14
|
+
log.error(line)
|
15
|
+
else
|
16
|
+
log.debug(line)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
rescue Errno::EIO
|
21
|
+
puts "end"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dploy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- DuckDO
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-11-04 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: aws-sdk
|
16
|
+
requirement: &24476420 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *24476420
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: chef
|
27
|
+
requirement: &24475920 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *24475920
|
36
|
+
description: deploymoent module for chef and aws
|
37
|
+
email:
|
38
|
+
- james@stylesaint.com
|
39
|
+
executables: []
|
40
|
+
extensions: []
|
41
|
+
extra_rdoc_files: []
|
42
|
+
files:
|
43
|
+
- .gitignore
|
44
|
+
- Gemfile
|
45
|
+
- Rakefile
|
46
|
+
- config/dploy.yml
|
47
|
+
- dploy.gemspec
|
48
|
+
- lib/dploy.rb
|
49
|
+
- lib/dploy/chef_client.rb
|
50
|
+
- lib/dploy/client.rb
|
51
|
+
- lib/dploy/deployer.rb
|
52
|
+
- lib/dploy/ec2_client.rb
|
53
|
+
- lib/dploy/load_config.rb
|
54
|
+
- lib/dploy/logger.rb
|
55
|
+
- lib/dploy/shell_command.rb
|
56
|
+
- lib/dploy/version.rb
|
57
|
+
homepage: ''
|
58
|
+
licenses: []
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ! '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
requirements: []
|
76
|
+
rubyforge_project: dploy
|
77
|
+
rubygems_version: 1.8.11
|
78
|
+
signing_key:
|
79
|
+
specification_version: 3
|
80
|
+
summary: deployment module for chef and aws
|
81
|
+
test_files: []
|