orchestrator_api 0.0.2
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 +7 -0
- data/CONTRIBUTING.md +46 -0
- data/Gemfile +4 -0
- data/LICENSE +17 -0
- data/README.md +75 -0
- data/lib/orchestrator_api.rb +94 -0
- data/lib/orchestrator_api/command.rb +18 -0
- data/lib/orchestrator_api/environments.rb +19 -0
- data/lib/orchestrator_api/error.rb +56 -0
- data/lib/orchestrator_api/jobs.rb +37 -0
- data/orchestrator_api-ruby.gemspec +11 -0
- data/spec/orchestrator_api_spec.rb +67 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/command_spec.rb +64 -0
- data/spec/unit/environment_spec.rb +29 -0
- data/spec/unit/error_spec.rb +58 -0
- data/spec/unit/jobs_spec.rb +73 -0
- metadata +59 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c38a1f1dd4cb1d0fd573379098051f4eabfb8590
|
4
|
+
data.tar.gz: 1400ae44d04625a0e6ef680f8edf3cc0b7b76c01
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 70d8f68a0b2801b5cc3cf850874159ec910ba259a9b71af3ac8cd9f7dd3d154ab9573fdbf248a120ad7385eef25f1811ba166bfbd1e8e890b14e92199340bae1
|
7
|
+
data.tar.gz: ab88fb43301ccb59114b8d10441662163115e2127fe7402654f5330c55039875583d3cf29cb911ca72ef74e21cfe0c633fdd5841dd14e16b5ea4c09dbf06ade5
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# How to contribute
|
2
|
+
|
3
|
+
* Make sure you have a [GitHub account](https://github.com/signup/free)
|
4
|
+
* Fork the repository on GitHub
|
5
|
+
|
6
|
+
## Making Changes
|
7
|
+
|
8
|
+
* Create a topic branch from where you want to base your work (this is almost
|
9
|
+
definitely the master branch).
|
10
|
+
* To quickly create a topic branch based on master; `git branch
|
11
|
+
fix/master/my_contribution master` then checkout the new branch with `git
|
12
|
+
checkout fix/master/my_contribution`.
|
13
|
+
* Please avoid working directly on the
|
14
|
+
`master` branch.
|
15
|
+
* Make commits of logical units.
|
16
|
+
* Check for unnecessary whitespace with `git diff --check` before committing.
|
17
|
+
* Make sure your commit messages are in the proper format.
|
18
|
+
|
19
|
+
````
|
20
|
+
Make the example in CONTRIBUTING imperative and concrete
|
21
|
+
|
22
|
+
Without this patch applied the example commit message in the CONTRIBUTING
|
23
|
+
document is not a concrete example. This is a problem because the
|
24
|
+
contributor is left to imagine what the commit message should look like
|
25
|
+
based on a description rather than an example. This patch fixes the
|
26
|
+
problem by making the example concrete and imperative.
|
27
|
+
|
28
|
+
The first line is a real life imperative statement with a ticket number
|
29
|
+
from our issue tracker. The body describes the behavior without the patch,
|
30
|
+
why this is a problem, and how the patch fixes the problem when applied.
|
31
|
+
````
|
32
|
+
|
33
|
+
* Make sure you have added the necessary tests for your changes.
|
34
|
+
* Run _all_ the tests to assure nothing else was accidentally broken.
|
35
|
+
|
36
|
+
## Submitting Changes
|
37
|
+
|
38
|
+
* Sign the [Contributor License Agreement](http://links.puppet.com/cla).
|
39
|
+
* Push your changes to a topic branch in your fork of the repository.
|
40
|
+
* Submit a pull request to the repository in the puppetlabs organization.
|
41
|
+
|
42
|
+
# Additional Resources
|
43
|
+
|
44
|
+
* [Contributor License Agreement](http://links.puppet.com/cla)
|
45
|
+
* [General GitHub documentation](http://help.github.com/)
|
46
|
+
* [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
orchestrator_api
|
2
|
+
|
3
|
+
Copyright (C) 2016 Puppet Labs Inc
|
4
|
+
|
5
|
+
Puppet,Inc. can be contacted at: info@puppet.com
|
6
|
+
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
you may not use this file except in compliance with the License.
|
9
|
+
You may obtain a copy of the License at
|
10
|
+
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
See the License for the specific language governing permissions and
|
17
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# Orchestrator_api
|
2
|
+
|
3
|
+
A simple client for interacting with the Orchestration Services API in Puppet Enterprise
|
4
|
+
[Puppet orchestration API](https://docs.puppet.com/pe/latest/api_index.html#puppet-orchestrator-api)
|
5
|
+
|
6
|
+
## Compatibility
|
7
|
+
|
8
|
+
Currently, this client supports the "V1" endpoints shipped as part of Puppet Enterprise 2016.2.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
```shell
|
13
|
+
gem install orchestrator_api
|
14
|
+
```
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
|
18
|
+
Requires a token with 'Orchestration' permissions. By default the token is
|
19
|
+
expected to be at `~/.puppetlabs/token` which is the default location used by
|
20
|
+
`puppet-access` when creating token.
|
21
|
+
|
22
|
+
### initialization Settings
|
23
|
+
|
24
|
+
* `service-url` **[required]** - Base URL for the location of the Orchestrator API service
|
25
|
+
* `ca_cert` **[required]** - Path to the CA certificate file needed to verify the SSL connection to the API.
|
26
|
+
* `token_path`- Path to a file with the RBAC token in it (defaults to `~/.puppetlabs/token`)
|
27
|
+
* `token` - Pass directly the RBAC token, if specified the token will be used instead of a token from file.
|
28
|
+
|
29
|
+
### Example
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
require 'orchestrator_api'
|
33
|
+
|
34
|
+
# Create a new client
|
35
|
+
# Requires at least a server name and path to the CA certificate
|
36
|
+
|
37
|
+
client = Orchestrator_api.new({
|
38
|
+
'service-url' => 'https://orchestrator.example.lan:8143/orchestrator/v1',
|
39
|
+
'ca_cert' => '/path/to/cert'
|
40
|
+
})
|
41
|
+
|
42
|
+
## Access endpoints through the client object
|
43
|
+
|
44
|
+
# Get details on all known jobs
|
45
|
+
result = client.jobs.all
|
46
|
+
|
47
|
+
# Get details on Individual jobs (job "5" in this example)
|
48
|
+
client.jobs.details(5)
|
49
|
+
|
50
|
+
# Perform an orchestrator deployment
|
51
|
+
new_job_details = client.command.deploy('production', {'noop' => true })
|
52
|
+
```
|
53
|
+
|
54
|
+
## Tests
|
55
|
+
|
56
|
+
```shell
|
57
|
+
bundle install
|
58
|
+
bundle exec rspec
|
59
|
+
```
|
60
|
+
|
61
|
+
## Issues & Contributions
|
62
|
+
|
63
|
+
File issues or feature requests using [GitHub
|
64
|
+
issues](https://github.com/puppetlabs/orchestrator_api-ruby/issues).
|
65
|
+
|
66
|
+
If you are interested in contributing to this project, please see the
|
67
|
+
[Contribution Guidelines](CONTRIBUTING.md)
|
68
|
+
|
69
|
+
## Authors
|
70
|
+
|
71
|
+
Tom Linkin <tom@puppet.com>
|
72
|
+
|
73
|
+
## License
|
74
|
+
|
75
|
+
See LICENSE.
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require 'uri'
|
3
|
+
require 'json'
|
4
|
+
require 'openssl'
|
5
|
+
|
6
|
+
class Orchestrator_api
|
7
|
+
require 'orchestrator_api/error'
|
8
|
+
require 'orchestrator_api/command'
|
9
|
+
require 'orchestrator_api/jobs'
|
10
|
+
require 'orchestrator_api/environments'
|
11
|
+
|
12
|
+
attr_accessor :config, :token
|
13
|
+
|
14
|
+
def initialize(settings = {})
|
15
|
+
|
16
|
+
@config = { 'token_path' => File.join(Dir.home, '.puppetlabs', 'token'),
|
17
|
+
}.merge(settings)
|
18
|
+
|
19
|
+
if @config['token']
|
20
|
+
@token = @config['token']
|
21
|
+
else
|
22
|
+
@token = File.read(@config['token_path'])
|
23
|
+
end
|
24
|
+
|
25
|
+
if @config['service-url'].nil?
|
26
|
+
raise "Configuration error: 'service-url' must specify the server running the Orchestration services and cannot be empty"
|
27
|
+
end
|
28
|
+
if @config['ca_cert'].nil?
|
29
|
+
raise "Configuration error: 'ca_cert' must specify a path to the CA certificate used for communications with the server and cannot be empty"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def url
|
34
|
+
config['service-url']
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_http(uri)
|
38
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
39
|
+
http.use_ssl = true
|
40
|
+
http.ssl_version = :TLSv1
|
41
|
+
http.ca_file = config['ca_cert']
|
42
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
43
|
+
http
|
44
|
+
end
|
45
|
+
|
46
|
+
def get(location)
|
47
|
+
uri = URI.parse(location)
|
48
|
+
https = create_http(uri)
|
49
|
+
|
50
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
51
|
+
req['Content-Type'] = "application/json"
|
52
|
+
req.add_field('X-Authentication', token)
|
53
|
+
res = https.request(req)
|
54
|
+
|
55
|
+
if res.code != "200"
|
56
|
+
raise Orchestrator_api::Error.make_error_from_response(res)
|
57
|
+
end
|
58
|
+
|
59
|
+
JSON.parse(res.body)
|
60
|
+
end
|
61
|
+
|
62
|
+
def post(location, body)
|
63
|
+
uri = URI.parse(location)
|
64
|
+
https = create_http(uri)
|
65
|
+
|
66
|
+
req = Net::HTTP::Post.new(uri.request_uri)
|
67
|
+
req['Content-Type'] = "application/json"
|
68
|
+
req.add_field('X-Authentication', token)
|
69
|
+
req.body = body.to_json
|
70
|
+
res = https.request(req)
|
71
|
+
|
72
|
+
if res.code != "202"
|
73
|
+
raise Orchestrator_api::Error.make_error_from_response(res)
|
74
|
+
end
|
75
|
+
|
76
|
+
JSON.parse(res.body)
|
77
|
+
end
|
78
|
+
|
79
|
+
def command
|
80
|
+
@command ||= Orchestrator_api::Command.new(self, url)
|
81
|
+
end
|
82
|
+
|
83
|
+
def environments
|
84
|
+
@environments ||= Orchestrator_api::Environments.new(self, url)
|
85
|
+
end
|
86
|
+
|
87
|
+
def jobs
|
88
|
+
@jobs ||= Orchestrator_api::Jobs.new(self, url)
|
89
|
+
end
|
90
|
+
|
91
|
+
def root
|
92
|
+
get(url)
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Orchestrator_api::Command
|
2
|
+
|
3
|
+
def initialize(https, api_url_base)
|
4
|
+
@https = https
|
5
|
+
@api_url_base = api_url_base
|
6
|
+
end
|
7
|
+
|
8
|
+
def deploy(environment, options = {})
|
9
|
+
raise ArgumentError, 'Must pass options as a hash' unless options.is_a? Hash
|
10
|
+
options['environment'] = environment
|
11
|
+
@https.post("#{@api_url_base}/command/deploy", options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def stop(job_number)
|
15
|
+
data = {"job" => "#{job_number}"}
|
16
|
+
@https.post("#{@api_url_base}/command/stop",data)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Orchestrator_api::Environments
|
2
|
+
|
3
|
+
def initialize(https, api_url_base)
|
4
|
+
@https = https
|
5
|
+
@api_url_base = api_url_base
|
6
|
+
end
|
7
|
+
|
8
|
+
def all
|
9
|
+
@https.get("#{api_url_base}/environments")
|
10
|
+
end
|
11
|
+
|
12
|
+
def applications(id)
|
13
|
+
@https.get("#{api_url_base}/environments/#{id}/applications")
|
14
|
+
end
|
15
|
+
|
16
|
+
def instances(id)
|
17
|
+
@https.get("#{api_url_base}/environments/#{id}/instances")
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
class Orchestrator_api::Error < Exception
|
3
|
+
|
4
|
+
def initialize(data,code)
|
5
|
+
@code = code
|
6
|
+
@kind = data['kind']
|
7
|
+
@details = data['details']
|
8
|
+
super(data['msg'])
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.make_error_from_response(res)
|
12
|
+
begin
|
13
|
+
data = JSON.parse(res.body)
|
14
|
+
rescue
|
15
|
+
return EndpointError.new("An unspecified error has occurred with the Orchestrator API")
|
16
|
+
end
|
17
|
+
code = res.code
|
18
|
+
|
19
|
+
case data['kind']
|
20
|
+
when 'puppetlabs.validators/validation-error'
|
21
|
+
ValidationError.new(data, code)
|
22
|
+
when 'puppetlabs.orchestrator/unknown-job'
|
23
|
+
UnknownJob.new(data, code)
|
24
|
+
when 'puppetlabs.orchestrator/unknown-environment'
|
25
|
+
UnknownEnvironment.new(data, code)
|
26
|
+
when 'puppetlabs.orchestrator/empty-environment'
|
27
|
+
EmptyEnvironment.new(data, code)
|
28
|
+
when 'puppetlabs.orchestrator/empty-target'
|
29
|
+
EmptyTarget.new(data, code)
|
30
|
+
when 'puppetlabs.orchestrator/dependency-cycle'
|
31
|
+
DependencyCycle.new(data, code)
|
32
|
+
when 'puppetlabs.orchestrator/puppetdb-error'
|
33
|
+
PuppetdbError.new(data, code)
|
34
|
+
when 'puppetlabs.orchestrator/query-error'
|
35
|
+
QueryError.new(data, code)
|
36
|
+
when 'puppetlabs.orchestrator/unknown-error'
|
37
|
+
UnknownError.new(data, code)
|
38
|
+
when 'puppetlabs.orchestrator/not-permitted'
|
39
|
+
UnauthorizedError.new(data, code)
|
40
|
+
else
|
41
|
+
EndpointError.new("An unspecified error has occurred with the Orchestrator API")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Orchestrator_api::Error::ValidationError < Orchestrator_api::Error; end
|
47
|
+
class Orchestrator_api::Error::UnknownJob < Orchestrator_api::Error; end
|
48
|
+
class Orchestrator_api::Error::UnknownEnvironment < Orchestrator_api::Error; end
|
49
|
+
class Orchestrator_api::Error::EmptyEnvironment < Orchestrator_api::Error; end
|
50
|
+
class Orchestrator_api::Error::EmptyTarget < Orchestrator_api::Error; end
|
51
|
+
class Orchestrator_api::Error::DependencyCycle < Orchestrator_api::Error; end
|
52
|
+
class Orchestrator_api::Error::PuppetdbError < Orchestrator_api::Error; end
|
53
|
+
class Orchestrator_api::Error::QueryError < Orchestrator_api::Error; end
|
54
|
+
class Orchestrator_api::Error::UnknownError < Orchestrator_api::Error; end
|
55
|
+
class Orchestrator_api::Error::UnauthorizedError < Orchestrator_api::Error; end
|
56
|
+
class Orchestrator_api::Error::EndpointError < Exception; end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class Orchestrator_api::Jobs
|
2
|
+
|
3
|
+
def initialize(http, api_url_base)
|
4
|
+
@https = http
|
5
|
+
@api_url_base = api_url_base
|
6
|
+
end
|
7
|
+
|
8
|
+
def all(limit=nil)
|
9
|
+
url = "#{@api_url_base}/jobs"
|
10
|
+
if limit
|
11
|
+
url << "?limit=#{limit}"
|
12
|
+
end
|
13
|
+
|
14
|
+
@https.get(url)
|
15
|
+
end
|
16
|
+
|
17
|
+
def details(id)
|
18
|
+
@https.get("#{@api_url_base}/jobs/#{id}")
|
19
|
+
end
|
20
|
+
|
21
|
+
def nodes(id)
|
22
|
+
@https.get("#{@api_url_base}/jobs/#{id}/nodes")
|
23
|
+
end
|
24
|
+
|
25
|
+
def report(id)
|
26
|
+
@https.get("#{@api_url_base}/jobs/#{id}/report")
|
27
|
+
end
|
28
|
+
|
29
|
+
def events(id, start = nil)
|
30
|
+
url = "#{@api_url_base}/jobs/#{id}/events"
|
31
|
+
if start
|
32
|
+
url << "?start=#{start}"
|
33
|
+
end
|
34
|
+
|
35
|
+
@https.get(url)
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'orchestrator_api'
|
3
|
+
s.version = '0.0.2'
|
4
|
+
s.summary = "Simple Ruby client library for PE Orchestration Services"
|
5
|
+
s.authors = "Thomas Linkin"
|
6
|
+
s.email = 'info@puppet.com'
|
7
|
+
s.files = `git ls-files`.split($/)
|
8
|
+
s.homepage = 'https://github.com/puppetlabs/orchestrator_api-ruby'
|
9
|
+
s.license = "apache"
|
10
|
+
s.require_paths = ["lib"]
|
11
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative '../lib/orchestrator_api'
|
3
|
+
|
4
|
+
describe Orchestrator_api do
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
config = {
|
8
|
+
'service-url' => 'https://orchestrator.example.lan:8143/orchestrator/v1',
|
9
|
+
'ca_cert' => '/etc/puppetlabs/puppet/ssl/certs/ca.pem',
|
10
|
+
'token' => 'myfaketoken'
|
11
|
+
}
|
12
|
+
|
13
|
+
@orchestrator_api = Orchestrator_api.new(config)
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#newobject" do
|
17
|
+
it "takes a configuration hash and returns a Orchestrator_api object" do
|
18
|
+
expect(@orchestrator_api).to be_an_instance_of Orchestrator_api
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
it "has methods with objects that are not nil" do
|
23
|
+
expect(@orchestrator_api.command).to be_truthy
|
24
|
+
expect(@orchestrator_api.jobs).to be_truthy
|
25
|
+
expect(@orchestrator_api.environments).to be_truthy
|
26
|
+
end
|
27
|
+
|
28
|
+
it "complains when a configuration value for 'server' is not provided" do
|
29
|
+
config = {
|
30
|
+
'ca_cert' => '/etc/puppetlabs/puppet/ssl/certs/ca.pem',
|
31
|
+
'token' => 'myfaketoken'
|
32
|
+
}
|
33
|
+
|
34
|
+
expect{ Orchestrator_api.new(config) }.to raise_error("Configuration error: 'service-url' must specify the server running the Orchestration services and cannot be empty")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "complains when a configuration value for 'ca_certificate_path' is not provided" do
|
38
|
+
config = {
|
39
|
+
'service-url' => 'https://orchestrator.example.lan:8143/orchestrator/v1',
|
40
|
+
'token' => 'myfaketoken'
|
41
|
+
}
|
42
|
+
|
43
|
+
expect{ Orchestrator_api.new(config) }.to raise_error("Configuration error: 'ca_cert' must specify a path to the CA certificate used for communications with the server and cannot be empty")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#get" do
|
48
|
+
it 'takes an endpoint and parses the response as JSON' do
|
49
|
+
stub_request(:get, "https://orchestrator.example.lan:8143/endpoint").
|
50
|
+
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby', 'X-Authentication'=>'myfaketoken'}).
|
51
|
+
to_return(:status => 200, :body => "{}", :headers => {})
|
52
|
+
|
53
|
+
expect(@orchestrator_api.get('https://orchestrator.example.lan:8143/endpoint')).to be_an_instance_of Hash
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#post" do
|
58
|
+
it 'takes an endpoint, endpoint, and parses the response as JSON' do
|
59
|
+
stub_request(:post, "https://orchestrator.example.lan:8143/endpoint").
|
60
|
+
with( :body => "{\"data\":\"atad\"}",
|
61
|
+
:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby', 'X-Authentication'=>'myfaketoken'}).
|
62
|
+
to_return(:status => 202, :body => "{}", :headers => {})
|
63
|
+
|
64
|
+
expect(@orchestrator_api.post('https://orchestrator.example.lan:8143/endpoint',{'data' => 'atad'})).to be_an_instance_of Hash
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Orchestrator_api::Command do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@url_base = "https://master.puppetlabs.vm:8143/orchestrator/v1"
|
7
|
+
|
8
|
+
config = {
|
9
|
+
'service-url' => 'https://orchestrator.example.lan:8143/orchestrator/v1',
|
10
|
+
'ca_cert' => '/etc/puppetlabs/puppet/ssl/certs/ca.pem',
|
11
|
+
'token' => 'myfaketoken'
|
12
|
+
}
|
13
|
+
|
14
|
+
@orchestrator = Orchestrator_api.new(config)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#newobject' do
|
18
|
+
it 'takes an orchestrator url and Orchestrator_api object and returns a Orchestrator_api::Command object' do
|
19
|
+
expect(@orchestrator.command).to be_an_instance_of Orchestrator_api::Command
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#deploy' do
|
24
|
+
it 'takes an environment and issues a deploy command' do
|
25
|
+
response = "{\n \"job\" : {\n \"id\" : \"https://orchestrator.example.lan:8143/orchestrator/v1/jobs/1\",\n \"name\" : \"1\"\n }\n}"
|
26
|
+
|
27
|
+
stub_request(:post, "https://orchestrator.example.lan:8143/orchestrator/v1/command/deploy").
|
28
|
+
with(:body => "{\"environment\":\"production\"}",
|
29
|
+
:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby', 'X-Authentication'=>'myfaketoken'}).
|
30
|
+
to_return(:status => 202, :body => response, :headers => {})
|
31
|
+
|
32
|
+
expect(@orchestrator.command.deploy('production')).to be_an_instance_of Hash
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'takes an environment and hash of additional details' do
|
36
|
+
response = "{\n \"job\" : {\n \"id\" : \"https://orchestrator.example.lan:8143/orchestrator/v1/jobs/1\",\n \"name\" : \"1\"\n }\n}"
|
37
|
+
|
38
|
+
stub_request(:post, "https://orchestrator.example.lan:8143/orchestrator/v1/command/deploy").
|
39
|
+
with(:body => "{\"scope\":{\"nodes\":[\"node1.example.lan\",\"node2.example.lan\"]},\"environment\":\"production\"}",
|
40
|
+
:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby', 'X-Authentication'=>'myfaketoken'}).
|
41
|
+
to_return(:status => 202, :body => response, :headers => {})
|
42
|
+
|
43
|
+
details = {
|
44
|
+
'scope' => {
|
45
|
+
'nodes' => ['node1.example.lan','node2.example.lan']
|
46
|
+
}
|
47
|
+
}
|
48
|
+
expect(@orchestrator.command.deploy('production',details)).to be_an_instance_of Hash
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#stop' do
|
53
|
+
it 'takes a valid job number and stops the job' do
|
54
|
+
response = "{\n \"job\" : {\n \"id\" : \"https://orchestrator.example.lan:8143/orchestrator/v1/jobs/1\",\n \"name\" : \"1\",\n \"nodes\" : {\n \"finished\" : 3\n }\n }\n}"
|
55
|
+
|
56
|
+
stub_request(:post, "https://orchestrator.example.lan:8143/orchestrator/v1/command/stop").
|
57
|
+
with(:body => "{\"job\":\"1\"}",
|
58
|
+
:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby', 'X-Authentication'=>'myfaketoken'}).
|
59
|
+
to_return(:status => 202, :body => response, :headers => {})
|
60
|
+
|
61
|
+
expect(@orchestrator.command.stop(1)).to be_an_instance_of Hash
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Orchestrator_api::Environments do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
config = {
|
7
|
+
'service-url' => 'https://orchestrator.example.lan:8143/orchestrator/v1',
|
8
|
+
'ca_cert' => '/etc/puppetlabs/puppet/ssl/certs/ca.pem',
|
9
|
+
'token' => 'myfaketoken'
|
10
|
+
}
|
11
|
+
|
12
|
+
@orchestrator = Orchestrator_api.new(config)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#newobject' do
|
16
|
+
it 'takes an orchestrator url and Orchestrator_api object and returns a Orchestrator_api::Environment object' do
|
17
|
+
expect(@orchestrator.environments).to be_an_instance_of Orchestrator_api::Environments
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#all' do
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#applications' do
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#instances' do
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Orchestrator_api::Error do
|
4
|
+
|
5
|
+
it 'is an child class of Exception' do
|
6
|
+
expect(Orchestrator_api::Error).to be <= Exception
|
7
|
+
end
|
8
|
+
|
9
|
+
%w(ValidationError UnknownJob UnknownEnvironment EmptyEnvironment EmptyTarget DependencyCycle PuppetdbError QueryError UnknownError UnauthorizedError).each do |name|
|
10
|
+
describe "::#{name}" do
|
11
|
+
klass = Orchestrator_api::Error.const_get(name)
|
12
|
+
it "should be a child class of Orchestrator_api::Error" do
|
13
|
+
expect(klass).to be <= Orchestrator_api::Error
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "::make_error_from_response" do
|
19
|
+
before(:all) do
|
20
|
+
class FakeResponse
|
21
|
+
attr_reader :code, :body
|
22
|
+
def initialize(error)
|
23
|
+
@code = '400'
|
24
|
+
@body = "{\"kind\":\"#{error}\"}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
%w(
|
30
|
+
puppetlabs.validators/validation-error:ValidationError
|
31
|
+
puppetlabs.orchestrator/unknown-job:UnknownJob
|
32
|
+
puppetlabs.orchestrator/unknown-environment:UnknownEnvironment
|
33
|
+
puppetlabs.orchestrator/empty-target:EmptyTarget
|
34
|
+
puppetlabs.orchestrator/dependency-cycle:DependencyCycle
|
35
|
+
puppetlabs.orchestrator/puppetdb-error:PuppetdbError
|
36
|
+
puppetlabs.orchestrator/query-error:QueryError
|
37
|
+
puppetlabs.orchestrator/unknown-error:UnknownError
|
38
|
+
puppetlabs.orchestrator/not-permitted:UnauthorizedError
|
39
|
+
).each do |item|
|
40
|
+
error = item.split(':')
|
41
|
+
it "creates an exception based on an \"#{error.first}\" error response from the server" do
|
42
|
+
res = FakeResponse.new(error.first)
|
43
|
+
klass = Orchestrator_api::Error.const_get(error[1])
|
44
|
+
expect( Orchestrator_api::Error.make_error_from_response(res) ).to be_an_instance_of klass
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it "creates an Orchestrator_api::Error::EndpointError exception when the response from the server isn't parsable JSON" do
|
49
|
+
res = FakeResponse.new("makesforunparsablejson\"")
|
50
|
+
expect( Orchestrator_api::Error.make_error_from_response(res)).to be_an_instance_of Orchestrator_api::Error::EndpointError
|
51
|
+
end
|
52
|
+
|
53
|
+
it "creates an Orchestrator_api::Error::EndpointError exception when it does not understand the error from the service" do
|
54
|
+
res = FakeResponse.new('notarealerror')
|
55
|
+
expect( Orchestrator_api::Error.make_error_from_response(res)).to be_an_instance_of Orchestrator_api::Error::EndpointError
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Orchestrator_api::Jobs do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
config = {
|
7
|
+
'service-url' => 'https://orchestrator.example.lan:8143/orchestrator/v1',
|
8
|
+
'ca_cert' => '/etc/puppetlabs/puppet/ssl/certs/ca.pem',
|
9
|
+
'token' => 'myfaketoken'
|
10
|
+
}
|
11
|
+
|
12
|
+
@orchestrator = Orchestrator_api.new(config)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#newobject' do
|
16
|
+
it 'takes an orchestrator url and Orchestrator_api object and returns a Orchestrator_api::Jobs object' do
|
17
|
+
expect(@orchestrator.jobs).to be_an_instance_of Orchestrator_api::Jobs
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#all' do
|
22
|
+
response = "{\n \"items\" : [ {\n \"report\" : {\n \"id\" : \"https://orchestrator.example.lan:8143/orchestrator/v1/jobs/58/report\"\n },\n \"name\" : \"58\",\n \"events\" : {\n \"id\" : \"https://orchestrator.example.lan:8143/orchestrator/v1/jobs/58/events\"\n },\n \"state\" : \"failed\",\n \"nodes\" : {\n \"id\" : \"https://orchestrator.example.lan:8143/orchestrator/v1/jobs/58/nodes\"\n },\n \"id\" : \"https://orchestrator.example.lan:8143/orchestrator/v1/jobs/58\",\n \"environment\" : {\n \"name\" : \"production\"\n },\n \"options\" : {\n \"concurrency\" : null,\n \"noop\" : false,\n \"trace\" : false,\n \"debug\" : false,\n \"scope\" : {\n \"whole_environment\" : true\n },\n \"enforce_environment\" : true,\n \"environment\" : \"production\",\n \"evaltrace\" : false,\n \"target\" : \"whole environment\"\n },\n \"timestamp\" : \"2016-09-20T19:01:18Z\",\n \"owner\" : {\n \"id\" : \"80f8475c-d8b5-4dfd-b567-3cae4ef7a3a7\",\n \"login\" : \"tom\"\n },\n \"node_count\" : 2\n } ]\n}"
|
23
|
+
|
24
|
+
it 'optionally accepts a number to limit results' do
|
25
|
+
stub_request(:get, "https://orchestrator.example.lan:8143/orchestrator/v1/jobs?limit=1").
|
26
|
+
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby', 'X-Authentication'=>'myfaketoken'}).
|
27
|
+
to_return(:status => 200, :body => response, :headers => {})
|
28
|
+
|
29
|
+
expect(@orchestrator.jobs.all(1)).to be_an_instance_of Hash
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'can return results without a limit' do
|
33
|
+
response = "{\n \"items\" : [ {\n \"report\" : {\n \"id\" : \"https://orchestrator.example.lan:8143/orchestrator/v1/jobs/58/report\"\n },\n \"name\" : \"58\",\n \"events\" : {\n \"id\" : \"https://orchestrator.example.lan:8143/orchestrator/v1/jobs/58/events\"\n },\n \"state\" : \"failed\",\n \"nodes\" : {\n \"id\" : \"https://orchestrator.example.lan:8143/orchestrator/v1/jobs/58/nodes\"\n },\n \"id\" : \"https://orchestrator.example.lan:8143/orchestrator/v1/jobs/58\",\n \"environment\" : {\n \"name\" : \"production\"\n },\n \"options\" : {\n \"concurrency\" : null,\n \"noop\" : false,\n \"trace\" : false,\n \"debug\" : false,\n \"scope\" : {\n \"whole_environment\" : true\n },\n \"enforce_environment\" : true,\n \"environment\" : \"production\",\n \"evaltrace\" : false,\n \"target\" : \"whole environment\"\n },\n \"timestamp\" : \"2016-09-20T19:01:18Z\",\n \"owner\" : {\n \"id\" : \"80f8475c-d8b5-4dfd-b567-3cae4ef7a3a7\",\n \"login\" : \"tom\"\n },\n \"node_count\" : 2\n } ]\n}"
|
34
|
+
|
35
|
+
stub_request(:get, "https://orchestrator.example.lan:8143/orchestrator/v1/jobs").
|
36
|
+
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby', 'X-Authentication'=>'myfaketoken'}).
|
37
|
+
to_return(:status => 200, :body => response, :headers => {})
|
38
|
+
|
39
|
+
expect(@orchestrator.jobs.all).to be_an_instance_of Hash
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#details' do
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#nodes' do
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '#report' do
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#events' do
|
53
|
+
response = "{\n \"next-events\" : {\n \"id\" : \"https://orchestrator.example.lan:8143/orchestrator/v1/jobs/1/events?start=7\"\n },\n \"items\" : [ {\n \"type\" : \"node_finished\",\n \"timestamp\" : \"2016-08-15T21:35:26Z\",\n \"details\" : {\n \"node\" : \"orchestrator.example.lan\",\n \"detail\" : {\n \"hash\" : \"c02e060e07da2a0e3ecdbad0e46a265ca22f433d\",\n \"noop\" : false,\n \"status\" : \"unchanged\",\n \"metrics\" : {\n \"corrective_change\" : 0,\n \"out_of_sync\" : 3,\n \"restarted\" : 0,\n \"skipped\" : 0,\n \"total\" : 789,\n \"changed\" : 0,\n \"scheduled\" : 0,\n \"failed_to_restart\" : 0,\n \"failed\" : 0\n },\n \"report-url\" : \"https://orchestrator.example.lan/#/cm/report/c02e060e07da2a0e3ecdbad0e46a265ca22f433d\",\n \"environment\" : \"production\",\n \"configuration_version\" : \"1471296883\"\n }\n },\n \"message\" : \"Finished puppet run on orchestrator.example.lan - Success! \",\n \"id\" : \"6\"\n } ]\n}"
|
54
|
+
|
55
|
+
it 'takes a job number and accespts a start number to retrieve latest results' do
|
56
|
+
stub_request(:get, "https://orchestrator.example.lan:8143/orchestrator/v1/jobs/1/events?start=10").
|
57
|
+
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby', 'X-Authentication'=>'myfaketoken'}).
|
58
|
+
to_return(:status => 200, :body => response, :headers => {})
|
59
|
+
|
60
|
+
expect(@orchestrator.jobs.events(1,10)).to be_an_instance_of Hash
|
61
|
+
end
|
62
|
+
it 'takes a job number but does not need a start number' do
|
63
|
+
response = "{\n \"next-events\" : {\n \"id\" : \"https://orchestrator.example.lan:8143/orchestrator/v1/jobs/1/events?start=7\"\n },\n \"items\" : [ {\n \"type\" : \"node_running\",\n \"timestamp\" : \"2016-08-15T21:34:34Z\",\n \"details\" : {\n \"node\" : \"orchestrator.example.lan\",\n \"detail\" : {\n \"noop\" : false\n }\n },\n \"message\" : \"Started puppet run on orchestrator.example.lan ...\",\n \"id\" : \"1\"\n }, {\n \"type\" : \"node_running\",\n \"timestamp\" : \"2016-08-15T21:34:34Z\",\n \"details\" : {\n \"node\" : \"agent2.puppetlabs.vm\",\n \"detail\" : {\n \"noop\" : false\n }\n },\n \"message\" : \"Started puppet run on agent2.puppetlabs.vm ...\",\n \"id\" : \"2\"\n }, {\n \"type\" : \"node_running\",\n \"timestamp\" : \"2016-08-15T21:34:34Z\",\n \"details\" : {\n \"node\" : \"agent1.puppetlabs.vm\",\n \"detail\" : {\n \"noop\" : false\n }\n },\n \"message\" : \"Started puppet run on agent1.puppetlabs.vm ...\",\n \"id\" : \"3\"\n }, {\n \"type\" : \"node_finished\",\n \"timestamp\" : \"2016-08-15T21:34:47Z\",\n \"details\" : {\n \"node\" : \"agent1.puppetlabs.vm\",\n \"detail\" : {\n \"hash\" : \"20d60265af8bafc0ac1b99c1eb4b8bde411a81a3\",\n \"noop\" : false,\n \"status\" : \"unchanged\",\n \"metrics\" : {\n \"corrective_change\" : 0,\n \"out_of_sync\" : 0,\n \"restarted\" : 0,\n \"skipped\" : 0,\n \"total\" : 171,\n \"changed\" : 0,\n \"scheduled\" : 0,\n \"failed_to_restart\" : 0,\n \"failed\" : 0\n },\n \"report-url\" : \"https://orchestrator.example.lan/#/cm/report/20d60265af8bafc0ac1b99c1eb4b8bde411a81a3\",\n \"environment\" : \"production\",\n \"configuration_version\" : \"1471296882\"\n }\n },\n \"message\" : \"Finished puppet run on agent1.puppetlabs.vm - Success! \",\n \"id\" : \"4\"\n }, {\n \"type\" : \"node_finished\",\n \"timestamp\" : \"2016-08-15T21:34:47Z\",\n \"details\" : {\n \"node\" : \"agent2.puppetlabs.vm\",\n \"detail\" : {\n \"hash\" : \"d460f0a513b1e18d43f19867b269cf105772fc41\",\n \"noop\" : false,\n \"status\" : \"unchanged\",\n \"metrics\" : {\n \"corrective_change\" : 0,\n \"out_of_sync\" : 0,\n \"restarted\" : 0,\n \"skipped\" : 0,\n \"total\" : 171,\n \"changed\" : 0,\n \"scheduled\" : 0,\n \"failed_to_restart\" : 0,\n \"failed\" : 0\n },\n \"report-url\" : \"https://orchestrator.example.lan/#/cm/report/d460f0a513b1e18d43f19867b269cf105772fc41\",\n \"environment\" : \"production\",\n \"configuration_version\" : \"1471296882\"\n }\n },\n \"message\" : \"Finished puppet run on agent2.puppetlabs.vm - Success! \",\n \"id\" : \"5\"\n }, {\n \"type\" : \"node_finished\",\n \"timestamp\" : \"2016-08-15T21:35:26Z\",\n \"details\" : {\n \"node\" : \"orchestrator.example.lan\",\n \"detail\" : {\n \"hash\" : \"c02e060e07da2a0e3ecdbad0e46a265ca22f433d\",\n \"noop\" : false,\n \"status\" : \"unchanged\",\n \"metrics\" : {\n \"corrective_change\" : 0,\n \"out_of_sync\" : 3,\n \"restarted\" : 0,\n \"skipped\" : 0,\n \"total\" : 789,\n \"changed\" : 0,\n \"scheduled\" : 0,\n \"failed_to_restart\" : 0,\n \"failed\" : 0\n },\n \"report-url\" : \"https://orchestrator.example.lan/#/cm/report/c02e060e07da2a0e3ecdbad0e46a265ca22f433d\",\n \"environment\" : \"production\",\n \"configuration_version\" : \"1471296883\"\n }\n },\n \"message\" : \"Finished puppet run on orchestrator.example.lan - Success! \",\n \"id\" : \"6\"\n } ]\n}"
|
64
|
+
|
65
|
+
stub_request(:get, "https://orchestrator.example.lan:8143/orchestrator/v1/jobs/1/events").
|
66
|
+
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby', 'X-Authentication'=>'myfaketoken'}).
|
67
|
+
to_return(:status => 200, :body => response, :headers => {})
|
68
|
+
|
69
|
+
expect(@orchestrator.jobs.events(1)).to be_an_instance_of Hash
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: orchestrator_api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Thomas Linkin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-04 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email: info@puppet.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- CONTRIBUTING.md
|
20
|
+
- Gemfile
|
21
|
+
- LICENSE
|
22
|
+
- README.md
|
23
|
+
- lib/orchestrator_api.rb
|
24
|
+
- lib/orchestrator_api/command.rb
|
25
|
+
- lib/orchestrator_api/environments.rb
|
26
|
+
- lib/orchestrator_api/error.rb
|
27
|
+
- lib/orchestrator_api/jobs.rb
|
28
|
+
- orchestrator_api-ruby.gemspec
|
29
|
+
- spec/orchestrator_api_spec.rb
|
30
|
+
- spec/spec_helper.rb
|
31
|
+
- spec/unit/command_spec.rb
|
32
|
+
- spec/unit/environment_spec.rb
|
33
|
+
- spec/unit/error_spec.rb
|
34
|
+
- spec/unit/jobs_spec.rb
|
35
|
+
homepage: https://github.com/puppetlabs/orchestrator_api-ruby
|
36
|
+
licenses:
|
37
|
+
- apache
|
38
|
+
metadata: {}
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
requirements: []
|
54
|
+
rubyforge_project:
|
55
|
+
rubygems_version: 2.2.3
|
56
|
+
signing_key:
|
57
|
+
specification_version: 4
|
58
|
+
summary: Simple Ruby client library for PE Orchestration Services
|
59
|
+
test_files: []
|