dream-ops 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8b8aa9f131724802c240a24e1d23dbf37eb8c70e
4
- data.tar.gz: 513e26443ce943be5ec10c4251a90794364ce536
3
+ metadata.gz: fe5971e39855c322c3680d7495452950f6e129ac
4
+ data.tar.gz: 6d7b6cd6b14ae02429694879aa34d7656ecd2228
5
5
  SHA512:
6
- metadata.gz: d95f1f526da0bcc544dfcb06af87576571b46cf4ad75e14d1b68b36085084121ec5f14a1c561a9da5197fad733a9c2ad934d0ea5a6e3f2d73e4ad98c928f0341
7
- data.tar.gz: 42e9083c9b3d6038548bd5b4897a91c861eb673a84bb2e46d0f675eef5166e74802bc17d7a0fe674082b5e067a7fc08509beed271cb178e59ad70b9644a5465d
6
+ metadata.gz: 04e7efd3f5108eb2f547265bee1b326a1523d4e19b8cd49eea62b0d45c7331509ec380d5a7d4e996312bf6d90fcd3665226d4fa220fb8f81644e93d24be31ac5
7
+ data.tar.gz: 642468eaabb2ec607aa1c3415dc21052884927d6e8835bca5849a9d08eee1edf966cc66db02993962a34438b73f0649f776ca786c077f23e89ee43b03cda8d22
data/README.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # DreamOps
2
+ [![Gem Version](https://img.shields.io/gem/v/dream-ops.svg)][gem]
2
3
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/dream-ops`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+ [gem]: https://rubygems.org/gems/dream-ops
4
5
 
5
- TODO: Delete this and the text above, and describe your gem
6
+ CLI for Dream projects based on the [berkshelf](https://github.com/berkshelf/berkshelf) project.
6
7
 
7
8
  ## Installation
8
9
 
@@ -22,7 +23,23 @@ Or install it yourself as:
22
23
 
23
24
  ## Usage
24
25
 
25
- TODO: Write usage instructions here
26
+ ```bash
27
+ dream help
28
+ ```
29
+
30
+ ### Deploying to OpsWorks
31
+
32
+ ```bash
33
+ dream deploy opsworks --stacks 08137c03-1e85-4787-b82c-cb825638cdfa
34
+ Stack: nodeapp
35
+ --- Cookbook: chef-nodeapp
36
+ --- Apps: ["nodeapp"]
37
+ ...Building cookbook [chef-nodeapp]
38
+ ...Deploying cookbook [chef-nodeapp]
39
+ ...Updating custom cookbooks [stack="nodeapp"]
40
+ ...Running setup command [stack="nodeapp"]
41
+ ...Deploying [stack="nodeapp"] [app="nodeapp"]
42
+ ```
26
43
 
27
44
  ## Development
28
45
 
@@ -32,8 +49,4 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
32
49
 
33
50
  ## Contributing
34
51
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/dream-ops.
36
-
37
- ## License
38
-
39
- The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
52
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/dream-ops.
@@ -11,24 +11,28 @@ end
11
11
  require "berkshelf"
12
12
  require "thor"
13
13
 
14
- Berkshelf.ui.mute!
15
-
16
14
  module DreamOps
17
15
 
18
16
  require_relative "dream-ops/version"
19
17
  require_relative "dream-ops/errors"
20
18
 
21
- autoload :Shell, "dream-ops/shell"
19
+ module Mixin
20
+ autoload :Logging, "dream-ops/mixin/logging"
21
+ end
22
+
23
+ autoload :Shell, "dream-ops/shell"
22
24
 
23
- autoload :BaseFormatter, "dream-ops/formatters/base"
24
- autoload :HumanFormatter, "dream-ops/formatters/human"
25
- # autoload :JsonFormatter, "dream-ops/formatters/json"
26
- # autoload :NullFormatter, "dream-ops/formatters/null"
25
+ autoload :BaseFormatter, "dream-ops/formatters/base"
26
+ autoload :HumanFormatter, "dream-ops/formatters/human"
27
+ # autoload :JsonFormatter, "dream-ops/formatters/json"
28
+ # autoload :NullFormatter, "dream-ops/formatters/null"
27
29
 
28
- autoload :BaseDeployer, "dream-ops/deployment/base"
30
+ autoload :BaseDeployer, "dream-ops/deployment/base"
29
31
  autoload :OpsWorksDeployer, "dream-ops/deployment/opsworks"
30
32
 
31
33
  class << self
34
+ include Mixin::Logging
35
+
32
36
  # @return [DreamOps::Shell]
33
37
  def ui
34
38
  @ui ||= DreamOps::Shell.new
@@ -57,4 +61,8 @@ module DreamOps
57
61
  end
58
62
  end
59
63
 
60
- require_relative "dream-ops/cli"
64
+ require_relative "dream-ops/cli"
65
+ require_relative "dream-ops/logger"
66
+
67
+ Ridley.logger = DreamOps.logger
68
+ DreamOps.logger.level = Logger::WARN
@@ -27,12 +27,8 @@ module DreamOps
27
27
  @kernel.exit(0)
28
28
  rescue DreamOps::DreamOpsError => e
29
29
  DreamOps.ui.error e
30
- DreamOps.ui.error "\t" + e.backtrace.join("\n\t") if ENV["BERKSHELF_DEBUG"]
30
+ DreamOps.ui.error "\t" + e.backtrace.join("\n\t") if ENV["DREAMOPS_DEBUG"]
31
31
  @kernel.exit(e.status_code)
32
- # rescue Ridley::Errors::RidleyError => e
33
- # DreamOps.ui.error "#{e.class} #{e}"
34
- # DreamOps.ui.error "\t" + e.backtrace.join("\n\t") if ENV["BERKSHELF_DEBUG"]
35
- # @kernel.exit(47)
36
32
  end
37
33
  end
38
34
 
@@ -56,17 +52,11 @@ module DreamOps
56
52
  def initialize(*args)
57
53
  super(*args)
58
54
 
59
- # if @options[:config]
60
- # unless File.exist?(@options[:config])
61
- # raise ConfigNotFound.new(:berkshelf, @options[:config])
62
- # end
63
-
64
- # DreamOps.config = DreamOps::Config.from_file(@options[:config])
65
- # end
66
-
67
55
  if @options[:debug]
68
- ENV["BERKSHELF_DEBUG"] = "true"
56
+ ENV["DREAMOPS_DEBUG"] = "true"
69
57
  DreamOps.logger.level = ::Logger::DEBUG
58
+ else
59
+ Berkshelf.ui.mute!
70
60
  end
71
61
 
72
62
  if @options[:quiet]
@@ -77,19 +67,13 @@ module DreamOps
77
67
  @options = options.dup # unfreeze frozen options Hash from Thor
78
68
  end
79
69
 
80
- namespace "berkshelf"
70
+ namespace "dream-ops"
81
71
 
82
- map "ls" => :list
83
- map "book" => :cookbook
72
+ # map "ls" => :list
84
73
  map ["ver", "-v", "--version"] => :version
85
74
 
86
- default_task :install
75
+ default_task :version
87
76
 
88
- class_option :config,
89
- type: :string,
90
- desc: "Path to DreamOps configuration to use.",
91
- aliases: "-c",
92
- banner: "PATH"
93
77
  class_option :format,
94
78
  type: :string,
95
79
  default: "human",
@@ -115,8 +99,9 @@ module DreamOps
115
99
  method_option :stacks,
116
100
  type: :array,
117
101
  desc: "Only these stack IDs.",
118
- aliases: "-s"
119
- desc "deploy", "Creates deploy"
102
+ aliases: "-s",
103
+ required: true
104
+ desc "deploy [TYPE]", "Deploys to specified targets"
120
105
  def deploy(type)
121
106
  deployer = nil
122
107
 
@@ -125,26 +110,14 @@ module DreamOps
125
110
  deployer = OpsWorksDeployer.new
126
111
  stack_ids = options[:stacks]
127
112
  args = [*stack_ids]
113
+ else
114
+ DreamOps.ui.error "Deployment of type '#{type}' is not supported"
115
+ exit(1)
128
116
  end
129
117
 
130
118
  if !deployer.nil?
131
119
  deployer.deploy(*args)
132
120
  end
133
121
  end
134
-
135
- # tasks["cookbook"].options = DreamOps::CookbookGenerator.class_options
136
-
137
- private
138
-
139
- # Print a list of the given cookbooks. This is used by various
140
- # methods like {list} and {contingent}.
141
- #
142
- # @param [Array<CachedCookbook>] cookbooks
143
- #
144
- def print_list(cookbooks)
145
- Array(cookbooks).sort.each do |cookbook|
146
- DreamOps.formatter.msg " * #{cookbook.cookbook_name} (#{cookbook.version})"
147
- end
148
- end
149
122
  end
150
- end
123
+ end
@@ -20,13 +20,47 @@ module DreamOps
20
20
  end
21
21
  end
22
22
 
23
- # These MUST be implemented by subclasses
23
+ # This method MUST be implemented by subclasses.
24
+ #
25
+ # @return [Hash]
26
+ # a hash containing cookbooks that need to be built/updated
27
+ # and deploy targets
28
+ #
29
+ # Example:
30
+ # {
31
+ # :cookbooks => [
32
+ # {
33
+ # :bucket => "chef-app",
34
+ # :cookbook_key => "chef-app-dev.zip",
35
+ # :sha_key => "chef-app-dev_SHA.txt",
36
+ # :name => "chef-app",
37
+ # :path => "./chef",
38
+ # :local_sha => "7bfa19491170563f422a321c144800f4435323b1",
39
+ # :remote_sha => ""
40
+ # }
41
+ # ],
42
+ # deploy_targets: [
43
+ # #<Hash>,
44
+ # #<Hash>
45
+ # ]
46
+ # }
24
47
  deployer_method :analyze
48
+
49
+ # This method MUST be implemented by subclasses.
50
+ #
51
+ # @param [Hash] cookbook
52
+ # a hash containing the cookbook details
25
53
  deployer_method :deploy_cookbook
54
+
55
+
56
+ # This method MUST be implemented by subclasses.
57
+ #
58
+ # @param [Hash] target
59
+ # a hash containing the deploy target details
26
60
  deployer_method :deploy_target
27
61
 
28
62
  # It may turn out that cookbook building will be different for different
29
- # deployments, but for now all deployments build them the same way.
63
+ # deployments, but for now all deployments will build them the same way.
30
64
  def build_cookbook(cookbook)
31
65
  berksfile = Berkshelf::Berksfile.from_file(File.join(cookbook[:path], "Berksfile"))
32
66
  berksfile.vendor("berks-cookbooks")
@@ -69,11 +103,13 @@ module DreamOps
69
103
  deploy_threads.each do |t|
70
104
  begin
71
105
  t.join
72
- rescue DreamOps::FatalDeployError
106
+ rescue DreamOps::DreamOpsError
107
+ DreamOps.ui.error "#{$!}"
73
108
  deploy_success = deploy_success && false
74
109
  end
75
110
  end
76
111
 
112
+ # If ANY deploy threads failed, exit with failure
77
113
  exit(1) if !deploy_success
78
114
  end
79
115
  end
@@ -6,13 +6,48 @@ Aws.use_bundled_cert!
6
6
  module DreamOps
7
7
  class OpsWorksDeployer < BaseDeployer
8
8
 
9
- # Output the version of DreamOps
9
+ # Analyze the OpsWorks stacks for deployment
10
+ #
11
+ # @return [Hash]
12
+ # a hash containing cookbooks that need to be built/updated
13
+ # and deploy targets
14
+ #
15
+ # Example:
16
+ # {
17
+ # :cookbooks => [
18
+ # {
19
+ # :bucket => "chef-app",
20
+ # :cookbook_key => "chef-app-dev.zip",
21
+ # :sha_key => "chef-app-dev_SHA.txt",
22
+ # :name => "chef-app",
23
+ # :path => "./chef",
24
+ # :local_sha => "7bfa19491170563f422a321c144800f4435323b1",
25
+ # :remote_sha => ""
26
+ # }
27
+ # ],
28
+ # deploy_targets: [
29
+ # {
30
+ # :stack => #<Stack: name="my-stack">,
31
+ # :apps => [
32
+ # #<App: name="my-app"
33
+ # ],
34
+ # :cookbook => {
35
+ # :bucket => "chef-app",
36
+ # :cookbook_key => "chef-app-dev.zip",
37
+ # :sha_key => "chef-app-dev_SHA.txt",
38
+ # :name => "chef-app",
39
+ # :path => "./chef",
40
+ # :local_sha => "7bfa19491170563f422a321c144800f4435323b1",
41
+ # :remote_sha => ""
42
+ # }
43
+ # }
44
+ # ]
45
+ # }
10
46
  def analyze(stack_ids)
11
47
  begin
12
48
  @opsworks = Aws::OpsWorks::Client.new
13
49
  stacks = @opsworks.describe_stacks({ stack_ids: stack_ids, }).stacks
14
50
  rescue => e
15
- DreamOps.ui.error "Failed to fetch OpsWorks stacks\n"
16
51
  DreamOps.ui.error "#{$!}"
17
52
  exit(1)
18
53
  end
@@ -42,8 +77,6 @@ module DreamOps
42
77
  return result
43
78
  end
44
79
 
45
- ################## OpsWorks specific methods ##################
46
-
47
80
  # Retrieves stack apps and gets all information about remote/local cookbook
48
81
  def analyze_stack(stack)
49
82
  cookbook = nil
@@ -118,13 +151,8 @@ module DreamOps
118
151
  # If this stack has a new cookbook
119
152
  if !target[:cookbook].nil?
120
153
  if __cookbook_in_array(target[:cookbook], cookbooks)
121
- begin
122
- # Grab a fresh copy of the cookbook on all instances in the stack
123
- update_custom_cookbooks(target[:stack])
124
- rescue Aws::OpsWorks::Errors::ValidationException
125
- DreamOps.ui.error "Stack \"#{target[:stack].name}\" has no running instances."
126
- __bail_with_fatal_error
127
- end
154
+ # Grab a fresh copy of the cookbook on all instances in the stack
155
+ update_custom_cookbooks(target[:stack])
128
156
 
129
157
  # Re-run the setup step for all layers
130
158
  setup(target[:stack])
@@ -133,22 +161,27 @@ module DreamOps
133
161
 
134
162
  # Deploy all apps for stack
135
163
  target[:apps].each do |app|
136
- begin
137
- deploy_app(app, target[:stack])
138
- rescue Aws::OpsWorks::Errors::ValidationException
139
- DreamOps.ui.error "Stack \"#{target[:stack].name}\" has no running instances."
140
- __bail_with_fatal_error
141
- end
164
+ deploy_app(app, target[:stack])
142
165
  end
143
166
  end
144
167
 
145
168
  def update_custom_cookbooks(stack)
146
169
  DreamOps.ui.info "...Updating custom cookbooks [stack=\"#{stack.name}\"]"
147
- response = @opsworks.create_deployment({
148
- stack_id: stack.stack_id,
149
- command: { name: "update_custom_cookbooks" }
150
- })
151
- return wait_for_deployment(response.deployment_id)
170
+ begin
171
+ response = @opsworks.create_deployment({
172
+ stack_id: stack.stack_id,
173
+ command: { name: "update_custom_cookbooks" }
174
+ })
175
+ rescue Aws::OpsWorks::Errors::ValidationException
176
+ bail_with_fatal_error(NoRunningInstancesError.new(stack))
177
+ end
178
+
179
+ status = wait_for_deployment(response.deployment_id)
180
+ if status != 'successful'
181
+ bail_with_fatal_error(OpsWorksCommandFailedError.new(
182
+ stack, response.deployment_id, 'update_custom_cookbooks')
183
+ )
184
+ end
152
185
  end
153
186
 
154
187
  def setup(stack)
@@ -157,17 +190,33 @@ module DreamOps
157
190
  stack_id: stack.stack_id,
158
191
  command: { name: "setup" }
159
192
  })
160
- return wait_for_deployment(response.deployment_id)
193
+
194
+ status = wait_for_deployment(response.deployment_id)
195
+ if status != 'successful'
196
+ bail_with_fatal_error(OpsWorksCommandFailedError.new(
197
+ stack, response.deployment_id, 'setup')
198
+ )
199
+ end
161
200
  end
162
201
 
163
202
  def deploy_app(app, stack)
164
203
  DreamOps.ui.info "...Deploying [stack=\"#{stack.name}\"] [app=\"#{app.name}\"]"
165
- response = @opsworks.create_deployment({
166
- stack_id: stack.stack_id,
167
- app_id: app.app_id,
168
- command: { name: "deploy" }
169
- })
170
- return wait_for_deployment(response.deployment_id)
204
+ begin
205
+ response = @opsworks.create_deployment({
206
+ stack_id: stack.stack_id,
207
+ app_id: app.app_id,
208
+ command: { name: "deploy" }
209
+ })
210
+ rescue Aws::OpsWorks::Errors::ValidationException
211
+ bail_with_fatal_error(NoRunningInstancesError.new(stack))
212
+ end
213
+
214
+ status = wait_for_deployment(response.deployment_id)
215
+ if status != 'successful'
216
+ bail_with_fatal_error(OpsWorksCommandFailedError.new(
217
+ stack, response.deployment_id, 'deploy')
218
+ )
219
+ end
171
220
  end
172
221
 
173
222
  def get_deployment_status(deployment_id)
@@ -188,8 +237,8 @@ module DreamOps
188
237
  return status
189
238
  end
190
239
 
191
- def __bail_with_fatal_error
192
- raise FatalDeployError
240
+ def bail_with_fatal_error(ex)
241
+ raise ex
193
242
  Thread.exit
194
243
  end
195
244
 
@@ -11,5 +11,35 @@ module DreamOps
11
11
  alias_method :message, :to_s
12
12
  end
13
13
 
14
- class FatalDeployError < DreamOpsError; set_status_code(10); end
14
+ class NoRunningInstancesError < DreamOpsError
15
+ set_status_code(10)
16
+
17
+ def initialize(stack)
18
+ @stack = stack
19
+ end
20
+
21
+ def to_s
22
+ "Stack \"#{@stack.name}\" has no running instances."
23
+ end
24
+ end
25
+
26
+ class OpsWorksCommandFailedError < DreamOpsError
27
+ set_status_code(11)
28
+
29
+ def initialize(stack, deployment_id, command)
30
+ @stack = stack
31
+ @deployment_id = deployment_id
32
+ @command = command
33
+ end
34
+
35
+ def to_s
36
+ [
37
+ "Stack \"#{@stack.name}\" failed running command '#{@command}'. To view the failure log, visit:",
38
+ "",
39
+ "https://console.aws.amazon.com/opsworks/home?region=#{@stack.region}#/stack/#{@stack.stack_id}/deployments/#{@deployment_id}",
40
+ ].join("\n")
41
+ end
42
+
43
+ alias_method :message, :to_s
44
+ end
15
45
  end
@@ -2,7 +2,7 @@ module DreamOps
2
2
  class HumanFormatter < BaseFormatter
3
3
  # Output the version of DreamOps
4
4
  def version
5
- DreamOps.ui.info DreamOps::VERSION
5
+ DreamOps.ui.info "Dream Ops v#{DreamOps::VERSION}"
6
6
  end
7
7
 
8
8
  # @param [DreamOps::Dependency] dependency
@@ -0,0 +1,18 @@
1
+ module DreamOps
2
+ class Logger < Ridley::Logging::Logger
3
+ alias_method :fatal, :error
4
+
5
+ def deprecate(message)
6
+ trace = caller.join("\n\t")
7
+ warn "DEPRECATION WARNING: #{message}\n\t#{trace}"
8
+ end
9
+
10
+ # Log an exception and its backtrace to FATAL
11
+ #
12
+ # @param [Exception] ex
13
+ def exception(ex)
14
+ fatal("#{ex.class}: #{ex}")
15
+ fatal(ex.backtrace.join("\n")) unless ex.backtrace.nil?
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ module DreamOps
2
+ module Mixin
3
+ module Logging
4
+ attr_writer :logger
5
+
6
+ def logger
7
+ @logger ||= DreamOps::Logger.new(STDOUT)
8
+ end
9
+ alias_method :log, :logger
10
+ end
11
+ end
12
+ end
@@ -1,3 +1,3 @@
1
1
  module DreamOps
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dream-ops
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Allen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-07-16 00:00:00.000000000 Z
11
+ date: 2017-07-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -152,6 +152,8 @@ files:
152
152
  - lib/dream-ops/errors.rb
153
153
  - lib/dream-ops/formatters/base.rb
154
154
  - lib/dream-ops/formatters/human.rb
155
+ - lib/dream-ops/logger.rb
156
+ - lib/dream-ops/mixin/logging.rb
155
157
  - lib/dream-ops/shell.rb
156
158
  - lib/dream-ops/utils/zip.rb
157
159
  - lib/dream-ops/version.rb
@@ -175,7 +177,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
175
177
  version: 2.0.0
176
178
  requirements: []
177
179
  rubyforge_project:
178
- rubygems_version: 2.4.8
180
+ rubygems_version: 2.6.12
179
181
  signing_key:
180
182
  specification_version: 4
181
183
  summary: This is the summary