motherbrain 1.2.1 → 1.3.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: 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