ops_worker 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/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: