rundeck 0.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -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>