subordinate 0.0.1

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