rundeck 0.0.1.pre

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.
@@ -0,0 +1,42 @@
1
+ module Rundeck
2
+ # Defines constants and methods related to configuration.
3
+ module Configuration
4
+ # An array of valid keys in the options hash when configuring a Rundeck::API.
5
+ VALID_OPTIONS_KEYS = [:endpoint, :api_token, :user_agent].freeze
6
+
7
+ # The user agent that will be sent to the API endpoint if none is set.
8
+ DEFAULT_USER_AGENT = "Rundeck Ruby Gem #{Rundeck::VERSION}".freeze
9
+
10
+ # @private
11
+ attr_accessor(*VALID_OPTIONS_KEYS)
12
+
13
+ # Sets all configuration options to their default values
14
+ # when this module is extended.
15
+ def self.extended(base)
16
+ base.reset
17
+ end
18
+
19
+ # Convenience method to allow configuration options to be set in a block.
20
+ def configure
21
+ yield self
22
+ end
23
+
24
+ # Creates a hash of options and their values.
25
+ def options
26
+ VALID_OPTIONS_KEYS.reduce({}) do |option, key|
27
+ option.merge!(key => send(key))
28
+ end
29
+ end
30
+
31
+ def endpoint=(endpoint)
32
+ @endpoint = "#{endpoint}/api/12"
33
+ end
34
+
35
+ # Resets all configuration options to the defaults.
36
+ def reset
37
+ self.endpoint = ENV['RUNDECK_API_ENDPOINT']
38
+ self.api_token = ENV['RUNDECK_API_TOKEN']
39
+ self.user_agent = DEFAULT_USER_AGENT
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,45 @@
1
+ module Rundeck
2
+ module Error
3
+ # Custom error class for rescuing from all Rundeck errors.
4
+ class Error < StandardError; end
5
+
6
+ # Raise when attributes are missing.
7
+ class MissingAttributes < Error; end
8
+
9
+ # Raise when attributes are invalid.
10
+ class InvalidAttributes < Error; end
11
+
12
+ # Raised when API endpoint credentials not configured.
13
+ class MissingCredentials < Error; end
14
+
15
+ # Raised when impossible to parse response body.
16
+ class Parsing < Error; end
17
+
18
+ # Raised when API endpoint returns the HTTP status code 400.
19
+ class BadRequest < Error; end
20
+
21
+ # Raised when API endpoint returns the HTTP status code 401.
22
+ class Unauthorized < Error; end
23
+
24
+ # Raised when API endpoint returns the HTTP status code 403.
25
+ class Forbidden < Error; end
26
+
27
+ # Raised when API endpoint returns the HTTP status code 404.
28
+ class NotFound < Error; end
29
+
30
+ # Raised when API endpoint returns the HTTP status code 405.
31
+ class MethodNotAllowed < Error; end
32
+
33
+ # Raised when API endpoint returns the HTTP status code 409.
34
+ class Conflict < Error; end
35
+
36
+ # Raised when API endpoint returns the HTTP status code 500.
37
+ class InternalServerError < Error; end
38
+
39
+ # Raised when API endpoint returns the HTTP status code 502.
40
+ class BadGateway < Error; end
41
+
42
+ # Raised when API endpoint returns the HTTP status code 503.
43
+ class ServiceUnavailable < Error; end
44
+ end
45
+ end
@@ -0,0 +1,24 @@
1
+ module Rundeck
2
+ # Converts hashes to the objects.
3
+ class ObjectifiedHash
4
+ # Creates a new ObjectifiedHash object.
5
+ def initialize(hash)
6
+ @hash = hash
7
+ @data = hash.each_with_object({}) do |(key, value), data|
8
+ value = ObjectifiedHash.new(value) if value.is_a? Hash
9
+ data[key.to_s.downcase] = value
10
+ data
11
+ end
12
+ end
13
+
14
+ def to_hash
15
+ @hash
16
+ end
17
+ alias_method :to_h, :to_hash
18
+
19
+ # Delegate to ObjectifiedHash.
20
+ def method_missing(key)
21
+ @data.key?(key.to_s) ? @data[key.to_s] : nil
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,93 @@
1
+ require 'httparty'
2
+ require 'libxml'
3
+
4
+ module Rundeck
5
+ # @private
6
+ class Request
7
+ include HTTParty
8
+
9
+ format :xml
10
+ attr_accessor :api_token
11
+
12
+ def get(path, options = {})
13
+ request_settings(options)
14
+ validate self.class.get(path, options)
15
+ end
16
+
17
+ def post(path, options = {})
18
+ request_settings(options, path)
19
+ validate self.class.post(path, options)
20
+ end
21
+
22
+ def put(path, options = {})
23
+ request_settings(options)
24
+ validate self.class.put(path, options)
25
+ end
26
+
27
+ def delete(path, options = {})
28
+ request_settings(options)
29
+ validate self.class.delete(path, options)
30
+ end
31
+
32
+ # Checks the response code for common errors.
33
+ # Returns parsed response for successful requests.
34
+ def validate(response)
35
+ case response.code
36
+ when 400 then fail Error::BadRequest, error_message(response)
37
+ when 401 then fail Error::Unauthorized, error_message(response)
38
+ when 403 then fail Error::Forbidden, error_message(response)
39
+ when 404 then fail Error::NotFound, error_message(response)
40
+ when 405 then fail Error::MethodNotAllowed, error_message(response)
41
+ when 409 then fail Error::Conflict, error_message(response)
42
+ when 500 then fail Error::InternalServerError, error_message(response)
43
+ when 502 then fail Error::BadGateway, error_message(response)
44
+ when 503 then fail Error::ServiceUnavailable, error_message(response)
45
+ end
46
+
47
+ response.parsed_response
48
+ end
49
+
50
+ # Sets a base_uri and default_params for requests.
51
+ # @raise [Error::MissingCredentials] if endpoint not set.
52
+ def set_request_defaults(endpoint, api_token)
53
+ unless endpoint
54
+ fail Error::MissingCredentials, 'Please set an endpoint to API'
55
+ end
56
+ @api_token = api_token
57
+
58
+ self.class.base_uri endpoint
59
+ end
60
+
61
+ private
62
+
63
+ def request_settings(options, path = nil)
64
+ api_token_header(options, path)
65
+ accept_header(options)
66
+ end
67
+
68
+ # Sets a PRIVATE-TOKEN header for requests.
69
+ # @raise [Error::MissingCredentials] if api_token not set.
70
+ def api_token_header(options, path = nil)
71
+ return nil if path == '/j_security_check'
72
+ unless @api_token
73
+ fail Error::MissingCredentials, 'Please set a api_token for user'
74
+ end
75
+ options[:headers] = {} if options[:headers].nil?
76
+ options[:headers].merge!('X-Rundeck-Auth-Token' => @api_token)
77
+ end
78
+
79
+ def accept_header(options)
80
+ return nil if options[:headers].nil?
81
+
82
+ unless options[:headers].include?('Accept')
83
+ options[:headers].merge!('Accept' => 'application/xml')
84
+ end
85
+ end
86
+
87
+ def error_message(response)
88
+ "Server responded with code #{response.code}, " \
89
+ "message: #{response.parsed_response['result']['error'][1]['message']}. " \
90
+ "Request URI: #{response.request.base_uri}#{response.request.path}"
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,3 @@
1
+ module Rundeck
2
+ VERSION = '0.0.1.pre'
3
+ end
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rundeck/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'rundeck'
8
+ spec.version = Rundeck::VERSION
9
+ spec.authors = ['Drew A. Blessing']
10
+ spec.email = ['drew.blessing@mac.com']
11
+ spec.description = 'Ruby client for Rundeck API'
12
+ spec.summary = 'A ruby wrapper for the Rundeck API'
13
+ spec.homepage = 'https://github.com/dblessing/rundeck'
14
+ spec.license = 'BSD-2-Clause'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(/^(test|spec|features)\//)
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_runtime_dependency 'httparty'
22
+ spec.add_runtime_dependency 'libxml-ruby'
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.3'
25
+ spec.add_development_dependency 'rake'
26
+ spec.add_development_dependency 'rspec', '~> 3.1.0'
27
+ spec.add_development_dependency 'rspec-core', '~> 3.1.2'
28
+ spec.add_development_dependency 'rspec-its', '~> 1.0.0'
29
+ spec.add_development_dependency 'webmock'
30
+ spec.add_development_dependency 'codeclimate-test-reporter'
31
+ spec.add_development_dependency 'guard-rspec', '~> 4.3.0'
32
+ spec.add_development_dependency 'guard-rubocop'
33
+ end
File without changes
@@ -0,0 +1,17 @@
1
+ <joblist>
2
+ <job>
3
+ <id>c07518ef-b697-4792-9a59-5b4f08855b67</id>
4
+ <loglevel>INFO</loglevel>
5
+ <sequence keepgoing='false' strategy='node-first'>
6
+ <command>
7
+ <exec>echo 'Hello world!'</exec>
8
+ </command>
9
+ </sequence>
10
+ <description>Well, Hello World</description>
11
+ <name>Hello World</name>
12
+ <context>
13
+ <project>My_Project</project>
14
+ </context>
15
+ <uuid>c07518ef-b697-4792-9a59-5b4f08855b67</uuid>
16
+ </job>
17
+ </joblist>
@@ -0,0 +1,31 @@
1
+ <result success='true' apiversion='12'>
2
+ <executions count='2'>
3
+ <execution id='2' href='http://localhost:4440/execution/follow/2' status='aborted' project='My_Project'>
4
+ <user>admin</user>
5
+ <date-started unixtime='1409608790308'>2014-09-01T21:59:50Z</date-started>
6
+ <date-ended unixtime='1409608832192'>2014-09-01T22:00:32Z</date-ended>
7
+ <abortedby>admin</abortedby>
8
+ <job id='c07518ef-b697-4792-9a59-5b4f08855b67'>
9
+ <name>Job 1</name>
10
+ <group></group>
11
+ <project>My_Project</project>
12
+ <description>test</description>
13
+ </job>
14
+ <description>echo 'Hello world'</description>
15
+ <argstring />
16
+ </execution>
17
+ <execution id='1' href='http://dc-mb-am-009.bucklehq.com:4440/execution/follow/1' status='success' project='My_Project'>
18
+ <user>admin</user>
19
+ <date-started unixtime='1409608610264'>2014-09-01T21:56:50Z</date-started>
20
+ <date-ended unixtime='1409608781159'>2014-09-01T21:59:41Z</date-ended>
21
+ <job id='c07518ef-b697-4792-9a59-5b4f08855b67'>
22
+ <name>Job 1</name>
23
+ <group></group>
24
+ <project>My_Project</project>
25
+ <description>Awesome job 1</description>
26
+ </job>
27
+ <description>echo 'Hello world'</description>
28
+ <argstring />
29
+ </execution>
30
+ </executions>
31
+ </result>
@@ -0,0 +1,16 @@
1
+ <result success='true' apiversion='12'>
2
+ <executions count='1'>
3
+ <execution id='4' href='http://localhost:4440/execution/follow/4' status='running' project='My_Project'>
4
+ <user>admin</user>
5
+ <date-started unixtime='1411238010218'>2014-09-20T18:33:30Z</date-started>
6
+ <job id='2fe8bfbb-d12e-49b5-859b-7472cf1ed3a0' averageDuration='1003'>
7
+ <name>My_Job</name>
8
+ <group></group>
9
+ <project>My_Project</project>
10
+ <description>This is an awesome job</description>
11
+ </job>
12
+ <description>echo "foo"</description>
13
+ <argstring />
14
+ </execution>
15
+ </executions>
16
+ </result>
@@ -0,0 +1,23 @@
1
+ <result>
2
+ <succeeded count="1">
3
+ <job>
4
+ <id>c07518ef-b697-4792-9a59-5b4f08855b67</id>
5
+ <loglevel>INFO</loglevel>
6
+ <sequence keepgoing='false' strategy='node-first'>
7
+ <command>
8
+ <exec>echo 'Hello world!'</exec>
9
+ </command>
10
+ </sequence>
11
+ <description>Well, Hello World</description>
12
+ <name>Hello World</name>
13
+ <context>
14
+ <project>My_Project</project>
15
+ </context>
16
+ <uuid>c07518ef-b697-4792-9a59-5b4f08855b67</uuid>
17
+ </job>
18
+ </succeeded>
19
+ <failed count="0">
20
+ </failed>
21
+ <skipped count="0">
22
+ </skipped>
23
+ </result>
@@ -0,0 +1,14 @@
1
+ <jobs count='2'>
2
+ <job id='c07518ef-b697-4792-9a59-5b4f08855b67'>
3
+ <name>Job 1</name>
4
+ <group />
5
+ <project>My_Project</project>
6
+ <description>My first super awesome project</description>
7
+ </job>
8
+ <job id='9c922902-faa6-49cf-aa8d-4de3f5e1d3e1'>
9
+ <name>Job 2</name>
10
+ <group>My_Group</group>
11
+ <project>My_Project</project>
12
+ <description>My second project</description>
13
+ </job>
14
+ </jobs>
@@ -0,0 +1,32 @@
1
+ <joblist>
2
+ <job>
3
+ <id>c07518ef-b697-4792-9a59-5b4f08855b67</id>
4
+ <loglevel>INFO</loglevel>
5
+ <sequence keepgoing='false' strategy='node-first'>
6
+ <command>
7
+ <exec>echo 'hello world'</exec>
8
+ </command>
9
+ </sequence>
10
+ <description>Hello World</description>
11
+ <name>hello</name>
12
+ <context>
13
+ <project>my_project</project>
14
+ </context>
15
+ <uuid>c07518ef-b697-4792-9a59-5b4f08855b67</uuid>
16
+ </job>
17
+ <job>
18
+ <id>9c922902-faa6-49cf-aa8d-4de3f5e1d3e1</id>
19
+ <loglevel>INFO</loglevel>
20
+ <sequence keepgoing='false' strategy='node-first'>
21
+ <command>
22
+ <exec>echo 'foo'</exec>
23
+ </command>
24
+ </sequence>
25
+ <description>Foo Bar</description>
26
+ <name>foo</name>
27
+ <context>
28
+ <project>my_project</project>
29
+ </context>
30
+ <uuid>9c922902-faa6-49cf-aa8d-4de3f5e1d3e1</uuid>
31
+ </job>
32
+ </joblist>
@@ -0,0 +1,22 @@
1
+ - id: c07518ef-b697-4792-9a59-5b4f08855b67
2
+ project: Endeca
3
+ loglevel: INFO
4
+ sequence:
5
+ keepgoing: false
6
+ strategy: node-first
7
+ commands:
8
+ - exec: echo 'boom'
9
+ description: test
10
+ name: test
11
+ uuid: c07518ef-b697-4792-9a59-5b4f08855b67
12
+ - id: 9c922902-faa6-49cf-aa8d-4de3f5e1d3e1
13
+ project: Endeca
14
+ loglevel: INFO
15
+ sequence:
16
+ keepgoing: false
17
+ strategy: node-first
18
+ commands:
19
+ - exec: echo 'boom'
20
+ description: test
21
+ name: test
22
+ uuid: 9c922902-faa6-49cf-aa8d-4de3f5e1d3e1
@@ -0,0 +1 @@
1
+ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDaNirADvOztppFoWeH+fc+5u8zQwRNeiamCXbrcWEK+/fB+Jqf/k1CLlikvp0dt1TKFmQXYJc58ZHv2RRxfe1lMdnv04VDVTE6DoB1jShnv0VL/KN0J+bNm4WLCyJ1NEdhnlZN5nt9U0RkYSG8hcuvREJ5R/5miCCRMSeH4hDmT22v7mV+qZQArRprs1EOE3o25lItefa/c5h85uJMYgnMIMQjT4hhr1JhWg2NdpN08fwS0Bif3JKk3C1YOa3RgW+Wrd0hFQ5iPSBTgmAxWjdgMp1xT4Nw8V8kujiH1jBLfJTXwDgm7csS/Z0H+gORpz6qnkbWKcWVzX3izFLZlxUH user@example.com
@@ -0,0 +1,8 @@
1
+ <resource path='keys/path/to/my_key' type='file' url='http://localhost:4440/api/12/storage/keys/path/to/my_key' name='path/to/my_key'>
2
+ <resource-meta>
3
+ <Rundeck-content-type>application/octet-stream</Rundeck-content-type>
4
+ <Rundeck-content-size>1765</Rundeck-content-size>
5
+ <Rundeck-content-mask>content</Rundeck-content-mask>
6
+ <Rundeck-key-type>private</Rundeck-key-type>
7
+ </resource-meta>
8
+ </resource>
@@ -0,0 +1,7 @@
1
+ <resource path='keys/key1' type='file' url='http://localhost:4440/api/12/storage/keys/key1' name='key1'>
2
+ <resource-meta>
3
+ <Rundeck-content-type>application/pgp-keys</Rundeck-content-type>
4
+ <Rundeck-content-size>406</Rundeck-content-size>
5
+ <Rundeck-key-type>public</Rundeck-key-type>
6
+ </resource-meta>
7
+ </resource>
@@ -0,0 +1,27 @@
1
+ <resource path='keys' type='directory' url='http://localhost:4440/api/12/storage/keys'>
2
+ <contents count='4'>
3
+ <resource path='keys/key1' type='file' url='http://localhost:4440/api/12/storage/keys/key1' name='key1'>
4
+ <resource-meta>
5
+ <Rundeck-content-type>application/pgp-keys</Rundeck-content-type>
6
+ <Rundeck-content-size>406</Rundeck-content-size>
7
+ <Rundeck-key-type>public</Rundeck-key-type>
8
+ </resource-meta>
9
+ </resource>
10
+ <resource path='keys/key2' type='file' url='http://localhost:4440/api/12/storage/keys/key2' name='key2'>
11
+ <resource-meta>
12
+ <Rundeck-content-type>application/octet-stream</Rundeck-content-type>
13
+ <Rundeck-content-size>1765</Rundeck-content-size>
14
+ <Rundeck-content-mask>content</Rundeck-content-mask>
15
+ <Rundeck-key-type>private</Rundeck-key-type>
16
+ </resource-meta>
17
+ </resource>
18
+ <resource path='keys/key3' type='file' url='http://localhost:4440/api/12/storage/keys/key3' name='key3'>
19
+ <resource-meta>
20
+ <Rundeck-content-type>application/octet-stream</Rundeck-content-type>
21
+ <Rundeck-content-size>1765</Rundeck-content-size>
22
+ <Rundeck-content-mask>content</Rundeck-content-mask>
23
+ <Rundeck-key-type>private</Rundeck-key-type>
24
+ </resource-meta>
25
+ </resource>
26
+ </contents>
27
+ </resource>