subordinate 0.0.1

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.
data/.gitignore ADDED
@@ -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
+ spec/cassettes
19
+ .DS_Store
20
+
21
+ spec/fixtures/authentications.yml
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in subordinate.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Jason Truluck
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # Subordinate
2
+
3
+ Subordinate is a api wrapper for the Jenkins API. It is not exhaustive at the moment and is being built out.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'subordinate'
10
+
11
+ And then execute:
12
+
13
+ $ bundle install
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install subordinate
18
+
19
+ ## Documentation
20
+
21
+ ## Configuration
22
+
23
+ Configuration allows for specifying your Jenkins instances variables
24
+
25
+ `domain` - The domain of your Jenkins server i.e. example.com
26
+
27
+ `subdomain` - The subdomain of your Jenkins server i.e. jenkins
28
+
29
+ `port` - The port of your Jenkins server i.e. 8080
30
+
31
+ `ssl` - If you would like to use ssl [Boolean]
32
+
33
+ Within an initializer `config/initializer/subordinate.rb`
34
+
35
+ ```ruby
36
+ Subordinate.configure do |c|
37
+ c.subdomain = "subdomain"
38
+ c.domain = "domain"
39
+ c.port = 1234
40
+ c.ssl = false
41
+ end
42
+ ```
43
+
44
+ ## Examples
45
+
46
+ ### Setting up a new client
47
+
48
+ ```ruby
49
+ client = Subordinate::Client.new(:username => "username", :api_token =>"token")
50
+ ```
51
+ or
52
+ ```ruby
53
+ client = Subordinate.new(:username => "username", :api_token =>"token")
54
+ ```
55
+
56
+ You can also pass configuration keys such as `domain`, `subdomain`, `port` etc. as well.
57
+
58
+ ### [Jobs](http://rdoc.info/github/jasontruluck/subordinate/Subordinate/Client/Job)
59
+
60
+ Currently offers the ability to build, delete, enable/disable jobs
61
+
62
+ ```ruby
63
+ client.job("Job-Name")
64
+ ```
65
+
66
+ ### [Builds](http://rdoc.info/github/jasontruluck/subordinate/Subordinate/Client/Build)
67
+
68
+ Currently can retrieve information, console output, and timestamps
69
+
70
+ ```ruby
71
+ client.build("Job-Name", 1)
72
+ ```
73
+
74
+ ###[System](http://rdoc.info/github/jasontruluck/subordinate/Subordinate/Client/System)
75
+
76
+ Currently can safe restart, restart, and quiet down
77
+
78
+ ```ruby
79
+ client.restart
80
+ ```
81
+
82
+ ## Testing
83
+
84
+ This gem uses VCR to record requests to the api so you must test using a valid Jenkins server and credentails to test
85
+
86
+ Add a sample authentications file to your `spec/fixtures` directory:
87
+
88
+ ```ruby
89
+ #spec/fixtures/authentications.yml
90
+ username: jasontruluck # Your Username
91
+ token: 12345678901234567890 # Your Jenkins Token (found at jenkins-server/user/your-user-name/configure)
92
+ domain: mydomain.com # The domain of your Jenkins server
93
+ port: 8080 # The port of your Jenkins Server
94
+ subdomain: jenkins # The subdomain of your Jenkins Server
95
+ job: My-Jenkins-Job # The job that you want to run tests on
96
+ ```
97
+
98
+ A sample is included in the [source](https://github.com/jasontruluck/subordinate/blob/master/spec/fixtures/authentications.yml.sample).
99
+
100
+ *Note: for tests concerning disabling, deleting, restarting, etc they are mocked explicitly with webmock and will not effect your server*
101
+
102
+ ## Contributing
103
+
104
+ 1. Fork it
105
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
106
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
107
+ 4. Push to the branch (`git push origin my-new-feature`)
108
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,22 @@
1
+ # Authentication Module
2
+ module Subordinate
3
+ module Authentication
4
+ def authentication
5
+ if username && api_token
6
+ { :username => username, :api_token => api_token}
7
+ else
8
+ {}
9
+ end
10
+ end
11
+
12
+ def authenticated?
13
+ !authentication.empty?
14
+ end
15
+
16
+ def username_and_token(username = "", api_token = "")
17
+ return if username.nil? || api_token.nil?
18
+ self.username = username
19
+ self.api_token = api_token
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,85 @@
1
+ # Build
2
+ module Subordinate
3
+ class Client
4
+ # Build management and configuration
5
+ #
6
+ # @see https://ci.jenkins-ci.org/job/jenkins_rc_branch/322/api/
7
+ module Build
8
+ # Returns the response with information about the specific build
9
+ #
10
+ # @see https://ci.jenkins-ci.org/job/jenkins_rc_branch/322/api/json?pretty=true
11
+ #
12
+ # @param [String] job the job that you want to retrieve information about
13
+ # @param [String] build_number the build number that you want to retrieve information about
14
+ #
15
+ # @return [Hashie::Mash] build response
16
+ #
17
+ # @example Get the build api response
18
+ # Subordinate::Client.build("My-Job-I-Want-Info-On", 5)
19
+ #
20
+ # @author Jason Truluck
21
+ def build(job, build_number, options = {})
22
+ get("job/#{job}/#{build_number}/api/json", options)
23
+ end
24
+
25
+ # Returns the builds timestamp
26
+ # This methods accepts the simple data formatter seen here:
27
+ # @see http://docs.oracle.com/javase/1.5.0/docs/api/java/text/SimpleDateFormat.html
28
+ #
29
+ # @see https://ci.jenkins-ci.org/job/jenkins_rc_branch/322/buildTimestamp
30
+ #
31
+ # @param [String] job the job that you want to retrieve the build number from
32
+ # @param [String] build_number the build number that you want to retrieve information about
33
+ # @param [String] format the timestamp format you want returned
34
+ #
35
+ # @return [String] build timestamp
36
+ #
37
+ # @example Get the first builds timestamp
38
+ # Subordinate::Client.build_timestamp("My-Job-I-Want-Info-On", 1)
39
+ #
40
+ # @example Get the first builds timestamp in format "yyyy/MM/dd"
41
+ # Subordinate::Client.build_timestamp("My-Job-I-Want-Info-On", 1, "yyyy/MM/dd")
42
+ #
43
+ # @author Jason Truluck
44
+ def build_timestamp(job, build_number, format = nil, options = {})
45
+ options.merge!(
46
+ :format => format
47
+ ) if !format.nil?
48
+
49
+ get("job/#{job}/#{build_number}/buildTimestamp", options)
50
+ end
51
+
52
+ # Returns the console output for the build specified
53
+ #
54
+ # @see https://ci.jenkins-ci.org/job/jenkins_rc_branch/322/logText/progressiveText?start=0
55
+ #
56
+ # @param [String] job the job that you want to retrieve the build number from
57
+ # @param [String] build_number the build number that you want to retrieve information about
58
+ # @param [String] start_offset the byte offset of where you start.
59
+ # @param [Boolean] pre if you want the output to be returned in a pre-formatted foramat for use in <pre> tags
60
+ #
61
+ # @return [String] console output of build
62
+ #
63
+ # @example Get the console output of the first build
64
+ # Subordinate::Client.console_output_for_build("My-Job-I-Want-Info-On", 1)
65
+ #
66
+ # @example Get the console output of the first build pre formatted
67
+ # Subordinate::Client.console_output_for_build("My-Job-I-Want-Info-On", 1, 0, true)
68
+ #
69
+ # @example Get the console output of the first build starting from the 200th byte
70
+ # Subordinate::Client.console_output_for_build("My-Job-I-Want-Info-On", 1, 200)
71
+ #
72
+ # @author Jason Truluck
73
+ def console_output_for_build(job, build_number, start_offset = 0, pre = false, options = {})
74
+ options.merge!(
75
+ :start => start_offset
76
+ )
77
+ if pre
78
+ get("job/#{job}/#{build_number}/logText/pregressiveHTML", options)
79
+ else
80
+ get("job/#{job}/#{build_number}/logText/pregressiveText", options)
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,89 @@
1
+ # Job
2
+ module Subordinate
3
+ class Client
4
+ # Job management and configuration
5
+ #
6
+ # @see https://ci.jenkins-ci.org/job/jenkins_rc_branch/api/
7
+ module Job
8
+ # Returns the response with information about the specific job
9
+ #
10
+ # @see https://ci.jenkins-ci.org/job/jenkins_rc_branch/api/json?pretty=true
11
+ #
12
+ # @param [String] job the job that you want to retrieve information about
13
+ #
14
+ # @return [Hashie::Mash] job response
15
+ #
16
+ # @example Get the job api response
17
+ # Subordinate::Client.job("My-Job-I-Want-Info-On")
18
+ #
19
+ # @author Jason Truluck
20
+ def job(job, options = {})
21
+ get("job/#{job}/api/json", options)
22
+ end
23
+
24
+ # Builds the job specified on the Jenkins server
25
+ #
26
+ # @see https://ci.jenkins-ci.org/job/jenkins_rc_branch/api/
27
+ #
28
+ # @param [String] job the job that you want to build
29
+ #
30
+ # @return [Integer] status
31
+ #
32
+ # @example Build the job
33
+ # Subordinate::Client.build("My-Job-I-Want-Info-On")
34
+ #
35
+ # @author Jason Truluck
36
+ def build_job(job, options = {})
37
+ post("job/#{job}/build", options).status
38
+ end
39
+
40
+ # Disables the specified job on the Jenkins Server
41
+ #
42
+ # @see https://ci.jenkins-ci.org/job/jenkins_rc_branch/api/
43
+ #
44
+ # @param [String] job the job that you want to disable
45
+ #
46
+ # @return [Integer] status
47
+ #
48
+ # @example Disable the job
49
+ # Subordinate::Client.disable("My-Job-I-Want-Info-On")
50
+ #
51
+ # @author Jason Truluck
52
+ def disable_job(job, options = {})
53
+ post("job/#{job}/disable", options).status
54
+ end
55
+
56
+ # Enables the specified job on the Jenkins Server
57
+ #
58
+ # @see https://ci.jenkins-ci.org/job/jenkins_rc_branch/api/
59
+ #
60
+ # @param [String] job the job that you want to enable
61
+ #
62
+ # @return [Integer] status
63
+ #
64
+ # @example Enable the job
65
+ # Subordinate::Client.enable("My-Job-I-Want-Info-On")
66
+ #
67
+ # @author Jason Truluck
68
+ def enable_job(job, options = {})
69
+ post("job/#{job}/enable", options).status
70
+ end
71
+
72
+ # Deletes the specified job on the Jenkins Server
73
+ #
74
+ # @see https://ci.jenkins-ci.org/job/jenkins_rc_branch/api/
75
+ #
76
+ # @param [String] job the job that you want to delete
77
+ #
78
+ # @return [Integer] status
79
+ #
80
+ # @example Disable the job
81
+ # Subordinate::Client.disable("My-Job-I-Want-Info-On")
82
+ #
83
+ # @author Jason Truluck
84
+ def delete_job(job, options = {})
85
+ post("job/#{job}/disable", options).status
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,81 @@
1
+ # System
2
+ module Subordinate
3
+ class Client
4
+ # System level tasks for Jenkins Server. These functions typically require admin
5
+ # level privileges to execute.
6
+ #
7
+ # @see https://ci.jenkins-ci.org/api/
8
+ module System
9
+
10
+ # Returns the response from the root api
11
+ #
12
+ # @see https://ci.jenkins-ci.org/api/json?pretty=true
13
+ #
14
+ # @return [Hashie::Mash] State is the server is currently up
15
+ #
16
+ # @example Get the root api response
17
+ # Subordinate::Client.root
18
+ #
19
+ # @author Jason Truluck
20
+ def root(options = {})
21
+ get('/api/json', options)
22
+ end
23
+
24
+ # Shuts down Jenkins Server
25
+ #
26
+ # @see https://ci.jenkins-ci.org/api/
27
+ #
28
+ # @return [Integer] response code
29
+ #
30
+ # @example Send a quiet down request to the Jenkins server
31
+ # Subordinate::Client.quiet_down
32
+ #
33
+ # @author Jason Truluck
34
+ def quiet_down(options = {})
35
+ post('/quietDown', options).status
36
+ end
37
+
38
+ # Cancel a shut down request to the Jenkins Server
39
+ #
40
+ # @see https://ci.jenkins-ci.org/api/
41
+ #
42
+ # @return [Integer] response code
43
+ #
44
+ # @example Send a quiet down request to the Jenkins server
45
+ # Subordinate::Client.cancel_quiet_down
46
+ #
47
+ # @author Jason Truluck
48
+ def cancel_quiet_down(options = {})
49
+ post('/cancelQuietDown', options).status
50
+ end
51
+
52
+ # Restarts the jenkins server, will not wait for jobs to finish
53
+ #
54
+ # @see https://ci.jenkins-ci.org/api/
55
+ #
56
+ # @return [Integer] response code
57
+ #
58
+ # @example Sends a force restart request to the Jenkins server
59
+ # Subordinate::Client.restart(true)
60
+ #
61
+ # @author Jason Truluck
62
+ def restart(options = {})
63
+ post("restart", options).status
64
+ end
65
+
66
+ # Safely Restarts the jenkins server, will wait for jobs to finish
67
+ #
68
+ # @see https://ci.jenkins-ci.org/api/
69
+ #
70
+ # @return [Integer] response code
71
+ #
72
+ # @example Sends a restart request to the Jenkins server (defaults to no force)
73
+ # Subordinate::Client.restart
74
+ #
75
+ # @author Jason Truluck
76
+ def safe_restart(options = {})
77
+ post("safeRestart", options).status
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,46 @@
1
+ # Client Module
2
+ require "subordinate/authentication"
3
+ require "subordinate/connection"
4
+ require "subordinate/request"
5
+
6
+ require "subordinate/client/job"
7
+ require "subordinate/client/system"
8
+ require "subordinate/client/build"
9
+
10
+ module Subordinate
11
+ class Client
12
+ attr_accessor(*Configuration::VALID_OPTIONS_KEYS)
13
+
14
+ def initialize(options = {})
15
+ options = Subordinate.options.merge(options)
16
+
17
+ Configuration::VALID_OPTIONS_KEYS.each do |key|
18
+ send("#{key}=", options[key])
19
+ end
20
+
21
+ username_and_token(options[:username], options[:api_token])
22
+ build_endpoint
23
+ end
24
+
25
+ # Builds the api endpoint to reach the Jenkins Server
26
+ #
27
+ # @return [String] Endpoint
28
+ #
29
+ # @author Jason Truluck
30
+ def build_endpoint
31
+ endpoint = ssl ? "https://" : "http://"
32
+ endpoint << "#{self.username}:#{self.api_token}@" if self.authenticated?
33
+ endpoint << "#{self.subdomain}.#{self.domain}:#{self.port}"
34
+ endpoint << "?depth=#{self.depth}" if !depth.nil?
35
+ self.api_endpoint = endpoint
36
+ end
37
+
38
+ include Subordinate::Authentication
39
+ include Subordinate::Connection
40
+ include Subordinate::Request
41
+
42
+ include Subordinate::Client::Job
43
+ include Subordinate::Client::System
44
+ include Subordinate::Client::Build
45
+ end
46
+ end
@@ -0,0 +1,35 @@
1
+ #Configuration
2
+ module Subordinate
3
+ module Configuration
4
+ VALID_OPTIONS_KEYS = [
5
+ :username,
6
+ :api_token,
7
+ :api_endpoint,
8
+ :domain,
9
+ :subdomain,
10
+ :port,
11
+ :ssl,
12
+ :depth
13
+ ]
14
+
15
+ attr_accessor(*VALID_OPTIONS_KEYS)
16
+
17
+ def configure
18
+ yield self
19
+ end
20
+
21
+ def options
22
+ VALID_OPTIONS_KEYS.inject({}){|o,k| o.merge!(k => send(k)) }
23
+ end
24
+
25
+ def reset!
26
+ self.username = nil
27
+ self.api_token = nil
28
+ self.domain = nil
29
+ self.subdomain = nil
30
+ self.port = nil
31
+ self.ssl = true
32
+ self.depth = nil
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,20 @@
1
+ # Connection
2
+ require "faraday_middleware"
3
+
4
+ module Subordinate
5
+ module Connection
6
+ def connection(options = {})
7
+ options = {
8
+ :ssl => { :verify => false }
9
+ }.merge(options)
10
+
11
+ connection = Faraday.new(options) do |build|
12
+ build.use FaradayMiddleware::Mashify
13
+ build.use FaradayMiddleware::ParseJson, :content_type => /\bjson$/
14
+ build.adapter Faraday.default_adapter
15
+ end
16
+
17
+ connection
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ module Subordinate
2
+ module Request
3
+ def get(path, options = {})
4
+ response = request(:get, path, options)
5
+ response.body
6
+ end
7
+
8
+ def post(path, options = {})
9
+ response = request(:post, path, options)
10
+ response
11
+ end
12
+
13
+ private
14
+ def request(method, path, options = {})
15
+ url = options.delete(:endpoint) || api_endpoint
16
+
17
+ connection_options = {
18
+ :url => url
19
+ }
20
+
21
+ response = connection(connection_options).send(method) do |request|
22
+ case method
23
+ when :get
24
+ request.url(path, options)
25
+ when :post
26
+ request.path = path
27
+ request.body = options unless options.empty?
28
+ end
29
+ end
30
+ response
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module Subordinate
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,19 @@
1
+ require "subordinate/version"
2
+ require "subordinate/configuration"
3
+ require "subordinate/client"
4
+
5
+ module Subordinate
6
+ extend Configuration
7
+
8
+ class << self
9
+ # Alias for Subordinate::Client.new
10
+ # @return [Subordinate::Client]
11
+ def new(options = {})
12
+ Subordinate::Client.new(options)
13
+ end
14
+
15
+ def respond_to?(method, include_private=false)
16
+ new.respond_to?(method, include_private) || super(method, include_private)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,6 @@
1
+ username: jasontruluck # Your Username
2
+ token: 12345678901234567890 # Your Jenkins Token (found at jenkins-server/user/your-user-name/configure)
3
+ domain: mydomain.com # The domain of your Jenkins server
4
+ port: 8080 # The port of your Jenkins Server
5
+ subdomain: jenkins # The subdomain of your Jenkins Server
6
+ job: My-Jenkins-Job # The job that you want to run tests on
@@ -0,0 +1,43 @@
1
+ require 'rubygems'
2
+
3
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
4
+ ENV["RAILS_ENV"] ||= 'test'
5
+
6
+ if ENV['RAILS_ENV'] == 'test'
7
+ require 'simplecov'
8
+ SimpleCov.start 'rails'
9
+ class SimpleCov::Formatter::QualityFormatter
10
+ def format(result)
11
+ SimpleCov::Formatter::HTMLFormatter.new.format(result)
12
+ File.open("coverage/covered_percent", "w") do |f|
13
+ f.puts result.source_files.covered_percent.to_f
14
+ end
15
+ end
16
+ end
17
+ SimpleCov.formatter = SimpleCov::Formatter::QualityFormatter
18
+ end
19
+
20
+ require 'subordinate'
21
+ require 'vcr'
22
+ require "webmock/rspec"
23
+ require "mocha/api"
24
+
25
+ authentications = YAML::load(File.open(File.expand_path("../fixtures/authentications.yml", __FILE__)))
26
+ VCR.configure do |c|
27
+ c.cassette_library_dir = 'spec/cassettes'
28
+ c.hook_into :faraday
29
+ c.ignore_localhost = true
30
+ # Uncomment if you need to log VCR
31
+ # c.debug_logger = File.open(Rails.root.join("log","vcr_debugger.log"), 'w')
32
+ c.configure_rspec_metadata!
33
+ c.allow_http_connections_when_no_cassette = true
34
+ end
35
+
36
+ WebMock.allow_net_connect!
37
+
38
+ Dir[File.expand_path("spec/support/**/*.rb", __FILE__)].each {|f| require f}
39
+
40
+ RSpec.configure do |config|
41
+ config.treat_symbols_as_metadata_keys_with_true_values = true
42
+ config.order = "random"
43
+ end