ops_worker 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # opsworker
2
+
3
+ Quick command line tool for handling servers on OpsWorks.
4
+
5
+ ## Installation
6
+ gem install ops_worker
7
+
8
+ ## Example
9
+ ```ruby
10
+ require 'ops_worker'
11
+
12
+ client = OpsWorker::Client.new
13
+ # or, client = OpsWorker::Client.new(:access_key_id => "foo", :secret_access_key => "bar")
14
+
15
+ pp client.stacks
16
+ # => [#<OpsWorker::Stack f1735a57-51c3-4933-8440-4f7beb2cb2ff, homepage_staging, us-east-1>,
17
+ #<OpsWorker::Stack 3bf98404-dafc-45c4-9fa2-a17485aba4ec, homepage_production, us-east-1>]
18
+
19
+ app = client.find_app_by_name("homepage_staging")
20
+ app.instances.each do |instance|
21
+ puts("#{instance.name} is online: #{instance.online?}")
22
+ end
23
+
24
+ # Deploy the current revision branch set
25
+ app.deploy()
26
+
27
+ # Switches the current revision branch to feature/new, deploys, then switches it back
28
+ app.deploy("feature/new")
29
+
30
+ app.update_cookbooks()
31
+
32
+ app.execute_recipes(["monit"])
33
+
34
+ app.rollback()
35
+ ```
@@ -0,0 +1,119 @@
1
+ module OpsWorker
2
+ class App
3
+ attr_reader :id, :stack, :name, :revision
4
+
5
+ def initialize(id, name, revision, stack, opsworks_client)
6
+ @id = id
7
+ @name = name
8
+ @stack = stack
9
+ @revision = revision
10
+ @opsworks_client = opsworks_client
11
+ end
12
+
13
+ def instances
14
+ if @instances
15
+ return @instances
16
+ end
17
+
18
+ instances_result = @opsworks_client.describe_instances(:stack_id => @stack.id, :app_id => @id)[:instances]
19
+ @instances = instances_result.map do |instance_hash|
20
+ layers = @stack.layers.select {|l| instance_hash[:layer_ids].include?(l.id)}
21
+ Instance.new(instance_hash[:instance_id],
22
+ instance_hash[:hostname],
23
+ instance_hash[:status],
24
+ instance_hash[:instance_type],
25
+ instance_hash[:elastic_ip],
26
+ instance_hash[:availability_zone],
27
+ self,
28
+ layers)
29
+ end
30
+ end
31
+
32
+ # Updates the revision in this app such that all future deploys pull from this revision
33
+ # @param revision String revision to update, e.g., "feature/foo" or "d6053592ee39e64c5f092b0ba6e9cd1aa8334828"
34
+ def update_revision(revision)
35
+ OpsWorker.logger.info {"Changing revision from #{@revision} to #{revision} for app #{@name}"}
36
+
37
+ @opsworks_client.update_app(:app_id => @id, :app_source => {:revision => revision})
38
+ @revision = revision
39
+ reload()
40
+ end
41
+
42
+ # @param revision String revision to deploy, e.g., "feature/foo" or "d6053592ee39e64c5f092b0ba6e9cd1aa8334828"
43
+ def deploy(revision = nil)
44
+ OpsWorker.logger.info {"Deploying app #{@name} from #{revision || @revision}"}
45
+
46
+ if revision
47
+ existing_revision = @revision.dup()
48
+ update_revision(revision)
49
+ end
50
+
51
+ deployment_status = create_deployment(:deploy)
52
+
53
+ if revision
54
+ update_revision(existing_revision)
55
+ end
56
+
57
+ deployment_status
58
+ end
59
+
60
+ def rollback
61
+ OpsWorker.logger.info {"Rolling back #{@name}"}
62
+ create_deployment(:rollback)
63
+ end
64
+
65
+ def restart
66
+ OpsWorker.logger.info {"Restarting #{@name}"}
67
+ create_deployment(:restart)
68
+ end
69
+
70
+ def update_cookbooks
71
+ OpsWorker.logger.info {"Updating cookbooks for #{@name}"}
72
+ create_deployment(:update_custom_cookbooks, {}, :all)
73
+ end
74
+
75
+ # @param recipe_names Array of string recipe names, e.g., ["custom::recipe1"]
76
+ def execute_recipes(recipe_names)
77
+ OpsWorker.logger.info {"Executing recipes #{recipe_names} on #{@name}"}
78
+ create_deployment(:execute_recipes, {"recipes" => recipe_names}, :all)
79
+ end
80
+
81
+ def to_s
82
+ "#<OpsWorker::App #{@id}, #{@name}, stack: #{@stack.name}>"
83
+ end
84
+
85
+ def reload
86
+ @instances = nil
87
+ end
88
+
89
+ private
90
+ # @param instance_category Symbol, :all or :app_servers
91
+ def create_deployment(deployment_name, args = {}, instance_category = :app_servers)
92
+ case instance_category
93
+ when :all
94
+ instances = online_instances()
95
+ when :app_servers
96
+ instances = app_server_instances()
97
+ else
98
+ raise ArgumentError.new("Unknown instance category argument: #{instance_category}")
99
+ end
100
+ instance_ids = instances.map(&:id)
101
+ result = @opsworks_client.create_deployment(:stack_id => @stack.id,
102
+ :app_id => @id,
103
+ :command => {:args => args, :name => deployment_name.to_s()},
104
+ :instance_ids => instance_ids
105
+ )
106
+ DeploymentStatus.new(result[:deployment_id], @opsworks_client)
107
+ end
108
+
109
+ def app_server_instances
110
+ online_instances().select do |i|
111
+ i.layers.any? {|l| l.type == "rails-app" || l.type == "custom"}
112
+ end
113
+ end
114
+
115
+ def online_instances
116
+ instances().select {|i| i.online?}
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,40 @@
1
+ require 'aws-sdk'
2
+
3
+ module OpsWorker
4
+ class Client
5
+ def initialize(options = {})
6
+ @opsworks_client = AWS::OpsWorks.new(options).client
7
+ end
8
+
9
+ def find_app_by_name(name)
10
+ stacks().each do |stack|
11
+ app = stack.apps.detect {|a| a.name == name}
12
+
13
+ if app
14
+ return app
15
+ end
16
+ end
17
+
18
+ return nil
19
+ end
20
+
21
+ def stacks
22
+ if @stacks
23
+ return @stacks
24
+ end
25
+
26
+ stacks = @opsworks_client.describe_stacks()[:stacks]
27
+ @stacks = stacks.map do |stack_hash|
28
+ Stack.new(stack_hash[:stack_id], stack_hash[:name], stack_hash[:region], @opsworks_client)
29
+ end
30
+ end
31
+
32
+ def reload
33
+ @stacks = nil
34
+ end
35
+
36
+ def method_missing(method_name, *args, &block)
37
+ @opsworks_client.__send__(method_name, *args, &block)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,17 @@
1
+ module OpsWorker
2
+ class DeploymentStatus
3
+ def initialize(id, opsworks_client)
4
+ @id = id
5
+ @opsworks_client = opsworks_client
6
+ end
7
+
8
+ def status
9
+ commands().map {|c| c[:status] }
10
+ end
11
+
12
+ private
13
+ def commands
14
+ @opsworks_client.describe_commands(:deployment_id => @id)[:commands]
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,28 @@
1
+ module OpsWorker
2
+ class Instance
3
+ attr_reader :id, :hostname, :status, :app, :layers, :elastic_ip, :instance_type, :availability_zone
4
+
5
+ def initialize(id, hostname, status, instance_type, elastic_ip, availability_zone, app, layers)
6
+ @id = id
7
+ @hostname = hostname
8
+ @status = status
9
+ @instance_type = instance_type
10
+ @elastic_ip = elastic_ip
11
+ @availability_zone = availability_zone
12
+ @app = app
13
+ @layers = layers
14
+ end
15
+
16
+ def stopped?
17
+ @status == "stopped"
18
+ end
19
+
20
+ def online?
21
+ @status == "online"
22
+ end
23
+
24
+ def to_s
25
+ "#<OpsWorker::Instance #{@id}, host: #{@hostname}, status: #{@status}, type: #{@instance_type}, layers: #{@layers.map(&:name)}>"
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ module OpsWorker
2
+ class Layer
3
+ attr_reader :id, :name, :type, :stack
4
+
5
+ def initialize(id, name, type, stack, opsworks_client)
6
+ @id = id
7
+ @name = name
8
+ @stack = stack
9
+ @type = type
10
+ @opsworks_client = opsworks_client
11
+ end
12
+
13
+ def to_s
14
+ "#<OpsWorker::Layer #{@id}, #{@name}, stack: #{@stack.name}>"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ require 'logger'
2
+
3
+ module OpsWorker
4
+ def self.logger
5
+ if @logger.nil?
6
+ @logger = Logger.new(STDOUT)
7
+ end
8
+
9
+ @logger
10
+ end
11
+
12
+ def self.logger=(val)
13
+ @logger = val
14
+ end
15
+ end
@@ -0,0 +1,43 @@
1
+ module OpsWorker
2
+ class Stack
3
+ attr_reader :id, :name, :region
4
+
5
+ def initialize(id, name, region, opsworks_client)
6
+ @id = id
7
+ @name = name
8
+ @region = region
9
+ @opsworks_client = opsworks_client
10
+ end
11
+
12
+ def apps
13
+ if @apps
14
+ return @apps
15
+ end
16
+
17
+ apps_result = @opsworks_client.describe_apps(:stack_id => @id)[:apps]
18
+ @apps = apps_result.map do |app_hash|
19
+ App.new(app_hash[:app_id], app_hash[:name], app_hash[:app_source][:revision], self, @opsworks_client)
20
+ end
21
+ end
22
+
23
+ def layers
24
+ if @layers
25
+ return @layers
26
+ end
27
+
28
+ layers_result = @opsworks_client.describe_layers(:stack_id => @id)[:layers]
29
+ @layers = layers_result.map do |layer_hash|
30
+ Layer.new(layer_hash[:layer_id], layer_hash[:name], layer_hash[:type], self, @opsworks_client)
31
+ end
32
+ end
33
+
34
+ def to_s
35
+ "#<OpsWorker::Stack #{@id}, #{@name}, #{@region}>"
36
+ end
37
+
38
+ def reload
39
+ @layers = nil
40
+ @apps = nil
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ module OpsWorker
2
+ VERSION = "0.0.2"
3
+ end
data/lib/ops_worker.rb ADDED
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH << File.dirname(__FILE__)
2
+ require 'ops_worker/app'
3
+ require 'ops_worker/client'
4
+ require 'ops_worker/deployment_status'
5
+ require 'ops_worker/instance'
6
+ require 'ops_worker/layer'
7
+ require 'ops_worker/logger'
8
+ require 'ops_worker/stack'
9
+ require 'ops_worker/version'
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ops_worker
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jon Hyman
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: aws-sdk
16
+ requirement: !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: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: A simple wrapper for common OpsWorks tasks.
47
+ email: jon@appboy.com
48
+ executables: []
49
+ extensions: []
50
+ extra_rdoc_files: []
51
+ files:
52
+ - lib/ops_worker/app.rb
53
+ - lib/ops_worker/client.rb
54
+ - lib/ops_worker/deployment_status.rb
55
+ - lib/ops_worker/instance.rb
56
+ - lib/ops_worker/layer.rb
57
+ - lib/ops_worker/logger.rb
58
+ - lib/ops_worker/stack.rb
59
+ - lib/ops_worker/version.rb
60
+ - lib/ops_worker.rb
61
+ - README.md
62
+ homepage: https://github.com/jonhyman/opsworker
63
+ licenses: []
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubyforge_project:
82
+ rubygems_version: 1.8.23
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: A simple wrapper for common OpsWorks tasks.
86
+ test_files: []
87
+ has_rdoc: