ops_deploy 0.1.0

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: 2d6296c4432618e6f1dd7318a2e121580013c594
4
+ data.tar.gz: bf7140d4e1e1f062113a225dbd8253a6c088dab7
5
+ SHA512:
6
+ metadata.gz: f2e4b346c91db5bedf9a2e3a85cab0c36cbf50343486b35e6d2b0319b88140b1bf3905909c61cb11fd0933bccd61d83389a4e28b28c53de5d396c81b82be9b36
7
+ data.tar.gz: 2c312608dfbb5cc0c33bde8acd57103dbdffbcfa5c2aa6687c5c8d51d8731cb51619677ba8b10d1887fc0e32dab82cc58b348a732284a98274e56501c9945f82
data/README.md ADDED
@@ -0,0 +1,73 @@
1
+ ### OpsDeploy
2
+
3
+ A simple gem to perform deployment & checks on AWS OpsWorks. You can deploy, wait for deployments to finish, and check on the instances responses for a successful deployment flow. Also implements Slack notifications for each step.
4
+
5
+ ### Usage
6
+
7
+ ```
8
+ gem install ops_deploy
9
+ ```
10
+
11
+ In your code:
12
+
13
+ ```
14
+ require 'ops_deploy'
15
+ ops = OpsDeploy.new(aws_config)
16
+
17
+ # Start deployment (sync)
18
+ success = ops.start_deployment(stack_id, app_id, migrate)
19
+
20
+ # Wait for deployments (async)
21
+ ops.deployments_callback = Proc.new {
22
+ |aws_deployment_obj|
23
+ # whatever
24
+ }
25
+ a_thread = ops.wait_for_deployments(stack_id)
26
+ a_thread.join
27
+
28
+ # Check instances (async)
29
+ ops.instances_check_callback = Proc.new {
30
+ |aws_instance_obj, http_response, exception|
31
+ # whatever
32
+ }
33
+ a_thread = ops.check_instances(stack_id).join
34
+ a_thread.join
35
+
36
+ ```
37
+
38
+ or using the CLI:
39
+
40
+ ```
41
+ opsdeploy <tasks> --aws-region=<aws_region> --aws-profile=<...> --stack=<stack_name_or_id> --slack-webhook-url=<...> --slack-username=<...> --slack-channel=<...>
42
+ ```
43
+
44
+ The tasks are:
45
+
46
+ - deploy (starts a deployment)
47
+ - migrate (will also migrate during the deployment [for Rails apps])
48
+ - wait (waits for the deployments)
49
+ - check (checks the instances)
50
+
51
+ Example:
52
+
53
+ ```
54
+ opsdeploy deploy wait check --stack="Example" --aws-region="us-east-1"
55
+ ```
56
+
57
+ Output:
58
+
59
+ ```
60
+ -> Getting stack 'Example'...
61
+ -> Found stack 'Example' (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
62
+ -> Starting deployment on stack 'Example'...
63
+ ✓ Deployment started on stack 'Example'
64
+ -> Checking deployments...
65
+ -> Waiting for deployments to finish...
66
+ ..............................................................
67
+ ✓ Deployment OK (58s)
68
+ // Deployments finished
69
+ -> Checking instances' HTTP response...
70
+ .
71
+ ✓ Response from rails-app1: 200 OK
72
+ // Response check finished
73
+ ```
data/bin/opsdeploy ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ops_deploy'
4
+
5
+ cli = OpsDeploy::CLI.new
6
+
7
+ if OpsDeploy::CLI.argument("check-server")
8
+ cli.start_check_server
9
+ else
10
+ stack = OpsDeploy::CLI.argument("stack", "STACK_ID")
11
+ app_id = OpsDeploy::CLI.argument("app-id", "APP_ID")
12
+ migrate = OpsDeploy::CLI.argument("migrate")
13
+ check_via_proxy = OpsDeploy::CLI.argument("check-via-proxy")
14
+
15
+ cli.start_deployment(stack, app_id, migrate) if OpsDeploy::CLI.argument("deploy")
16
+ cli.wait_for_deployments(stack) if OpsDeploy::CLI.argument("wait")
17
+ cli.check_instances(stack, check_via_proxy) if OpsDeploy::CLI.argument("check") or check_via_proxy
18
+ end
data/lib/ops_deploy.rb ADDED
@@ -0,0 +1,167 @@
1
+ require "aws-sdk"
2
+ require "httparty"
3
+ require "pry-byebug"
4
+ require "colorize"
5
+ require "slack-notifier"
6
+ require "pusher"
7
+ require "pusher-client"
8
+
9
+ class OpsDeploy
10
+ attr_accessor :waiter
11
+ attr_accessor :deployments_callback
12
+ attr_accessor :instances_check_callback
13
+
14
+ def initialize(config = nil)
15
+ config = config || {
16
+ region: "us-east-1"
17
+ }
18
+
19
+ @opsworks = Aws::OpsWorks::Client.new(config)
20
+ end
21
+
22
+ def stacks
23
+ @opsworks.describe_stacks.stacks
24
+ end
25
+
26
+ def start_deployment(stack_id_name_or_object, application_id_name_or_object = nil, migrate = false)
27
+ stack = find_stack(stack_id_name_or_object)
28
+ app = find_app(stack, application_id_name_or_object)
29
+
30
+ command = { name: "deploy" }
31
+ command["args"] = { migrate: ["true"] } if migrate
32
+
33
+ resp = @opsworks.create_deployment({
34
+ stack_id: stack.stack_id,
35
+ app_id: app.app_id,
36
+ command: command
37
+ })
38
+
39
+ return (resp && resp.deployment_id)
40
+ end
41
+
42
+ def wait_for_deployments(stack_id_name_or_object)
43
+ stack = find_stack(stack_id_name_or_object)
44
+
45
+ running_deployments = @opsworks.describe_deployments(stack_id: stack.stack_id).deployments.select {
46
+ |deployment|
47
+
48
+ deployment.status == "running"
49
+ }
50
+
51
+ if running_deployments.empty?
52
+ false
53
+ else
54
+ waiters = []
55
+
56
+ running_deployments.each {
57
+ |deployment|
58
+
59
+ waiters << OpsDeploy::DeploymentWaiter.new(@opsworks, deployment, @deployments_callback)
60
+ }
61
+
62
+ @waiter = Thread.new(waiters) {
63
+ |deploy_threads|
64
+
65
+ deploy_threads.each(&:run)
66
+ deploy_threads.each(&:join)
67
+ }
68
+
69
+ @waiter.run
70
+ end
71
+ end
72
+
73
+ def check_instances(stack_id_name_or_object)
74
+ stack = find_stack(stack_id_name_or_object)
75
+
76
+ running_instances = @opsworks.describe_instances(stack_id: stack.stack_id).instances.select {
77
+ |instance|
78
+
79
+ instance.status == "online"
80
+ }
81
+
82
+ waiters = []
83
+ running_instances.each {
84
+ |instance|
85
+
86
+ waiters << OpsDeploy::InstanceResponseWaiter.new(@opsworks, instance, @instances_check_callback)
87
+ }
88
+
89
+ @waiter = Thread.new(waiters) {
90
+ |check_threads|
91
+
92
+ check_threads.each(&:run)
93
+ check_threads.each(&:join)
94
+ }
95
+
96
+ @waiter.run
97
+ end
98
+
99
+ def find_stack(stack_id_name_or_object)
100
+ found_stack = nil
101
+
102
+ if stack_id_name_or_object.kind_of?(String)
103
+ begin
104
+ if stack_id_name_or_object.match(/^[0-9a-f\-]+$/)
105
+ found_stack = @opsworks.describe_stacks(stack_ids: [stack_id_name_or_object]).stacks.first
106
+ end
107
+ rescue Aws::OpsWorks::Errors::ResourceNotFoundException
108
+ end
109
+
110
+ if found_stack.nil?
111
+ @opsworks.describe_stacks.stacks.each {
112
+ |stack|
113
+
114
+ if stack.name == stack_id_name_or_object
115
+ found_stack = stack
116
+ break
117
+ end
118
+ }
119
+ end
120
+ end
121
+
122
+ found_stack = stack_id_name_or_object if found_stack.nil?
123
+ invalid_stack_error = StandardError.new("Invalid stack #{found_stack} (#{stack_id_name_or_object}).")
124
+ raise invalid_stack_error unless found_stack.kind_of?(Aws::OpsWorks::Types::Stack)
125
+
126
+ found_stack
127
+ end
128
+
129
+ def find_app(stack, application_id_name_or_object)
130
+ found_app = nil
131
+
132
+ if application_id_name_or_object.kind_of?(String)
133
+ begin
134
+ if application_id_name_or_object.match(/^[0-9a-f\-]+$/)
135
+ found_app = @opsworks.describe_apps({
136
+ stack_id: stack.stack_id,
137
+ app_ids: [application_id_name_or_object]
138
+ }).apps.first
139
+ end
140
+ rescue Aws::OpsWorks::Errors::ResourceNotFoundException
141
+ end
142
+
143
+ if found_app.nil?
144
+ @opsworks.describe_apps(stack_id: stack.stack_id).apps.each {
145
+ |app|
146
+
147
+ if app.name == application_id_name_or_object
148
+ found_app = app
149
+ break
150
+ end
151
+ }
152
+ end
153
+ elsif application_id_name_or_object.nil?
154
+ apps = @opsworks.describe_apps(stack_id: stack.stack_id).apps
155
+ found_app = apps.first if apps.count == 1
156
+ end
157
+
158
+ found_app = application_id_name_or_object if found_app.nil?
159
+ invalid_app_error = StandardError.new("Invalid app #{found_app} (#{application_id_name_or_object}).")
160
+ raise invalid_app_error unless found_app.kind_of?(Aws::OpsWorks::Types::App)
161
+
162
+ found_app
163
+ end
164
+ end
165
+
166
+ require_relative "ops_deploy/waiter"
167
+ require_relative "ops_deploy/cli"
@@ -0,0 +1,220 @@
1
+ class OpsDeploy::CLI
2
+ def initialize
3
+ config = {
4
+ region: OpsDeploy::CLI.argument("aws-region", "AWS_REGION", true),
5
+ }
6
+
7
+ profile = OpsDeploy::CLI.argument("aws-profile", "AWS_PROFILE")
8
+ config[:credentials] = Aws::SharedCredentials.new(profile_name: profile) if profile
9
+
10
+ @main = OpsDeploy.new(config)
11
+ @stacks = {}
12
+ @notifier = OpsDeploy::CLI::Notifier.new({
13
+ slack: {
14
+ webhook_url: OpsDeploy::CLI.argument("slack-webhook-url", "SLACK_WEBHOOK_URL"),
15
+ username: OpsDeploy::CLI.argument("slack-username", "SLACK_USERNAME"),
16
+ channel: OpsDeploy::CLI.argument("slack-channel", "SLACK_CHANNEL")
17
+ }
18
+ })
19
+
20
+ @notification_messages = {
21
+ info: [],
22
+ success: [],
23
+ failure: []
24
+ }
25
+ @notification_success = false
26
+ @notification_failure = false
27
+ end
28
+
29
+ def start_deployment(stack_id_name_or_object, application_id = nil, migrate = false)
30
+ stack = find_stack(stack_id_name_or_object)
31
+
32
+ step_msg("Starting deployment on stack", "'#{stack.name.blue}'...")
33
+
34
+ if @main.start_deployment(stack, application_id, migrate)
35
+ success_msg("Deployment started on stack", "'#{stack.name.green}'")
36
+ send_notification(stack)
37
+
38
+ true
39
+ else
40
+ failure_msg("Couldn't start deployment on stack", "'#{stack.name.red}'")
41
+ send_notification(stack)
42
+
43
+ false
44
+ end
45
+ end
46
+
47
+ def wait_for_deployments(stack_id_name_or_object)
48
+ stack = find_stack(stack_id_name_or_object)
49
+
50
+ step_msg("Checking deployments...")
51
+ @main.deployments_callback = Proc.new {
52
+ |deployment|
53
+
54
+ puts
55
+
56
+ if (deployment.status == "successful")
57
+ success_msg("Deployment", "OK".green.bold, deployment.duration ? "(#{deployment.duration.to_s}s)" : "")
58
+ else
59
+ failure_msg("Deployment", "Failed".red.bold, deployment.duration ? "(#{deployment.duration.to_s}s)" : "")
60
+ end
61
+ }
62
+
63
+ waiter = @main.wait_for_deployments(stack)
64
+ if waiter
65
+ step_msg("Waiting for deployments to finish...")
66
+
67
+ print ".".blue
68
+ print ".".blue until waiter.join(1)
69
+
70
+ info_msg("Deployments finished")
71
+ else
72
+ info_msg("No running deployments on stack", "'#{stack_id_name_or_object.blue}'")
73
+ end
74
+
75
+ send_notification(stack)
76
+ end
77
+
78
+ def check_instances(stack_id_name_or_object, via_proxy = false)
79
+ stack = find_stack(stack_id_name_or_object)
80
+
81
+ if via_proxy
82
+ Pusher.url = OpsDeploy::CLI.argument("pusher-url", "PUSHER_URL", true)
83
+ Pusher['OpsDeploy'].trigger('check_instances', {
84
+ stack: stack.stack_id
85
+ })
86
+ else
87
+ @main.instances_check_callback = Proc.new {
88
+ |instance, response, error|
89
+
90
+ puts
91
+
92
+ if (error.nil? and response.code == 200)
93
+ success_msg("Response from", "#{instance.hostname.green}:", "200 OK".green)
94
+ elsif error.nil?
95
+ failure_msg("Response from", "#{instance.hostname.red}:", "#{response.code}".red)
96
+ else
97
+ failure_msg("Error checking", "#{instance.hostname.red}:", error.to_s)
98
+ end
99
+ }
100
+
101
+ waiter = @main.check_instances(stack)
102
+ if waiter
103
+ step_msg("Checking instances' HTTP response...")
104
+
105
+ print ".".blue
106
+ print ".".blue until waiter.join(1)
107
+
108
+ info_msg("Response check finished")
109
+ else
110
+ info_msg("No online instances on stack", "'#{stack_id_name_or_object.blue}'")
111
+ end
112
+
113
+ send_notification(stack)
114
+ end
115
+ end
116
+
117
+ def start_check_server
118
+ pusher_comp = URI.parse(OpsDeploy::CLI.argument("pusher-url", "PUSHER_URL", true))
119
+ PusherClient.logger.level = Logger::ERROR
120
+ socket = PusherClient::Socket.new(pusher_comp.user, secure: true)
121
+ socket.subscribe("OpsDeploy")
122
+ socket["OpsDeploy"].bind("check_instances") do |data|
123
+ begin
124
+ info = data
125
+ info = JSON.parse(data) if data.kind_of?(String)
126
+ stack_id = info["stack"] || info[:stack]
127
+
128
+ check_instances(stack_id)
129
+ rescue StandardError => e
130
+ puts e
131
+ end
132
+ end
133
+
134
+ info_msg("Started OpsDeploy server")
135
+ socket.connect
136
+ end
137
+
138
+ def self.argument(argv_name, env_name = nil, required = false)
139
+ value = nil
140
+ value = ENV[env_name] if env_name
141
+
142
+ if value.nil?
143
+ value = ARGV.include?(argv_name) ? true : nil
144
+ return value if value
145
+
146
+ ARGV.each {
147
+ |arg|
148
+ if arg.start_with?("--#{argv_name}=")
149
+ value = arg.split("--#{argv_name}=").last
150
+ end
151
+ }
152
+ end
153
+
154
+ if required && value.nil?
155
+ raise StandardError.new("Argument '#{argv_name}' unspecified. Please set #{env_name ? 'the environment variable '+env_name+' or ' : ''}the argument --#{argv_name}=<value>")
156
+ end
157
+
158
+ value
159
+ end
160
+
161
+ private
162
+
163
+ def find_stack(stack_id_name_or_object)
164
+ if stack_id_name_or_object.kind_of?(String)
165
+ hash = stack_id_name_or_object.hash
166
+
167
+ if @stacks[hash].nil?
168
+ step_msg("Getting stack", "'#{stack_id_name_or_object.blue}'...")
169
+ stack = @main.find_stack(stack_id_name_or_object)
170
+ step_msg("Found stack", "'#{stack.name.blue}' (#{stack.stack_id})")
171
+
172
+ @notifier.messages.info.pop
173
+ @notifier.messages.info.pop
174
+
175
+ @stacks[hash] = stack
176
+ end
177
+
178
+ @stacks[hash]
179
+ else
180
+ @main.find_stack(stack_id_name_or_object)
181
+ end
182
+ end
183
+
184
+ def success_msg(*args)
185
+ message = "✓ ".green+args.join(" ")
186
+ @notifier.messages.success << message
187
+ @notifier.notification_type = :success unless @notifier.notification_type == :failure
188
+ puts message
189
+ message
190
+ end
191
+
192
+ def info_msg(*args)
193
+ message = "// ".blue+args.join(" ")
194
+ @notifier.messages.info << message
195
+ puts message
196
+ message
197
+ end
198
+
199
+ def failure_msg(*args)
200
+ message = "╳ ".red+args.join(" ")
201
+ @notifier.messages.failure << message
202
+ @notifier.notification_type = :failure
203
+ puts message
204
+ message
205
+ end
206
+
207
+ def step_msg(*args)
208
+ message = "-> ".cyan+args.join(" ")
209
+ @notifier.messages.info << message
210
+ puts message
211
+ message
212
+ end
213
+
214
+ def send_notification(stack)
215
+ @notifier.notify(stack)
216
+ @notifier.reset
217
+ end
218
+ end
219
+
220
+ require_relative "cli/notifier"
@@ -0,0 +1,101 @@
1
+ class OpsDeploy::CLI::Notifier
2
+ attr_accessor :options
3
+ attr_accessor :messages
4
+ attr_accessor :notification_type
5
+
6
+ def initialize(options)
7
+ @options = options
8
+ @options.delete(:slack) if @options[:slack].nil? or @options[:slack][:webhook_url].nil?
9
+ @messages = OpsDeploy::CLI::Notifier::Messages.new
10
+ @notification_type = :info
11
+ end
12
+
13
+ def notify(stack)
14
+ if @notification_type == :failure
15
+ message = @messages.failure.join("\n")
16
+ failure_notify(stack, message)
17
+ elsif @notification_type == :success
18
+ message = @messages.success.join("\n")
19
+ success_notify(stack, message)
20
+ else
21
+ message = @messages.info.join("\n")
22
+ info_notify(stack, message)
23
+ end
24
+ end
25
+
26
+ def info_notify(stack, message)
27
+ OpsDeploy::CLI::Notifier::Slack.new(stack, @options[:slack]).notify(message) if @options[:slack]
28
+ end
29
+
30
+ def success_notify(stack, message)
31
+ OpsDeploy::CLI::Notifier::Slack.new(stack, @options[:slack]).success_notify(message) if @options[:slack]
32
+ end
33
+
34
+ def failure_notify(stack, message)
35
+ OpsDeploy::CLI::Notifier::Slack.new(stack, @options[:slack]).failure_notify(message) if @options[:slack]
36
+ end
37
+
38
+ def reset
39
+ @messages = OpsDeploy::CLI::Notifier::Messages.new
40
+ @notification_type = :info
41
+ end
42
+ end
43
+
44
+ class OpsDeploy::CLI::Notifier::Messages
45
+ attr_accessor :info, :success, :failure
46
+
47
+ def initialize
48
+ @info = []
49
+ @success = []
50
+ @failure = []
51
+ end
52
+ end
53
+
54
+ class OpsDeploy::CLI::Notifier::Generic
55
+ end
56
+
57
+ class OpsDeploy::CLI::Notifier::Slack < OpsDeploy::CLI::Notifier::Generic
58
+ attr_accessor :slack_notifier
59
+
60
+ def initialize(stack, options)
61
+ @stack = stack
62
+ @options = options
63
+ @options[:username] = "OpsDeploy" unless @options[:username]
64
+ @slack_notifier = Slack::Notifier.new @options[:webhook_url], @options
65
+ end
66
+
67
+ def notify(message)
68
+ message = message.gsub(/\[[0-9;]+?m/, "")
69
+ @slack_notifier.ping "", channel: @options[:channel], attachments: [
70
+ {
71
+ fallback: message,
72
+ author_name: @stack.name,
73
+ text: message
74
+ }
75
+ ]
76
+ end
77
+
78
+ def success_notify(message)
79
+ message = message.gsub(/\[[0-9;]+?m/, "")
80
+ @slack_notifier.ping "", channel: @options[:channel], attachments: [
81
+ {
82
+ fallback: message,
83
+ text: message,
84
+ author_name: @stack.name,
85
+ color: "good"
86
+ }
87
+ ]
88
+ end
89
+
90
+ def failure_notify(message)
91
+ message = message.gsub(/\[[0-9;]+?m/, "")
92
+ @slack_notifier.ping "", channel: @options[:channel], attachments: [
93
+ {
94
+ fallback: message,
95
+ text: message,
96
+ author_name: @stack.name,
97
+ color: "danger"
98
+ }
99
+ ]
100
+ end
101
+ end
@@ -0,0 +1,3 @@
1
+ module OpsDeploy
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,66 @@
1
+ class OpsDeploy::Waiter < Thread
2
+ attr_accessor :end_when, :callback, :data
3
+
4
+ def initialize(&task)
5
+ @task = task
6
+
7
+ super() {
8
+ error = nil
9
+
10
+ begin
11
+ until false
12
+ @data = @task.call
13
+ break if @end_when.call(@data)
14
+ sleep 5
15
+ end
16
+ rescue StandardError => e
17
+ error = e
18
+ end
19
+
20
+ @callback.call(@data, error) if @callback
21
+
22
+ @data
23
+ }
24
+ end
25
+ end
26
+
27
+ class OpsDeploy::DeploymentWaiter < OpsDeploy::Waiter
28
+ def initialize(opsworks, deployment, callback = nil)
29
+ super() {
30
+ deploy = opsworks.describe_deployments(deployment_ids: [deployment.deployment_id]).deployments.first
31
+
32
+ # Retry if there's no duration
33
+ if deploy.status != "running" and deploy.duration.nil?
34
+ deploy = opsworks.describe_deployments(deployment_ids: [deployment.deployment_id]).deployments.first
35
+ end
36
+
37
+ deploy
38
+ }
39
+
40
+ @end_when = Proc.new {
41
+ |deployment_obj|
42
+ deployment_obj.status != "running"
43
+ }
44
+
45
+ @callback = callback
46
+ end
47
+ end
48
+
49
+ class OpsDeploy::InstanceResponseWaiter < OpsDeploy::Waiter
50
+ class HTTParty::Basement
51
+ default_timeout 30
52
+ end
53
+
54
+ def initialize(opsworks, instance, callback = nil)
55
+ super() {
56
+ instance_ip = instance.public_ip || instance.private_ip
57
+ HTTParty.get("http://#{instance_ip}", verify: false)
58
+ }
59
+
60
+ @end_when = Proc.new { true }
61
+ @callback = Proc.new {
62
+ |data, error|
63
+ callback.call(instance, data, error)
64
+ }
65
+ end
66
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ops_deploy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Mahdi Bchetnia
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-14 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Perform deployment & checks on AWS OpsWorks. You can deploy, wait for
14
+ deployments to finish, and check on the instances responses for a successful deployment
15
+ flow. Also implements Slack notifications for each step.
16
+ email:
17
+ - injekter@gmail.com
18
+ executables:
19
+ - opsdeploy
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - README.md
24
+ - bin/opsdeploy
25
+ - lib/ops_deploy.rb
26
+ - lib/ops_deploy/cli.rb
27
+ - lib/ops_deploy/cli/notifier.rb
28
+ - lib/ops_deploy/version.rb
29
+ - lib/ops_deploy/waiter.rb
30
+ homepage: http://github.com/inket/OpsDeploy
31
+ licenses:
32
+ - MIT
33
+ metadata: {}
34
+ post_install_message:
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 1.3.6
48
+ requirements: []
49
+ rubyforge_project: ops_deploy
50
+ rubygems_version: 2.4.6
51
+ signing_key:
52
+ specification_version: 4
53
+ summary: Perform deployment & checks on AWS OpsWorks
54
+ test_files: []