motherbrain 1.2.1 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0fa9b41a671b085f2e3c08057bd1f2e3db7a61f2
4
- data.tar.gz: eadec4b2bc486a6b52eedf09d9c630608c4927db
3
+ metadata.gz: acc003650c5db20ee4a4198443bfc04339a538e9
4
+ data.tar.gz: ec7dbd88bac26101aa0029882106b775352025f2
5
5
  SHA512:
6
- metadata.gz: 52cadf2941cdd996c6f5f869cdafc0758a1d20f910e28d5e66fb0d1e23410b9d72d07b045b3ba3656edc737605470a675b9e0f2028f8e3235a2e8120377e6e62
7
- data.tar.gz: 035faec4ac2787b08b3491d49506caa3ac06a740c7ac37a1a8b8eb72122021051ff1c143c786a9a8863ba4ac44987aaef80b09346c87ace569933922559a9032
6
+ metadata.gz: c55553d715c7e67ac0a8627f3bffec8ce9ae47577c1601aa32242f75dade46acbb1d8614840df5ce8fddba26a028c91d91921fc88e0856aa5d98336e4811a0d6
7
+ data.tar.gz: 3f7d389d0ddcf5e067c417ada1412ff825710a3219bf83283808ff62bdf48ccf130ff0a5d8d6d97470b3a53e9c03eb1f0d8f9fffe17f8b606ce2bcc5ec233389
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ # 1.3.0
2
+
3
+ * [#711](https://github.com/RiotGames/motherbrain/pull/711) Implement a new command for upgrading a node or environment's Omnibus Chef installation
4
+ * [#708](https://github.com/RiotGames/motherbrain/pull/708) Fix a bug when node filtering would reduce the nodes to 0
5
+
1
6
  # 1.2.1
2
7
 
3
8
  * [#705](https://github.com/RiotGames/motherbrain/pull/705) Make connector_for_os public and ensure it is used for environment upgrades
@@ -0,0 +1,48 @@
1
+ module MotherBrain::API
2
+ class V1
3
+ class ChefEndpoint < MB::API::Endpoint
4
+ helpers MB::API::Helpers
5
+
6
+ rescue_from MB::OmnibusUpgradeError do |ex|
7
+ rack_response(ex.to_json, 400, "Content-type" => "application/json")
8
+ end
9
+
10
+ namespace 'chef' do
11
+
12
+ desc "Remove Chef from node and purge it's data from the Chef server"
13
+ params do
14
+ requires :hostname, type: String, desc: "the hostname of the node to purge"
15
+ optional :skip_chef, type: Boolean, desc: "Skip removing the Chef installation from the node"
16
+ end
17
+ post 'purge' do
18
+ node_querier.async_purge(params[:hostname], params.slice(:skip_chef).freeze)
19
+ end
20
+
21
+ desc "Upgrades the provided node and an environment of node's Omnibus Chef installation"
22
+ params do
23
+ requires :version, type: String, desc: "the version of omnibus to upgrade to"
24
+ optional :environment_id, type: String, desc: "an environment to upgrade"
25
+ optional :host, type: String, desc: "a host to upgrade"
26
+ optional :prerelease, type: Boolean, desc: "boolean to use a prerelease version of Chef"
27
+ optional :direct_url, type: String, desc: "a direct URL to a Omnibus binary file"
28
+ end
29
+ post 'upgrade' do
30
+ nodes = nil
31
+ host = params[:host]
32
+ environment_id = params[:environment_id]
33
+
34
+ raise MB::OmnibusUpgradeError.new("Need to provide one of :environment_id or :host") if host.nil? && environment_id.nil?
35
+ raise MB::OmnibusUpgradeError.new("Both :environment_id and :host were provided") if !host.nil? && !environment_id.nil?
36
+
37
+ if host
38
+ node_object = Ridley::NodeObject.new(host, automatic: { fqdn: host })
39
+ nodes = [node_object]
40
+ elsif environment_id
41
+ nodes = environment_manager.nodes_for_environment(params[:environment_id])
42
+ end
43
+ node_querier.async_upgrade_omnibus(params[:version], nodes, params.slice(:prerelease, :direct_url).freeze)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
data/lib/mb/api/v1.rb CHANGED
@@ -6,6 +6,7 @@ module MotherBrain::API
6
6
  require_relative 'v1/environments_endpoint'
7
7
  require_relative 'v1/jobs_endpoint'
8
8
  require_relative 'v1/plugins_endpoint'
9
+ require_relative 'v1/chef_endpoint'
9
10
 
10
11
  version 'v1', using: :header, vendor: 'motherbrain'
11
12
  format :json
@@ -41,6 +42,7 @@ module MotherBrain::API
41
42
  mount V1::JobsEndpoint
42
43
  mount V1::EnvironmentsEndpoint
43
44
  mount V1::PluginsEndpoint
45
+ mount V1::ChefEndpoint
44
46
  add_swagger_documentation
45
47
 
46
48
  if MB.testing?
@@ -234,7 +234,8 @@ module MotherBrain
234
234
  "template",
235
235
  "purge",
236
236
  "disable",
237
- "enable"
237
+ "enable",
238
+ "upgrade_omnibus"
238
239
  ].freeze
239
240
 
240
241
  CREATE_ENVIRONMENT_TASKS = [
@@ -334,6 +335,34 @@ module MotherBrain
334
335
  CliClient.new(job).display
335
336
  end
336
337
 
338
+ method_option :host,
339
+ type: :string,
340
+ desc: "A hostname for the node to be upgraded"
341
+ method_option :prerelease,
342
+ type: :boolean,
343
+ desc: "Boolean to install a prerelease version of Chef",
344
+ default: false
345
+ method_option :direct_url,
346
+ type: :string,
347
+ desc: "A URL pointing directly to a Chef package to install"
348
+ desc "upgrade_omnibus VERSION", "Upgrades the Omnibus Chef installation on a Chef Environment of nodes to a specified VERSION."
349
+ def upgrade_omnibus(version)
350
+ nodes = nil
351
+ if options["host"]
352
+ host = options["host"]
353
+ node_object = Ridley::NodeObject.new(host, automatic: { fqdn: host })
354
+ nodes = [node_object]
355
+ elsif options["environment"]
356
+ nodes = environment_manager.nodes_for_environment(options["environment"])
357
+ end
358
+ if nodes.nil?
359
+ ui.error "Error - you need to either define a host (--host) or an environment (-e) to operate on."
360
+ else
361
+ job = node_querier.async_upgrade_omnibus(version, nodes, options.to_hash.symbolize_keys)
362
+ CliClient.new(job).display
363
+ end
364
+ end
365
+
337
366
  method_option :force,
338
367
  type: :boolean,
339
368
  desc: "Perform HOST enable even if the environment is locked",
@@ -124,10 +124,10 @@ module MotherBrain
124
124
  group.nodes(environment)
125
125
  end.uniq
126
126
 
127
- return unless nodes.any?
128
-
129
127
  nodes = MB::NodeFilter.filter(node_filter, nodes) if node_filter
130
128
 
129
+ return unless nodes.any?
130
+
131
131
  if options[:any]
132
132
  nodes = nodes.sample(options[:any])
133
133
  end
data/lib/mb/errors.rb CHANGED
@@ -690,4 +690,9 @@ module MotherBrain
690
690
  "#{@name} is disabled."
691
691
  end
692
692
  end
693
+
694
+ class OmnibusUpgradeError < MBError
695
+ exit_code(25)
696
+ error_code(3029)
697
+ end
693
698
  end
@@ -279,6 +279,26 @@ module MotherBrain
279
279
  job.ticket
280
280
  end
281
281
 
282
+ # Asynchronously upgrade the Omnibus installation of Chef on the given nodes
283
+ # to a specific version.
284
+ #
285
+ # @param [String] version
286
+ # the version of Chef to upgrade to
287
+ # @param [Array<Ridley::NodeObject>] nodes
288
+ # the node(s) to upgrade omnibus on
289
+ #
290
+ # @option options [Boolean] :prerelease
291
+ # whether or not to use a prerelease version of Chef
292
+ # @option options [String] :direct_url
293
+ # a URL pointing directly to a Chef package to install
294
+ #
295
+ # @return [Mb::JobTicket]
296
+ def async_upgrade_omnibus(version, nodes, options = {})
297
+ job = Job.new(:upgrade_omnibus)
298
+ async(:upgrade_omnibus, job, version, nodes, options)
299
+ job.ticket
300
+ end
301
+
282
302
  # Asynchronously disable a node to stop services @host and prevent
283
303
  # chef-client from being run on @host until @host is reenabled
284
304
  #
@@ -353,6 +373,43 @@ module MotherBrain
353
373
  job.terminate if job && job.alive?
354
374
  end
355
375
 
376
+ # Upgrades the Omnibus installation of Chef on a specific node(s) to
377
+ # a specific version.
378
+ #
379
+ # @param [MB::Job] job
380
+ # @param [String] version
381
+ # the version of Chef to upgrade to
382
+ # @param [Array<Ridley::NodeObject>] nodes
383
+ # the node(s) to upgrade omnibus on
384
+ #
385
+ # @option options [Boolean] :prerelease
386
+ # whether or not to use a prerelease version of Chef
387
+ # @option options [String] :direct_url
388
+ # a URL pointing directly to a Chef package to install
389
+ #
390
+ # @return [MB::Job]
391
+ def upgrade_omnibus(job, version, nodes, options = {})
392
+ futures = Array.new
393
+ options = options.merge(chef_version: version)
394
+
395
+ hostnames = nodes.collect(&:public_hostname)
396
+ job.report_running("Upgrading Omnibus Chef installation on #{hostnames}")
397
+
398
+ hostnames.each do |hostname|
399
+ futures << chef_connection.node.future(:update_omnibus, hostname, options)
400
+ end
401
+
402
+ begin
403
+ safe_remote { futures.map(&:value) }
404
+ rescue RemoteCommandError => e
405
+ job.report_failure
406
+ end
407
+
408
+ job.report_success
409
+ ensure
410
+ job.terminate if job && job.alive?
411
+ end
412
+
356
413
  # Remove explicit service state on @host and remove disabled entry
357
414
  # from run list to allow chef-client to run on @host
358
415
  #
data/lib/mb/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module MotherBrain
2
- VERSION = '1.2.1'
2
+ VERSION = '1.3.0'
3
3
  end
data/motherbrain.gemspec CHANGED
@@ -44,7 +44,7 @@ Gem::Specification.new do |s|
44
44
  s.add_dependency 'net-ssh'
45
45
  s.add_dependency 'net-sftp'
46
46
  s.add_dependency 'solve', '~> 1.1'
47
- s.add_dependency 'ridley-connectors', '~> 2.1'
47
+ s.add_dependency 'ridley-connectors', '~> 2.2'
48
48
  s.add_dependency 'thor', '~> 0.18.0'
49
49
  s.add_dependency 'faraday', '~> 0.9'
50
50
  s.add_dependency 'multi_json'
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe MB::API::V1::ChefEndpoint do
4
+ include Rack::Test::Methods
5
+
6
+ before(:all) { MB::RestGateway.start(port: 26101) }
7
+ after(:all) { MB::RestGateway.stop }
8
+ let(:app) { MB::RestGateway.instance.app }
9
+ let(:job) { MB::Job.new(:test) }
10
+
11
+ describe "POST /chef/purge" do
12
+ let(:hostname) { "foo.bar.com" }
13
+
14
+ it "returns 201" do
15
+ node_querier.should_receive(:async_purge).with(hostname, anything()).and_return(job.ticket)
16
+ json_post "/chef/purge",
17
+ MultiJson.dump(hostname: hostname)
18
+ last_response.status.should == 201
19
+ end
20
+ end
21
+
22
+ describe "POST /chef/upgrade" do
23
+ let(:version) { "1.2.3" }
24
+ let(:environment_id) { "rpsec_test" }
25
+ let(:host) { "1.1.1.1" }
26
+
27
+ context "when neither environment_id nor host are provided" do
28
+ it "returns a 400" do
29
+ json_post "/chef/upgrade",
30
+ MultiJson.dump(version: version)
31
+
32
+ last_response.status.should == 400
33
+ end
34
+ end
35
+
36
+ context "when both environment_id and host are provided" do
37
+ it "returns a 400" do
38
+ json_post "/chef/upgrade",
39
+ MultiJson.dump(version: version, environment_id: environment_id, host: host)
40
+
41
+ last_response.status.should == 400
42
+ end
43
+ end
44
+
45
+ context "when host is provided" do
46
+ let(:node_object) { Ridley::NodeObject.new(host, automatic: { fqdn: host }) }
47
+
48
+ it "returns 201" do
49
+ node_querier.should_receive(:async_upgrade_omnibus).with(version, [node_object], anything()).and_return(job.ticket)
50
+
51
+ json_post "/chef/upgrade",
52
+ MultiJson.dump(version: version, host: host)
53
+
54
+ last_response.status.should == 201
55
+ end
56
+ end
57
+
58
+ context "when environment_id is provided" do
59
+ let(:nodes) { ["node1", "node2", "node3"] }
60
+
61
+ before do
62
+ environment_manager.stub(:nodes_for_environment).and_return(nodes)
63
+ end
64
+
65
+ it "returns 201" do
66
+ node_querier.should_receive(:async_upgrade_omnibus).with(version, nodes, anything()).and_return(job.ticket)
67
+
68
+ json_post "/chef/upgrade",
69
+ MultiJson.dump(version: version, environment_id: environment_id)
70
+
71
+ last_response.status.should == 201
72
+ end
73
+ end
74
+ end
75
+ end
@@ -7,7 +7,7 @@ describe MB::CommandRunner do
7
7
  described_class.new(job, environment, scope, command_block, node_filter)
8
8
  }
9
9
 
10
- let(:scope) { MB::Plugin.new(double(valid?: true)) }
10
+ let(:scope) { MB::Plugin.new(double(valid?: true, name: 'cookbook')) }
11
11
 
12
12
  let(:action_1) { double('action_1', name: "action 1", run: nil) }
13
13
  let(:action_2) { double('action_2', name: "action 2", run: nil) }
@@ -39,6 +39,7 @@ describe MB::CommandRunner do
39
39
  MB::CommandRunner::CleanRoom.stub_chain(:new, :actions).and_return(actions)
40
40
 
41
41
  scope.stub(:group!).with("master_group").and_return(master_group)
42
+ scope.stub(:group!).with("slave_group").and_return(slave_group)
42
43
  end
43
44
 
44
45
  describe "#on" do
@@ -126,6 +127,20 @@ describe MB::CommandRunner do
126
127
  end
127
128
  end
128
129
 
130
+ context "when a group's nodes are filtered to 0" do
131
+ let(:node_filter) { "c.riotgames.com" }
132
+ let(:command_block) {
133
+ proc {
134
+ on("master_group") {}
135
+ on("slave_group") {}
136
+ }
137
+ }
138
+
139
+ it "skips groups that have all their nodes filtered out" do
140
+ command_runner
141
+ end
142
+ end
143
+
129
144
  context "with max_concurrent: 1" do
130
145
  let(:command_block) {
131
146
  proc {
@@ -374,6 +374,58 @@ describe MB::NodeQuerier do
374
374
  end
375
375
  end
376
376
 
377
+ describe "#async_upgrade_omnibus" do
378
+ let(:host) { "192.168.1.1" }
379
+ let(:nodes) { [Ridley::NodeObject.new(host, automatic: { fqdn: host })] }
380
+ let(:version) { "11.12.4" }
381
+ let(:options) { Hash.new }
382
+
383
+ it "creates a Job and delegates to #upgrade_omnibus" do
384
+ ticket = double('ticket')
385
+ job = double('job', ticket: ticket)
386
+ MB::Job.should_receive(:new).and_return(job)
387
+ subject.should_receive(:upgrade_omnibus).with(job, version, nodes, options)
388
+
389
+ subject.async_upgrade_omnibus(version, nodes, options)
390
+ end
391
+
392
+ it "returns a JobTicket" do
393
+ expect(subject.async_purge(host, options)).to be_a(MB::JobRecord)
394
+ end
395
+ end
396
+
397
+ describe "#upgrade_omnibus" do
398
+ let(:host) { "192.168.1.1" }
399
+ let(:nodes) { [Ridley::NodeObject.new(host, automatic: { fqdn: host })] }
400
+ let(:version) { "11.12.4" }
401
+ let(:options) do
402
+ {
403
+ chef_version: version
404
+ }
405
+ end
406
+ let(:job) { MB::Job.new(:upgrade_omnibus) }
407
+ let(:future_stub) { double(Celluloid::Future, value: nil) }
408
+
409
+ before do
410
+ subject.chef_connection.
411
+ stub_chain(:node, :future).
412
+ with(:update_omnibus, host, options).
413
+ and_return(future_stub)
414
+ end
415
+
416
+ context "when there are no errors" do
417
+ it "reports success" do
418
+ job.should_receive(:report_success)
419
+ subject.upgrade_omnibus(job, version, nodes, options)
420
+ end
421
+ end
422
+
423
+ it "terminates the job" do
424
+ subject.upgrade_omnibus(job, version, nodes, options)
425
+ expect(job).to_not be_alive
426
+ end
427
+ end
428
+
377
429
  describe "#async_disable" do
378
430
  let(:host) { "192.168.1.1" }
379
431
  let(:options) { Hash.new }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: motherbrain
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jamie Winsor
@@ -15,7 +15,7 @@ authors:
15
15
  autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
- date: 2014-05-01 00:00:00.000000000 Z
18
+ date: 2014-05-20 00:00:00.000000000 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: celluloid
@@ -149,14 +149,14 @@ dependencies:
149
149
  requirements:
150
150
  - - ~>
151
151
  - !ruby/object:Gem::Version
152
- version: '2.1'
152
+ version: '2.2'
153
153
  type: :runtime
154
154
  prerelease: false
155
155
  version_requirements: !ruby/object:Gem::Requirement
156
156
  requirements:
157
157
  - - ~>
158
158
  - !ruby/object:Gem::Version
159
- version: '2.1'
159
+ version: '2.2'
160
160
  - !ruby/object:Gem::Dependency
161
161
  name: thor
162
162
  requirement: !ruby/object:Gem::Requirement
@@ -388,6 +388,7 @@ files:
388
388
  - lib/mb/api/endpoint.rb
389
389
  - lib/mb/api/helpers.rb
390
390
  - lib/mb/api/v1.rb
391
+ - lib/mb/api/v1/chef_endpoint.rb
391
392
  - lib/mb/api/v1/config_endpoint.rb
392
393
  - lib/mb/api/v1/environments_endpoint.rb
393
394
  - lib/mb/api/v1/jobs_endpoint.rb
@@ -517,6 +518,7 @@ files:
517
518
  - spec/support/spec_helpers.rb
518
519
  - spec/unit/mb/api/application_spec.rb
519
520
  - spec/unit/mb/api/helpers_spec.rb
521
+ - spec/unit/mb/api/v1/chef_endpoint_spec.rb
520
522
  - spec/unit/mb/api/v1/config_endpoint_spec.rb
521
523
  - spec/unit/mb/api/v1/environments_endpoint_spec.rb
522
524
  - spec/unit/mb/api/v1/jobs_endpoint_spec.rb
@@ -666,6 +668,7 @@ test_files:
666
668
  - spec/support/spec_helpers.rb
667
669
  - spec/unit/mb/api/application_spec.rb
668
670
  - spec/unit/mb/api/helpers_spec.rb
671
+ - spec/unit/mb/api/v1/chef_endpoint_spec.rb
669
672
  - spec/unit/mb/api/v1/config_endpoint_spec.rb
670
673
  - spec/unit/mb/api/v1/environments_endpoint_spec.rb
671
674
  - spec/unit/mb/api/v1/jobs_endpoint_spec.rb