dploy 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|