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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3458d53ff90537da707c22e709751439806a0e3c
4
+ data.tar.gz: 3d4414a87b8d98dce1bceb9d66c46ad2d5583e66
5
+ SHA512:
6
+ metadata.gz: 6fc35d21ed3a2138a6ac831b349ea5ecb23b16a15db24a254a1c7487d49adb333de9095b2a2235a0f23d66a1868406a01411e8f55d410418195e34ee7626eebb
7
+ data.tar.gz: 9629f50b5e04535b8e84b8095a4c5711186bf2af58ebb9a61dfa822f8b2752f09e854a81b86d509a9d0321167e75b3ebcdac2097d4906a8db5f15c6e004e8e66
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea
19
+ .ruby-version
20
+ .ruby-gemset
21
+ .idea/
@@ -0,0 +1,4 @@
1
+ # Prefer single quotes
2
+ StringLiterals:
3
+ EnforcedStyle: single_quotes
4
+ Enabled: true
@@ -0,0 +1,7 @@
1
+ # This is the configuration used to check the rubocop source code.
2
+
3
+ inherit_from: .rubocop_todo.yml
4
+
5
+ # Lots of crappy false-positives from this cop. Go home!
6
+ GuardClause:
7
+ Enabled: false
@@ -0,0 +1,35 @@
1
+ # This configuration was generated by `rubocop --auto-gen-config`
2
+ # on 2014-09-01 15:55:09 -0500 using RuboCop version 0.25.0.
3
+ # The point is for the user to remove these configuration records
4
+ # one by one as the offenses are removed from the code base.
5
+ # Note that changes in the inspected code, or installation of new
6
+ # versions of RuboCop, may require this file to be generated again.
7
+
8
+ # Offense count: 1
9
+ Metrics/CyclomaticComplexity:
10
+ Max: 10
11
+
12
+ # Offense count: 1
13
+ # Configuration parameters: AllowURI.
14
+ Metrics/LineLength:
15
+ Max: 159
16
+
17
+ # Offense count: 1
18
+ # Configuration parameters: CountComments.
19
+ Metrics/MethodLength:
20
+ Max: 12
21
+
22
+ # Offense count: 2
23
+ Style/Documentation:
24
+ Severity: warning
25
+ Enabled: false
26
+
27
+ # Offense count: 1
28
+ # Configuration parameters: MaxSlashes.
29
+ Style/RegexpLiteral:
30
+ Enabled: false
31
+
32
+ # Offense count: 1
33
+ # Cop supports --auto-correct.
34
+ Style/SpecialGlobalVars:
35
+ Enabled: false
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1
6
+ script:
7
+ - bundle exec rake
8
+ # - bundle exec rubocop
9
+ addons:
10
+ code_climate:
11
+ repo_token: 2aea108baa65d08ea87fb2371ec22a1741cb760027b8605bdabd5e153e595f4c
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rundeck.gemspec
4
+ gemspec
@@ -0,0 +1,16 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+ group :red_green_refactor, halt_on_fail: true do
4
+
5
+ guard :rspec, cmd: 'bundle exec rspec' do
6
+ watch(/^spec\/.+_spec\.rb$/)
7
+ watch(/^lib\/(.+)\.rb$/) { |m| "spec/lib/#{m[1]}_spec.rb" }
8
+ watch('spec/spec_helper.rb') { 'spec' }
9
+ end
10
+
11
+ guard :rubocop do
12
+ watch(/.+\.rb$/)
13
+ watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
14
+ end
15
+
16
+ end
@@ -0,0 +1,10 @@
1
+ Copyright (c) 2014, Drew A. Blessing
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5
+
6
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7
+
8
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9
+
10
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,49 @@
1
+ # Rundeck Ruby Gem
2
+
3
+ ## Work in progress. Not yet stable
4
+
5
+ **Note: Although the git repository is called 'rundeck-ruby', the gem is called 'Rundeck' on Rubygems.org. There is another gem called 'rundeck-ruby' on Rubygems.org and it is not affiliated with this project.**
6
+
7
+ [![Build Status](https://travis-ci.org/dblessing/rundeck-ruby.svg?branch=master)](https://travis-ci.org/dblessing/rundeck-ruby)
8
+ [![Dependency Status](https://gemnasium.com/dblessing/rundeck-ruby.svg)](https://gemnasium.com/dblessing/rundeck-ruby)
9
+ [![Code Climate](https://codeclimate.com/github/dblessing/rundeck-ruby/badges/gpa.svg)](https://codeclimate.com/github/dblessing/rundeck-ruby)
10
+ [![Test Coverage](https://codeclimate.com/github/dblessing/rundeck-ruby/badges/coverage.svg)](https://codeclimate.com/github/dblessing/rundeck-ruby)
11
+
12
+ A Ruby wrapper around the Rundeck API.
13
+
14
+ This library will strive to achieve reliable,
15
+ 100% coverage of the Rundeck API. Please submit an issue if you find a bug and feel
16
+ free to submit a pull request to contribute fixes or new features.
17
+
18
+ The layout and the code in this library is inspired by https://github.com/NARKOZ/gitlab.
19
+ NARKOZ does a fantastic job of keeping code simple and achieving API feature parity.
20
+
21
+ ## Installation
22
+
23
+ Add this line to your application's Gemfile:
24
+
25
+ gem 'rundeck'
26
+
27
+ And then execute:
28
+
29
+ $ bundle
30
+
31
+ Or install it yourself as:
32
+
33
+ $ gem install rundeck
34
+
35
+ ## Usage
36
+
37
+ TODO: Write usage instructions here
38
+
39
+ ## Contributing
40
+
41
+ 1. Fork it
42
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
43
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
44
+ 4. Push to the branch (`git push origin my-new-feature`)
45
+ 5. Create new Pull Request
46
+
47
+ ## License
48
+
49
+ Released under the BSD 2-clause license. See LICENSE.txt for details.
@@ -0,0 +1,8 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |spec|
5
+ spec.rspec_opts = ['--color', '--format d']
6
+ end
7
+
8
+ task default: :spec
@@ -0,0 +1,29 @@
1
+ require 'rundeck/version'
2
+ require 'rundeck/configuration'
3
+ require 'rundeck/error'
4
+ require 'rundeck/objectified_hash'
5
+ require 'rundeck/request'
6
+ require 'rundeck/api'
7
+ require 'rundeck/client'
8
+
9
+ module Rundeck
10
+ extend Configuration
11
+
12
+ # Alias for Rundeck::Client.new
13
+ #
14
+ # @return [Rundeck::Client]
15
+ def self.client(options = {})
16
+ Rundeck::Client.new(options)
17
+ end
18
+
19
+ # Delegate to Gitlab::Client
20
+ def self.method_missing(method, *args, &block)
21
+ return super unless client.respond_to?(method)
22
+ client.send(method, *args, &block)
23
+ end
24
+
25
+ # Delegate to Gitlab::Client
26
+ def self.respond_to?(method)
27
+ client.respond_to?(method) || super
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ module Rundeck
2
+ # @private
3
+ class API < Request
4
+ # @private
5
+ attr_accessor(*Configuration::VALID_OPTIONS_KEYS)
6
+
7
+ # Creates a new API.
8
+ # @raise [Error:MissingCredentials]
9
+ def initialize(options = {})
10
+ options = Rundeck.options.merge(options)
11
+ Configuration::VALID_OPTIONS_KEYS.each do |key|
12
+ send("#{key}=", options[key])
13
+ end
14
+ set_request_defaults @endpoint, @api_token
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ module Rundeck
2
+ # Wrapper for the Rundeck REST API.
3
+ class Client < API
4
+ Dir[File.expand_path('../client/*.rb', __FILE__)].each { |f| require f }
5
+
6
+ include Jobs
7
+ include Keys
8
+
9
+ def objectify(result)
10
+ if result.is_a?(Hash)
11
+ ObjectifiedHash.new(result)
12
+ elsif result.is_a? Array
13
+ result.map! { |e| ObjectifiedHash.new(e) }
14
+ elsif result.nil?
15
+ nil
16
+ else
17
+ fail Error::Parsing, "Couldn't parse a response body"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,119 @@
1
+ module Rundeck
2
+ class Client
3
+ # Defines methods related to projects.
4
+ module Jobs
5
+ # Gets a list of jobs for a specific project.
6
+ #
7
+ # @example
8
+ # Rundeck.jobs('project')
9
+ #
10
+ # @param [String] project Project name
11
+ # @param [Hash] options A set of options passed directly to HTTParty
12
+ # @return [Array<Rundeck::ObjectifiedHash>]
13
+ def jobs(project, options = {})
14
+ objectify get("/project/#{project}/jobs", options)['jobs']['job']
15
+ end
16
+
17
+ # Gets a single job by id
18
+ #
19
+ # @example Rundeck.job('c07518ef-b697-4792-9a59-5b4f08855b67')
20
+ #
21
+ # @param [String] id Job id
22
+ # @param [Hash] options A set of options passed directly to HTTParty
23
+ # @return [Rundeck::ObjectifiedHash]
24
+ def job(id, options = {})
25
+ objectify get("/job/#{id}", options)['joblist']['job']
26
+ end
27
+
28
+ # Delete a job
29
+ #
30
+ # @example
31
+ # Rundeck.delete_job('c07518ef-b697-4792-9a59-5b4f08855b67')
32
+ #
33
+ # @param [String] id Job id
34
+ # @param [Hash] options A set of options passed directly to HTTParty
35
+ # @return [Rundeck::ObjectifiedHash]
36
+ def delete_job(id, options = {})
37
+ delete("/job/#{id}", options)
38
+ end
39
+
40
+ # Get executions for a specific job
41
+ #
42
+ # @example
43
+ # Rundeck.job_executions('c07518ef-b697-4792-9a59-5b4f08855b67')
44
+ #
45
+ # @param [String] id Job id
46
+ # @param [Hash] options A set of options passed directly to HTTParty
47
+ # @return [Rundeck::ObjectifiedHash]
48
+ def job_executions(id, options = {})
49
+ r = get("/job/#{id}/executions", options)['result']['executions']['execution']
50
+ objectify r
51
+ end
52
+
53
+ # Run a job
54
+ #
55
+ # @example
56
+ # Rundeck.run_job('c07518ef-b697-4792-9a59-5b4f08855b67', 'DEBUG')
57
+ #
58
+ # @param [String] id Job id
59
+ # @param [Hash] options A set of options passed directly to HTTParty
60
+ # @return [Rundeck::ObjectifiedHash]
61
+ def run_job(id, options = {})
62
+ objectify post("/job/#{id}/executions", options)['result']['executions']['execution']
63
+ end
64
+
65
+ # Import a job or multiple jobs
66
+ #
67
+ # @example
68
+ # job = "- id: c07518ef-b697-4792-9a59-5b4f08855b67
69
+ # project: Endeca
70
+ # ..."
71
+ # Rundeck.import_jobs(job)
72
+ #
73
+ # @example
74
+ # job = "<joblist>
75
+ # <job>
76
+ # <id>c07518ef-b697-4792-9a59-5b4f08855b67</id>
77
+ # ..."
78
+ # Rundeck.import_jobs(job, 'xml')
79
+ #
80
+ # @param [String] content The job definition(s) as yaml or xml
81
+ # @param [String] format The import format. 'yaml|xml', defaults to 'yaml'
82
+ # @param [Hash] options A set of options passed directly to HTTParty
83
+ # @return [Rundeck::ObjectifiedHash]
84
+ def import_jobs(content, format = 'yaml', options = {})
85
+ unless format =~ /yaml|xml/
86
+ fail Error::InvalidAttributes, 'format must be yaml or xml'
87
+ end
88
+ options[:headers] = {} if options[:headers].nil?
89
+ options[:headers].merge!(
90
+ 'Content-Type' => 'application/x-www-form-urlencoded')
91
+ options[:body] = "xmlBatch=#{content}"
92
+ options[:query] = {} if options[:query].nil?
93
+ options[:query]['format'] = format
94
+
95
+ objectify post('/jobs/import', options)['result']
96
+ end
97
+
98
+ # Export jobs to yaml or xml format
99
+ #
100
+ # @example
101
+ # Rundeck.export_jobs('project')
102
+ #
103
+ # @param [String] project Project name
104
+ # @param [String] format The export format. 'yaml|xml', defaults to 'yaml'
105
+ # @param [Hash] options A set of options passed directly to HTTParty
106
+ # @return [String]
107
+ def export_jobs(project, format = 'yaml', options = {})
108
+ unless format =~ /yaml|xml/
109
+ fail Error::InvalidAttributes, 'format must be yaml or xml'
110
+ end
111
+ options[:query] = {} if options[:query].nil?
112
+ options[:query].merge!('project' => project, 'format' => format)
113
+ options[:format] = format
114
+
115
+ get('/jobs/export', options)
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,177 @@
1
+ module Rundeck
2
+ class Client
3
+ # Defines methods related to projects.
4
+ module Keys
5
+ STORAGE_KEYS_PATH = '/storage/keys'
6
+
7
+ # Gets a list of keys at a specific path.
8
+ #
9
+ # @example
10
+ # Rundeck.keys('path')
11
+ #
12
+ # @param [String] path A key storage path
13
+ # @param [Hash] options A set of options passed directly to HTTParty
14
+ # @return [Array<Rundeck::ObjectifiedHash>]
15
+ def keys(path = '', options = {})
16
+ r = get("#{STORAGE_KEYS_PATH}/#{path}", options)
17
+
18
+ # # In case a user provides a direct path to a key, error.
19
+ if r['resource']['contents']
20
+ objectify r['resource']['contents']['resource']
21
+ else
22
+ fail Error::InvalidAttributes,
23
+ 'Please provide a key storage path that ' \
24
+ 'isn\'t a direct path to a key'
25
+ end
26
+ end
27
+
28
+ # Get a single key's metadata
29
+ #
30
+ # @example
31
+ # Rundeck.key_metadata('path/to/key1')
32
+ #
33
+ # @param [String] path A key storage path, including key name
34
+ # @param [Hash] options A set of options passed directly to HTTParty
35
+ # @return [Rundeck::ObjectifiedHash]
36
+ def key_metadata(path, options = {})
37
+ r = get("#{STORAGE_KEYS_PATH}/#{path}", options)
38
+
39
+ # In case a user provides a key path instead of a path to a single key.
40
+ if r['resource']['contents']
41
+ fail Error::InvalidAttributes,
42
+ 'Please provide a key storage path that ' \
43
+ 'is a direct path to a key'
44
+ else
45
+ objectify r['resource']['resource_meta']
46
+ end
47
+ end
48
+
49
+ # Get the contents of a key. Only allowed for public keys.
50
+ # Note: This method returns a raw string of the public key,
51
+ # not at ObjectifiedHash.
52
+ #
53
+ # @example
54
+ # Rundeck.key_contents('path/to/key1')
55
+ #
56
+ # @param [String] path A key storage path, including key name
57
+ # @param [Hash] options A set of options passed directly to HTTParty
58
+ # @return [Rundeck::ObjectifiedHash]
59
+ def key_contents(path, options = {})
60
+ # Check if key exists first. Otherwise we could return some
61
+ # weird strings. Also, raise error if user is trying to get a
62
+ # private key.
63
+ if key_metadata(path, options).rundeck_key_type == 'private'
64
+ fail Error::Unauthorized,
65
+ 'You are not allowed to retrieve the contents of a private key'
66
+ end
67
+
68
+ options.merge!(format: :plain,
69
+ headers: { 'Accept' => 'application/pgp-keys' })
70
+ key = get("#{STORAGE_KEYS_PATH}/#{path}", options)
71
+ objectify 'public_key' => key
72
+ end
73
+
74
+ # Create a private key
75
+ #
76
+ # @example
77
+ # key = "-----BEGIN RSA PRIVATE KEY-----\nProc-Type:..."
78
+ # Rundeck.create_private_key('path', key)
79
+ #
80
+ # @param [String] path A key storage path
81
+ # @param [String] key The entire private key value
82
+ # @param [Hash] options A set of options passed directory to HTTParty
83
+ # @return [Rundeck::ObjectifiedHash]
84
+ def create_private_key(path, key, options = {})
85
+ create_or_update_key(path, key, 'private', 'post', options)
86
+ end
87
+
88
+ # Update a private key
89
+ #
90
+ # @example
91
+ # key = "-----BEGIN RSA PRIVATE KEY-----\nProc-Type:..."
92
+ # Rundeck.update_private_key('path', key)
93
+ #
94
+ # @param [String] path A key storage path
95
+ # @param [String] key The entire private key value
96
+ # @param [Hash] options A set of options passed directory to HTTParty
97
+ # @return [Rundeck::ObjectifiedHash]
98
+ def update_private_key(path, key, options = {})
99
+ key_check(path, 'private', options)
100
+ create_or_update_key(path, key, 'public', 'put', options)
101
+ end
102
+
103
+ # Create a public key
104
+ #
105
+ # @example
106
+ # key = "ssh-rsa AAAA.....3MOj user@example.com"
107
+ # Rundeck.create_public_key('path/to/key', key)
108
+ #
109
+ # @param [String] path A key storage path
110
+ # @param [String] key The entire private key value
111
+ # @param [Hash] options A set of options passed directory to HTTParty
112
+ # @return [Array<Rundeck::ObjectifiedHash>]
113
+ def create_public_key(path, key, options = {})
114
+ create_or_update_key(path, key, 'public', 'post', options)
115
+ end
116
+
117
+ # Update a public key
118
+ #
119
+ # @example
120
+ # key = "ssh-rsa AAAA.....3MOj user@example.com"
121
+ # Rundeck.update_public_key('path/to/key', key)
122
+ #
123
+ # @param [String] path A key storage path
124
+ # @param [String] key The entire private key value
125
+ # @param [Hash] options A set of options passed directory to HTTParty
126
+ # @return [Array<Rundeck::ObjectifiedHash>]
127
+ def update_public_key(path, key, options = {})
128
+ key_check(path, 'public', options)
129
+ create_or_update_key(path, key, 'public', 'put', options)
130
+ end
131
+
132
+ # Delete a key
133
+ #
134
+ # @example
135
+ # Rundeck.delete_key('path/to/key')
136
+ #
137
+ # @param [String] path A key storage path
138
+ # @param [Hash] options A set of options passed directly to HTTParty
139
+ # @return [nil]
140
+ def delete_key(path, options = {})
141
+ delete("#{STORAGE_KEYS_PATH}/#{path}", options)
142
+ end
143
+
144
+ private
145
+
146
+ def key_check(path, type, options)
147
+ # Check existence and type
148
+ if key_metadata(path, options).rundeck_key_type != type
149
+ fail Error::NotFound,
150
+ "A #{type} key was not found at the specified path."
151
+ end
152
+ end
153
+
154
+ def create_or_update_key(path, key, type, method, options)
155
+ key_type_headers(type, options)
156
+ options.merge!(body: key)
157
+
158
+ if method == 'post'
159
+ objectify post("#{STORAGE_KEYS_PATH}/#{path}", options)['resource']['resource_meta']
160
+ elsif method == 'put'
161
+ objectify put("#{STORAGE_KEYS_PATH}/#{path}", options)['resource']['resource_meta']
162
+ end
163
+ end
164
+
165
+ def key_type_headers(type, options)
166
+ if type == 'private'
167
+ options.merge!(headers: { 'Content-Type' => 'application/octet-stream' })
168
+ elsif type == 'public'
169
+ options.merge!(headers: { 'Content-Type' => 'application/pgp-key' })
170
+ else
171
+ fail Error::InvalidAttributes,
172
+ 'Invalid key type specified. Must be public or private.'
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end