motherbrain 1.3.0 → 1.4.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 +4 -4
- data/lib/mb/api/v1.rb +28 -6
- data/lib/mb/api/v1/server_control_endpoint.rb +37 -0
- data/lib/mb/application.rb +35 -2
- data/lib/mb/berkshelf.rb +2 -0
- data/lib/mb/errors.rb +26 -0
- data/lib/mb/plugin_manager.rb +5 -0
- data/lib/mb/rest_gateway.rb +7 -2
- data/lib/mb/version.rb +1 -1
- data/spec/unit/mb/api/v1/server_control_endpoint_spec.rb +54 -0
- data/spec/unit/mb/application_spec.rb +14 -0
- data/spec/unit/mb/rest_gateway_spec.rb +15 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dfdaf1022206cdd6053520eed112b1fedc5c25a8
|
4
|
+
data.tar.gz: 5b86141638b4849c04c864759e9a0546cca56ff4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3be6473cedd6894ac603158363ddfb0249fa9bea09c747d8ef1957cdb5a82cd0976f56cb5a6857a692929656a2d51aa3128438ff766e1116d61d00b522debda
|
7
|
+
data.tar.gz: 2017c5767c5f3aa705fd9dd991b65b76f4f7b5dd113192702a7eb03911d3c1b8a5a7ca6fd47bac11c3f6a5c2d6157d6c098a376d233f5d87390ef41ff1a1e794
|
data/lib/mb/api/v1.rb
CHANGED
@@ -7,18 +7,21 @@ module MotherBrain::API
|
|
7
7
|
require_relative 'v1/jobs_endpoint'
|
8
8
|
require_relative 'v1/plugins_endpoint'
|
9
9
|
require_relative 'v1/chef_endpoint'
|
10
|
+
require_relative 'v1/server_control_endpoint'
|
10
11
|
|
11
12
|
version 'v1', using: :header, vendor: 'motherbrain'
|
12
13
|
format :json
|
13
14
|
default_format :json
|
14
15
|
|
15
|
-
|
16
|
+
JSON_CONTENT_TYPE = {"Content-type" => "application/json"}
|
17
|
+
|
18
|
+
rescue_from Grape::Exceptions::Validation do |ex|
|
16
19
|
body = MultiJson.encode(
|
17
|
-
status:
|
18
|
-
message:
|
19
|
-
param:
|
20
|
+
status: ex.status,
|
21
|
+
message: ex.message,
|
22
|
+
param: ex.param
|
20
23
|
)
|
21
|
-
rack_response(body,
|
24
|
+
rack_response(body, ex.status, JSON_CONTENT_TYPE)
|
22
25
|
end
|
23
26
|
|
24
27
|
rescue_from :all do |ex|
|
@@ -29,13 +32,31 @@ module MotherBrain::API
|
|
29
32
|
MultiJson.encode(code: -1, message: ex.message)
|
30
33
|
end
|
31
34
|
|
32
|
-
|
35
|
+
http_status_code = if ex.is_a?(MB::APIError)
|
36
|
+
ex.http_status_code
|
37
|
+
else
|
38
|
+
500
|
39
|
+
end
|
40
|
+
|
41
|
+
rack_response(body, http_status_code, JSON_CONTENT_TYPE)
|
33
42
|
end
|
34
43
|
|
35
44
|
before do
|
36
45
|
header['Access-Control-Allow-Origin'] = '*'
|
37
46
|
header['Access-Control-Request-Method'] = '*'
|
38
47
|
header.delete('Transfer-Encoding')
|
48
|
+
|
49
|
+
server_control_endpoint = false
|
50
|
+
MB::API::V1::ServerControlEndpoint.endpoints.each do |endpoint|
|
51
|
+
endpoint.routes.each do |route|
|
52
|
+
if request.path =~ route.route_compiled
|
53
|
+
server_control_endpoint = true
|
54
|
+
break
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
raise MB::ApplicationPaused.new if MB::Application.paused? && !server_control_endpoint
|
39
60
|
end
|
40
61
|
|
41
62
|
mount V1::ConfigEndpoint
|
@@ -43,6 +64,7 @@ module MotherBrain::API
|
|
43
64
|
mount V1::EnvironmentsEndpoint
|
44
65
|
mount V1::PluginsEndpoint
|
45
66
|
mount V1::ChefEndpoint
|
67
|
+
mount V1::ServerControlEndpoint
|
46
68
|
add_swagger_documentation
|
47
69
|
|
48
70
|
if MB.testing?
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module MotherBrain::API
|
2
|
+
class V1
|
3
|
+
class ServerControlEndpoint < MB::API::Endpoint
|
4
|
+
helpers MB::Mixin::Services
|
5
|
+
|
6
|
+
desc "resume the server, preventing new requests from being processed"
|
7
|
+
put 'resume' do
|
8
|
+
MB::Application.resume
|
9
|
+
server_status
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "pause the server, preventing new requests from being processed"
|
13
|
+
put 'pause' do
|
14
|
+
MB::Application.pause
|
15
|
+
server_status
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "stop the server"
|
19
|
+
put 'stop' do
|
20
|
+
MB::Application.stop
|
21
|
+
status(202)
|
22
|
+
server_status
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "get the server status"
|
26
|
+
get 'status' do
|
27
|
+
server_status
|
28
|
+
end
|
29
|
+
|
30
|
+
helpers do
|
31
|
+
def server_status
|
32
|
+
{server_status: MB::Application.status}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/mb/application.rb
CHANGED
@@ -28,6 +28,12 @@ module MotherBrain
|
|
28
28
|
# @example running the application in the background
|
29
29
|
# MB::Application.run!(config)
|
30
30
|
module Application
|
31
|
+
module Status
|
32
|
+
RUNNING = :running
|
33
|
+
PAUSED = :paused
|
34
|
+
STOPPING = :stopping
|
35
|
+
end
|
36
|
+
|
31
37
|
class << self
|
32
38
|
extend Forwardable
|
33
39
|
include MB::Mixin::Services
|
@@ -95,7 +101,29 @@ module MotherBrain
|
|
95
101
|
|
96
102
|
# Stop the running application
|
97
103
|
def stop
|
98
|
-
instance.
|
104
|
+
instance.async_interrupt(3)
|
105
|
+
@status = Status::STOPPING
|
106
|
+
end
|
107
|
+
|
108
|
+
# Set the application state to paused. This allows actors to
|
109
|
+
# continue processing, but causes the RestGateway not to accept
|
110
|
+
# new requests.
|
111
|
+
#
|
112
|
+
# See: MotherBrain::API::V1 L51, 'before' block
|
113
|
+
def pause
|
114
|
+
@status = Status::PAUSED
|
115
|
+
end
|
116
|
+
|
117
|
+
def resume
|
118
|
+
@status = Status::RUNNING
|
119
|
+
end
|
120
|
+
|
121
|
+
def paused?
|
122
|
+
status == Status::PAUSED
|
123
|
+
end
|
124
|
+
|
125
|
+
def status
|
126
|
+
@status ||= Status::RUNNING
|
99
127
|
end
|
100
128
|
end
|
101
129
|
|
@@ -135,7 +163,12 @@ module MotherBrain
|
|
135
163
|
@registry[:ridley].async.configure(new_config.to_ridley)
|
136
164
|
end
|
137
165
|
|
138
|
-
def
|
166
|
+
def async_interrupt(delay = 0)
|
167
|
+
future.interrupt(delay)
|
168
|
+
end
|
169
|
+
|
170
|
+
def interrupt(delay = 0)
|
171
|
+
Celluloid.sleep(delay) if delay > 0
|
139
172
|
interrupt_mutex.synchronize do
|
140
173
|
unless interrupted
|
141
174
|
@interrupted = true
|
data/lib/mb/berkshelf.rb
CHANGED
@@ -63,6 +63,8 @@ module MotherBrain
|
|
63
63
|
|
64
64
|
def initialize(berksfile_lock_path)
|
65
65
|
@berksfile_lock = ::Berkshelf::Lockfile.from_file(berksfile_lock_path)
|
66
|
+
rescue ::Berkshelf::LockfileParserError
|
67
|
+
log.warn "Unable to parse Berksfile.lock - maybe it's an old format?"
|
66
68
|
end
|
67
69
|
|
68
70
|
# Return a hash of all of the cookbook versions found in the Berksfile.lock
|
data/lib/mb/errors.rb
CHANGED
@@ -89,6 +89,23 @@ module MotherBrain
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
+
class APIError < MBError
|
93
|
+
DEFAULT_HTTP_STATUS_CODE = 500
|
94
|
+
|
95
|
+
class << self
|
96
|
+
# @param [Integer] code
|
97
|
+
#
|
98
|
+
# @return [Integer]
|
99
|
+
def http_status_code(code = DEFAULT_HTTP_STATUS_CODE)
|
100
|
+
@exit_code ||= code
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def http_status_code
|
105
|
+
self.class.http_status_code
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
92
109
|
# Internal errors
|
93
110
|
class InternalError < MBError
|
94
111
|
exit_code(99)
|
@@ -695,4 +712,13 @@ module MotherBrain
|
|
695
712
|
exit_code(25)
|
696
713
|
error_code(3029)
|
697
714
|
end
|
715
|
+
|
716
|
+
class ApplicationPaused < APIError
|
717
|
+
error_code(3330)
|
718
|
+
http_status_code(503)
|
719
|
+
|
720
|
+
def message
|
721
|
+
"MotherBrain is paused. It will not accept new requests until it is resumed."
|
722
|
+
end
|
723
|
+
end
|
698
724
|
end
|
data/lib/mb/plugin_manager.rb
CHANGED
@@ -322,6 +322,7 @@ module MotherBrain
|
|
322
322
|
scratch_dir = FileSystem.tmpdir("cbplugin")
|
323
323
|
metadata_path = File.join(scratch_dir, CookbookMetadata::JSON_FILENAME)
|
324
324
|
plugin_path = File.join(scratch_dir, Plugin::PLUGIN_FILENAME)
|
325
|
+
lockfile_path = File.join(scratch_dir, Berkshelf::Lockfile::BERKSFILE_LOCK)
|
325
326
|
|
326
327
|
File.write(metadata_path, resource.metadata.to_json)
|
327
328
|
|
@@ -329,6 +330,10 @@ module MotherBrain
|
|
329
330
|
raise PluginLoadError, "failure downloading plugin file for #{resource.name}"
|
330
331
|
end
|
331
332
|
|
333
|
+
unless resource.download_file(:root_file, Berkshelf::Lockfile::BERKSFILE_LOCK, lockfile_path)
|
334
|
+
log.info "No Berksfile.lock found for #{resource.name} - won't be able to use cookbook versions from lockfile"
|
335
|
+
end
|
336
|
+
|
332
337
|
load_file(scratch_dir, options)
|
333
338
|
rescue PluginSyntaxError, PluginLoadError => ex
|
334
339
|
err_msg = "could not load remote plugin #{name} (#{version}): #{ex.message}"
|
data/lib/mb/rest_gateway.rb
CHANGED
@@ -29,7 +29,7 @@ module MotherBrain
|
|
29
29
|
|
30
30
|
include MB::Logging
|
31
31
|
|
32
|
-
DEFAULT_PORT = ENV["PORT"].to_i
|
32
|
+
DEFAULT_PORT = ENV["PORT"] ? ENV["PORT"].to_i : 26100
|
33
33
|
|
34
34
|
DEFAULT_OPTIONS = {
|
35
35
|
host: '0.0.0.0',
|
@@ -59,7 +59,12 @@ module MotherBrain
|
|
59
59
|
options[:Port] = options[:port]
|
60
60
|
|
61
61
|
log.info { "REST Gateway listening on #{options[:host]}:#{options[:port]}" }
|
62
|
-
|
62
|
+
|
63
|
+
begin
|
64
|
+
super(app, options)
|
65
|
+
rescue Errno::EADDRINUSE
|
66
|
+
log.fatal { "Port #{options[:port]} is already in use. Unable to start rest gateway." }
|
67
|
+
end
|
63
68
|
end
|
64
69
|
|
65
70
|
private
|
data/lib/mb/version.rb
CHANGED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MB::API::V1::ServerControlEndpoint do
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
before(:all) { MB::RestGateway.start(port: 26101) }
|
7
|
+
after(:all) { MB::RestGateway.stop }
|
8
|
+
after(:each) { MB::Application.resume }
|
9
|
+
let(:app) { MB::RestGateway.instance.app }
|
10
|
+
|
11
|
+
describe "PUT /pause" do
|
12
|
+
it "pauses the server" do
|
13
|
+
put '/pause'
|
14
|
+
last_response.status.should == 200
|
15
|
+
JSON.parse(last_response.body).should eq("server_status" => "paused")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "prevents actions while the server is paused" do
|
19
|
+
put '/pause'
|
20
|
+
json_post "/environments/environmentname/upgrade",
|
21
|
+
MultiJson.dump(plugin: { name: 'pluginname', version: '1.0.0' })
|
22
|
+
last_response.status.should == 503
|
23
|
+
JSON.parse(last_response.body).should eq("code"=>3330, "message"=>"MotherBrain is paused. It will not accept new requests until it is resumed.")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "PUT /resume" do
|
28
|
+
before do
|
29
|
+
MB::Application.pause
|
30
|
+
end
|
31
|
+
|
32
|
+
it "resumes the server" do
|
33
|
+
put '/resume'
|
34
|
+
last_response.status.should == 200
|
35
|
+
JSON.parse(last_response.body).should eq("server_status" => "running")
|
36
|
+
end
|
37
|
+
|
38
|
+
it "allows actions while the server is resumed" do
|
39
|
+
put '/resume'
|
40
|
+
json_post "/environments/environmentname/upgrade",
|
41
|
+
MultiJson.dump(plugin: { name: 'pluginname', version: '1.0.0' })
|
42
|
+
last_response.status.should == 201
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "PUT /stop" do
|
47
|
+
it "stops the server" do
|
48
|
+
MB::Application.instance.should_receive(:async_interrupt).with(3)
|
49
|
+
put '/stop'
|
50
|
+
last_response.status.should == 202
|
51
|
+
JSON.parse(last_response.body).should eq("server_status" => "stopping")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -15,5 +15,19 @@ describe MB::Application do
|
|
15
15
|
subject.config.should be_a(MB::Config)
|
16
16
|
end
|
17
17
|
end
|
18
|
+
|
19
|
+
describe "::pause" do
|
20
|
+
it "should pause" do
|
21
|
+
subject.pause
|
22
|
+
expect(subject.paused?).to eq(true)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "::resume" do
|
27
|
+
it "should resume" do
|
28
|
+
subject.resume
|
29
|
+
expect(subject.paused?).to eq(false)
|
30
|
+
end
|
31
|
+
end
|
18
32
|
end
|
19
33
|
end
|
@@ -10,4 +10,19 @@ describe MB::RestGateway do
|
|
10
10
|
subject.app.should be_a(MB::API::Application)
|
11
11
|
end
|
12
12
|
end
|
13
|
+
|
14
|
+
describe "constants" do
|
15
|
+
it "should set DEFAULT_PORT to $PORT" do
|
16
|
+
p = 12345
|
17
|
+
ENV["PORT"] = p.to_s
|
18
|
+
load File.join(MB.app_root, "lib", "mb", "rest_gateway.rb")
|
19
|
+
expect(MB::RestGateway::DEFAULT_PORT).to eq(p)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should set DEFAULT_PORT to 26100 if $PORT is not set" do
|
23
|
+
ENV.delete("PORT")
|
24
|
+
load File.join(MB.app_root, "lib", "mb", "rest_gateway.rb")
|
25
|
+
expect(MB::RestGateway::DEFAULT_PORT).to eq(26100)
|
26
|
+
end
|
27
|
+
end
|
13
28
|
end
|
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.
|
4
|
+
version: 1.4.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-
|
18
|
+
date: 2014-05-30 00:00:00.000000000 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: celluloid
|
@@ -393,6 +393,7 @@ files:
|
|
393
393
|
- lib/mb/api/v1/environments_endpoint.rb
|
394
394
|
- lib/mb/api/v1/jobs_endpoint.rb
|
395
395
|
- lib/mb/api/v1/plugins_endpoint.rb
|
396
|
+
- lib/mb/api/v1/server_control_endpoint.rb
|
396
397
|
- lib/mb/api/validators.rb
|
397
398
|
- lib/mb/api/validators/sem_ver.rb
|
398
399
|
- lib/mb/application.rb
|
@@ -523,6 +524,7 @@ files:
|
|
523
524
|
- spec/unit/mb/api/v1/environments_endpoint_spec.rb
|
524
525
|
- spec/unit/mb/api/v1/jobs_endpoint_spec.rb
|
525
526
|
- spec/unit/mb/api/v1/plugins_endpoint_spec.rb
|
527
|
+
- spec/unit/mb/api/v1/server_control_endpoint_spec.rb
|
526
528
|
- spec/unit/mb/api/v1_spec.rb
|
527
529
|
- spec/unit/mb/api/validators/sem_ver_spec.rb
|
528
530
|
- spec/unit/mb/application_spec.rb
|
@@ -622,7 +624,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
622
624
|
version: '0'
|
623
625
|
requirements: []
|
624
626
|
rubyforge_project:
|
625
|
-
rubygems_version: 2.
|
627
|
+
rubygems_version: 2.0.14
|
626
628
|
signing_key:
|
627
629
|
specification_version: 4
|
628
630
|
summary: An orchestrator for Chef
|
@@ -673,6 +675,7 @@ test_files:
|
|
673
675
|
- spec/unit/mb/api/v1/environments_endpoint_spec.rb
|
674
676
|
- spec/unit/mb/api/v1/jobs_endpoint_spec.rb
|
675
677
|
- spec/unit/mb/api/v1/plugins_endpoint_spec.rb
|
678
|
+
- spec/unit/mb/api/v1/server_control_endpoint_spec.rb
|
676
679
|
- spec/unit/mb/api/v1_spec.rb
|
677
680
|
- spec/unit/mb/api/validators/sem_ver_spec.rb
|
678
681
|
- spec/unit/mb/application_spec.rb
|