cyclid 0.2.0
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.
- checksums.yaml +7 -0
- data/LICENSE +174 -0
- data/README.md +54 -0
- data/app/cyclid.rb +61 -0
- data/app/cyclid/config.rb +38 -0
- data/app/cyclid/controllers.rb +123 -0
- data/app/cyclid/controllers/auth.rb +34 -0
- data/app/cyclid/controllers/auth/token.rb +78 -0
- data/app/cyclid/controllers/health.rb +96 -0
- data/app/cyclid/controllers/organizations.rb +104 -0
- data/app/cyclid/controllers/organizations/collection.rb +134 -0
- data/app/cyclid/controllers/organizations/config.rb +128 -0
- data/app/cyclid/controllers/organizations/document.rb +135 -0
- data/app/cyclid/controllers/organizations/job.rb +266 -0
- data/app/cyclid/controllers/organizations/members.rb +145 -0
- data/app/cyclid/controllers/organizations/stages.rb +251 -0
- data/app/cyclid/controllers/users.rb +47 -0
- data/app/cyclid/controllers/users/collection.rb +131 -0
- data/app/cyclid/controllers/users/document.rb +133 -0
- data/app/cyclid/health_helpers.rb +40 -0
- data/app/cyclid/job.rb +3 -0
- data/app/cyclid/job/helpers.rb +67 -0
- data/app/cyclid/job/job.rb +164 -0
- data/app/cyclid/job/runner.rb +275 -0
- data/app/cyclid/job/stage.rb +67 -0
- data/app/cyclid/log_buffer.rb +104 -0
- data/app/cyclid/models.rb +3 -0
- data/app/cyclid/models/job_record.rb +25 -0
- data/app/cyclid/models/organization.rb +64 -0
- data/app/cyclid/models/plugin_config.rb +25 -0
- data/app/cyclid/models/stage.rb +42 -0
- data/app/cyclid/models/step.rb +29 -0
- data/app/cyclid/models/user.rb +60 -0
- data/app/cyclid/models/user_permissions.rb +28 -0
- data/app/cyclid/monkey_patches.rb +37 -0
- data/app/cyclid/plugin_registry.rb +75 -0
- data/app/cyclid/plugins.rb +125 -0
- data/app/cyclid/plugins/action.rb +48 -0
- data/app/cyclid/plugins/action/command.rb +89 -0
- data/app/cyclid/plugins/action/email.rb +207 -0
- data/app/cyclid/plugins/action/email/html.erb +58 -0
- data/app/cyclid/plugins/action/email/text.erb +13 -0
- data/app/cyclid/plugins/action/script.rb +90 -0
- data/app/cyclid/plugins/action/slack.rb +129 -0
- data/app/cyclid/plugins/action/slack/note.erb +5 -0
- data/app/cyclid/plugins/api.rb +195 -0
- data/app/cyclid/plugins/api/github.rb +111 -0
- data/app/cyclid/plugins/api/github/callback.rb +66 -0
- data/app/cyclid/plugins/api/github/methods.rb +201 -0
- data/app/cyclid/plugins/api/github/status.rb +67 -0
- data/app/cyclid/plugins/builder.rb +80 -0
- data/app/cyclid/plugins/builder/mist.rb +107 -0
- data/app/cyclid/plugins/dispatcher.rb +89 -0
- data/app/cyclid/plugins/dispatcher/local.rb +167 -0
- data/app/cyclid/plugins/provisioner.rb +40 -0
- data/app/cyclid/plugins/provisioner/debian.rb +90 -0
- data/app/cyclid/plugins/provisioner/ubuntu.rb +98 -0
- data/app/cyclid/plugins/source.rb +39 -0
- data/app/cyclid/plugins/source/git.rb +64 -0
- data/app/cyclid/plugins/transport.rb +63 -0
- data/app/cyclid/plugins/transport/ssh.rb +155 -0
- data/app/cyclid/sinatra/api_helpers.rb +66 -0
- data/app/cyclid/sinatra/auth_helpers.rb +127 -0
- data/app/cyclid/sinatra/warden/strategies/api_token.rb +62 -0
- data/app/cyclid/sinatra/warden/strategies/basic.rb +58 -0
- data/app/cyclid/sinatra/warden/strategies/hmac.rb +76 -0
- data/app/db.rb +51 -0
- data/bin/cyclid-db-init +107 -0
- data/db/schema.rb +92 -0
- data/lib/cyclid/app.rb +4 -0
- metadata +407 -0
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Copyright 2016 Liqwyd Ltd.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
# Top level module for all of the core Cyclid code.
|
17
|
+
module Cyclid
|
18
|
+
# Module for the Cyclid API
|
19
|
+
module API
|
20
|
+
# Module for all Organization related API endpoints
|
21
|
+
module Organizations
|
22
|
+
# API endpoints for Organization specific configuration
|
23
|
+
# @api REST
|
24
|
+
module Configs
|
25
|
+
# rubocop:disable Metrics/LineLength
|
26
|
+
# @!group Organizations
|
27
|
+
|
28
|
+
# @!method get_organizations_organization_configs_type_plugin
|
29
|
+
# @overload GET /organizations/:organization/configs/:type/:plugin
|
30
|
+
# @macro rest
|
31
|
+
# @param [String] organization Name of the organization.
|
32
|
+
# @param [String] type The plugin type E.g. 'api' for an API plugin, 'source' for a
|
33
|
+
# Source plugin etc.
|
34
|
+
# @param [String] plugin Name of the plugin.
|
35
|
+
# Get the current configuration for the given plugin.
|
36
|
+
# @return The plugin configuration for the given plugin.
|
37
|
+
# @return [404] The organization or plugin does not exist.
|
38
|
+
# @example Get the 'example' plugin configuration from the 'example' organization
|
39
|
+
# GET /organizations/example/configs/type/example => {"id":1,
|
40
|
+
# "plugin":"example",
|
41
|
+
# "version":"1.0.0",
|
42
|
+
# "config":{<plugin specific object>},
|
43
|
+
# "organization_id":2,
|
44
|
+
# "schema":[<plugin configuration schema>]}
|
45
|
+
|
46
|
+
# @!method put_organizations_organization_configs_type_plugin
|
47
|
+
# @overload PUT /organizations/:organization/configs/:type/:plugin
|
48
|
+
# @macro rest
|
49
|
+
# @param [String] organization Name of the organization.
|
50
|
+
# @param [String] type The plugin type E.g. 'api' for an API plugin, 'source' for a
|
51
|
+
# Source plugin etc.
|
52
|
+
# @param [String] plugin Name of the plugin.
|
53
|
+
# Update the plugin configuration
|
54
|
+
# @return [200] The plugin configuration was updated.
|
55
|
+
# @return [404] The organization or plugin does not exist.
|
56
|
+
|
57
|
+
# @!endgroup
|
58
|
+
# rubocop:enable Metrics/LineLength
|
59
|
+
|
60
|
+
# Sinatra callback
|
61
|
+
# @private
|
62
|
+
def self.registered(app)
|
63
|
+
include Errors::HTTPErrors
|
64
|
+
include Constants::JobStatus
|
65
|
+
|
66
|
+
# Get the current configuration for the given plugin.
|
67
|
+
app.get '/:plugin' do
|
68
|
+
authorized_for!(params[:name], Operations::READ)
|
69
|
+
|
70
|
+
org = Organization.find_by(name: params[:name])
|
71
|
+
halt_with_json_response(404, INVALID_ORG, 'organization does not exist') \
|
72
|
+
if org.nil?
|
73
|
+
|
74
|
+
Cyclid.logger.debug "type=#{params[:type]} plugin=#{params[:plugin]}"
|
75
|
+
|
76
|
+
# Find the plugin
|
77
|
+
plugin = Cyclid.plugins.find(params[:plugin], params[:type])
|
78
|
+
halt_with_json_response(404, INVALID_PLUGIN, 'plugin does not exist') \
|
79
|
+
if plugin.nil?
|
80
|
+
|
81
|
+
# Ask the plugin for the current config for this organization. This
|
82
|
+
# will include the config schema under the 'schema' attribute.
|
83
|
+
begin
|
84
|
+
config = plugin.get_config(org)
|
85
|
+
|
86
|
+
halt_with_json_response(404, INVALID_PLUGIN_CONFIG, 'failed to get plugin config') \
|
87
|
+
if config.nil?
|
88
|
+
rescue StandardError => ex
|
89
|
+
halt_with_json_response(404, \
|
90
|
+
INVALID_PLUGIN_CONFIG, \
|
91
|
+
"failed to get plugin config: #{ex}") \
|
92
|
+
if config.nil?
|
93
|
+
end
|
94
|
+
|
95
|
+
return config.to_json
|
96
|
+
end
|
97
|
+
|
98
|
+
# Update the plugin configuration
|
99
|
+
app.put '/:plugin' do
|
100
|
+
authorized_for!(params[:name], Operations::ADMIN)
|
101
|
+
|
102
|
+
payload = parse_request_body
|
103
|
+
Cyclid.logger.debug payload
|
104
|
+
|
105
|
+
org = Organization.find_by(name: params[:name])
|
106
|
+
halt_with_json_response(404, INVALID_ORG, 'organization does not exist') \
|
107
|
+
if org.nil?
|
108
|
+
|
109
|
+
# Find the plugin
|
110
|
+
plugin = Cyclid.plugins.find(params[:plugin], params[:type])
|
111
|
+
halt_with_json_response(404, INVALID_PLUGIN, 'plugin does not exist') \
|
112
|
+
if plugin.nil?
|
113
|
+
|
114
|
+
# Ask the plugin for the current config for this organization. This
|
115
|
+
# will include the config schema under the 'schema' attribute.
|
116
|
+
begin
|
117
|
+
plugin.set_config(payload, org)
|
118
|
+
rescue StandardError => ex
|
119
|
+
halt_with_json_response(404, \
|
120
|
+
INVALID_PLUGIN_CONFIG, \
|
121
|
+
"failed to set plugin config: #{ex}") \
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Copyright 2016 Liqwyd Ltd.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
# Top level module for all of the core Cyclid code.
|
17
|
+
module Cyclid
|
18
|
+
# Module for the Cyclid API
|
19
|
+
module API
|
20
|
+
# Module for all Organization related API endpoints
|
21
|
+
module Organizations
|
22
|
+
# API endpoints for a single Organization document
|
23
|
+
# @api REST
|
24
|
+
module Document
|
25
|
+
# @!group Organizations
|
26
|
+
|
27
|
+
# @!method get_organizations_organization
|
28
|
+
# @overload GET /organizations/:organization
|
29
|
+
# @macro rest
|
30
|
+
# @param [String] organization Name of the organization.
|
31
|
+
# Get a specific organization. The RSA public key is in Base64 encoded
|
32
|
+
# DER format, and can be used to encrypt secrets that can be
|
33
|
+
# decrypted only by the server.
|
34
|
+
# @return The organization object.
|
35
|
+
# @return [404] The requested organization does not exist.
|
36
|
+
# @example Get the 'example' organization
|
37
|
+
# GET /organizations/example => [{"id": 1,
|
38
|
+
# "name": "example",
|
39
|
+
# "owner_email": "admin@example.com",
|
40
|
+
# "users": ["user1", "user2"],
|
41
|
+
# "public_key": "<RSA public key>"}]
|
42
|
+
# @see get_organizations
|
43
|
+
|
44
|
+
# @!method put_organizations(body)
|
45
|
+
# @overload PUT /organizations/:organization
|
46
|
+
# @macro rest
|
47
|
+
# @param [String] organization Name of the organization.
|
48
|
+
# Modify an organization. The organizations name or public key can not
|
49
|
+
# be changed.
|
50
|
+
# If a list of users is provided, the current list will be *replaced*,
|
51
|
+
# so clients should first retrieve the full list of users, modify it,
|
52
|
+
# and then use this API to set the final list of users.
|
53
|
+
# @param [JSON] body New organization data.
|
54
|
+
# @option body [String] owner_email Email address of the organization owner
|
55
|
+
# @option body [Array<String>] users List of users who are organization members.
|
56
|
+
# @return [200] The organization was changed successfully.
|
57
|
+
# @return [404] The organization does not exist
|
58
|
+
# @return [404] A user in the list of members does not exist
|
59
|
+
# @example Modify the 'example' organization to have user1 & user2 as members
|
60
|
+
# POST /organizations/example <= {"users": ["user1", "user2"]}
|
61
|
+
# @example Modify the 'example' organization to change the owner email
|
62
|
+
# POST /organizations/example <= {"owner_email": "bob@example.com"}
|
63
|
+
|
64
|
+
# @!endgroup
|
65
|
+
|
66
|
+
# Sinatra callback
|
67
|
+
# @private
|
68
|
+
def self.registered(app)
|
69
|
+
include Errors::HTTPErrors
|
70
|
+
|
71
|
+
# Get a specific organization.
|
72
|
+
app.get do
|
73
|
+
authorized_for!(params[:name], Operations::READ)
|
74
|
+
|
75
|
+
org = Organization.find_by(name: params[:name])
|
76
|
+
halt_with_json_response(404, INVALID_ORG, 'organization does not exist') \
|
77
|
+
if org.nil?
|
78
|
+
|
79
|
+
# Base64 encode the public key
|
80
|
+
public_key = Base64.strict_encode64(org.rsa_public_key)
|
81
|
+
|
82
|
+
# Convert to a Hash, sanitize and inject the Users data & encoded
|
83
|
+
# RSA key
|
84
|
+
org_hash = sanitize_organization(org.serializable_hash)
|
85
|
+
org_hash['users'] = org.users.map(&:username)
|
86
|
+
org_hash['public_key'] = public_key
|
87
|
+
|
88
|
+
return org_hash.to_json
|
89
|
+
end
|
90
|
+
|
91
|
+
# Modify a specific organization.
|
92
|
+
app.put do
|
93
|
+
authorized_for!(params[:name], Operations::WRITE)
|
94
|
+
|
95
|
+
payload = parse_request_body
|
96
|
+
Cyclid.logger.debug payload
|
97
|
+
|
98
|
+
org = Organization.find_by(name: params[:name])
|
99
|
+
halt_with_json_response(404, INVALID_ORG, 'organization does not exist') \
|
100
|
+
if org.nil?
|
101
|
+
|
102
|
+
begin
|
103
|
+
# Change the owner email if one is provided
|
104
|
+
org['owner_email'] = payload['owner_email'] if payload.key? 'owner_email'
|
105
|
+
|
106
|
+
# Change the users if a list of users was provided
|
107
|
+
if payload.key? 'users'
|
108
|
+
# Add each provided user to the Organization
|
109
|
+
org.users = payload['users'].map do |username|
|
110
|
+
user = User.find_by(username: username)
|
111
|
+
|
112
|
+
halt_with_json_response(404, \
|
113
|
+
INVALID_USER, \
|
114
|
+
"user #{username} does not exist") \
|
115
|
+
if user.nil?
|
116
|
+
|
117
|
+
user
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
org.save!
|
122
|
+
rescue ActiveRecord::ActiveRecordError, \
|
123
|
+
ActiveRecord::UnknownAttributeError => ex
|
124
|
+
|
125
|
+
Cyclid.logger.debug ex.message
|
126
|
+
halt_with_json_response(400, INVALID_JSON, ex.message)
|
127
|
+
end
|
128
|
+
|
129
|
+
return json_response(NO_ERROR, "organization #{params['name']} updated")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,266 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Copyright 2016 Liqwyd Ltd.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
# Top level module for all of the core Cyclid code.
|
17
|
+
module Cyclid
|
18
|
+
# Module for the Cyclid API
|
19
|
+
module API
|
20
|
+
# Module for all Organization related API endpoints
|
21
|
+
module Organizations
|
22
|
+
# API endpoints for Organization Jobs
|
23
|
+
# @api REST
|
24
|
+
module Jobs
|
25
|
+
# rubocop:disable Metrics/LineLength
|
26
|
+
# @!group Organizations
|
27
|
+
|
28
|
+
# @!method get_organizations_organization_jobs
|
29
|
+
# @overload GET /organizations/:organization/jobs
|
30
|
+
# @param [String] organization Name of the organization.
|
31
|
+
# @param [Boolean] stats_only Do not return the job records; just the count
|
32
|
+
# @param [Integer] limit Maxiumum number of records to return.
|
33
|
+
# @param [Integer] offset Offset to start returning records.
|
34
|
+
# @param [String] s_name Name of the job to search on.
|
35
|
+
# @param [Integer] s_status Status to search on.
|
36
|
+
# @param [String] s_from Date & time to search from.
|
37
|
+
# @param [String] s_to Date & time to search to.
|
38
|
+
# @macro rest
|
39
|
+
# Get a list of jobs that have been run for the organization. Jobs can be
|
40
|
+
# filtered using the s_name, s_status, s_from and s_to search parameters. If
|
41
|
+
# s_from & s_to are given, only jobs which started between the two times will
|
42
|
+
# be returned.
|
43
|
+
# @return The list of job details.
|
44
|
+
# @return [404] The organization does not exist.
|
45
|
+
|
46
|
+
# @!method post_organizations_organization_jobs(body)
|
47
|
+
# @overload POST /organizations/:organization/jobs
|
48
|
+
# @macro rest
|
49
|
+
# @param [String] organization Name of the organization.
|
50
|
+
# Create and run a job. The job definition can be either a JSON or
|
51
|
+
# YAML document.
|
52
|
+
# @param [JSON] body Job definition
|
53
|
+
# @option body [String] name Name of the job.
|
54
|
+
# @option body [Object] environment Job runtime environment details. At a minimum this
|
55
|
+
# must include the operating system name & version to use.
|
56
|
+
# @option body [Object] secrets ({}) Encrypted secret data for use by the job.
|
57
|
+
# @option body [Array<Object>] stages ([]) Ad-hoc stage definitions which are local to this job.
|
58
|
+
# @option body [Array<Object>] sequence ([]) List of stages to be run.
|
59
|
+
# @return [200] The job was created and successfully queued.
|
60
|
+
# @return [400] The job definition was invalid.
|
61
|
+
# @return [404] The organization does not exist.
|
62
|
+
# @example Create a simple job in the 'example' organization with no secrets or ad-hoc stages
|
63
|
+
# POST /organizations/example/jobs <= {"name": "example",
|
64
|
+
# "environment" : {
|
65
|
+
# "os" : "ubuntu_trusty"
|
66
|
+
# },
|
67
|
+
# "sequence": [
|
68
|
+
# {
|
69
|
+
# "stage": "example_stage"
|
70
|
+
# }
|
71
|
+
# ]}
|
72
|
+
|
73
|
+
# @!method get_organizations_organization_job
|
74
|
+
# @overload GET /organizations/:organization/jobs/:job
|
75
|
+
# @param [String] organization Name of the organization.
|
76
|
+
# @param [Integer] job Job ID.
|
77
|
+
# @macro rest
|
78
|
+
# Get the complete JobRecord for the given job ID.
|
79
|
+
# @return The job record for the job ID.
|
80
|
+
# @return [404] The organization or job record does not exist.
|
81
|
+
|
82
|
+
# @!method get_organizations_organization_job_status
|
83
|
+
# @overload GET /organizations/:organization/jobs/:job/status
|
84
|
+
# @param [String] organization Name of the organization.
|
85
|
+
# @param [Integer] job Job ID.
|
86
|
+
# @macro rest
|
87
|
+
# Get the current status of the given job ID.
|
88
|
+
# @return The current job status for the job ID.
|
89
|
+
# @return [404] The organization or job record does not exist.
|
90
|
+
|
91
|
+
# @!method get_organizations_organization_job_log
|
92
|
+
# @overload GET /organizations/:organization/jobs/:job/log
|
93
|
+
# @param [String] organization Name of the organization.
|
94
|
+
# @param [Integer] job Job ID.
|
95
|
+
# @macro rest
|
96
|
+
# Get the current complete log of the given job ID.
|
97
|
+
# @return The job log for the job ID.
|
98
|
+
# @return [404] The organization or job record does not exist.
|
99
|
+
|
100
|
+
# @!endgroup
|
101
|
+
# rubocop:enable Metrics/LineLength
|
102
|
+
|
103
|
+
# Sinatra callback
|
104
|
+
# @private
|
105
|
+
def self.registered(app)
|
106
|
+
include Errors::HTTPErrors
|
107
|
+
include Constants::JobStatus
|
108
|
+
|
109
|
+
# Return a list of jobs
|
110
|
+
app.get do
|
111
|
+
authorized_for!(params[:name], Operations::READ)
|
112
|
+
|
113
|
+
org = Organization.find_by(name: params[:name])
|
114
|
+
halt_with_json_response(404, INVALID_ORG, 'organization does not exist') \
|
115
|
+
if org.nil?
|
116
|
+
|
117
|
+
# Get any search terms that we'll need to find the appropriate jobs
|
118
|
+
search = {}
|
119
|
+
search[:job_name] = URI.decode(params[:s_name]) if params[:s_name]
|
120
|
+
search[:status] = params[:s_status] if params[:s_status]
|
121
|
+
|
122
|
+
# search_from & search_to should be some parsable format
|
123
|
+
if params[:s_from] and params[:s_to]
|
124
|
+
from = Time.parse(params[:s_from])
|
125
|
+
to = Time.parse(params[:s_to])
|
126
|
+
|
127
|
+
# ActiveRecord understands a range
|
128
|
+
search[:started] = from..to
|
129
|
+
end
|
130
|
+
|
131
|
+
Cyclid.logger.debug "search=#{search.inspect}"
|
132
|
+
|
133
|
+
# Find the number of matching jobs
|
134
|
+
count = if search.empty?
|
135
|
+
org.job_records.count
|
136
|
+
else
|
137
|
+
org.job_records
|
138
|
+
.where(search)
|
139
|
+
.count
|
140
|
+
end
|
141
|
+
|
142
|
+
Cyclid.logger.debug "count=#{count}"
|
143
|
+
|
144
|
+
stats_only = params[:stats_only] || false
|
145
|
+
limit = (params[:limit] || 100).to_i
|
146
|
+
offset = (params[:offset] || 0).to_i
|
147
|
+
|
148
|
+
job_data = { 'total' => count,
|
149
|
+
'offset' => offset,
|
150
|
+
'limit' => limit }
|
151
|
+
|
152
|
+
unless stats_only
|
153
|
+
# Get the available job records, but be terse with the
|
154
|
+
# information returned; there is no need to return a full job log
|
155
|
+
# with every job, for example.
|
156
|
+
job_records = if search.empty?
|
157
|
+
org.job_records
|
158
|
+
.all
|
159
|
+
.select('id, job_name, job_version, started, ended, status')
|
160
|
+
.offset(offset)
|
161
|
+
.limit(limit)
|
162
|
+
else
|
163
|
+
org.job_records
|
164
|
+
.where(search)
|
165
|
+
.select('id, job_name, job_version, started, ended, status')
|
166
|
+
.offset(offset)
|
167
|
+
.limit(limit)
|
168
|
+
end
|
169
|
+
|
170
|
+
job_data['records'] = job_records
|
171
|
+
end
|
172
|
+
|
173
|
+
return job_data.to_json
|
174
|
+
end
|
175
|
+
|
176
|
+
# Create and run a job.
|
177
|
+
app.post do
|
178
|
+
authorized_for!(params[:name], Operations::WRITE)
|
179
|
+
|
180
|
+
payload = parse_request_body
|
181
|
+
Cyclid.logger.debug payload
|
182
|
+
|
183
|
+
halt_with_json_response(400, INVALID_JOB, 'invalid job definition') \
|
184
|
+
unless payload.key? 'sequence' and \
|
185
|
+
payload.key? 'environment'
|
186
|
+
|
187
|
+
begin
|
188
|
+
job_id = job_from_definition(payload)
|
189
|
+
rescue StandardError => ex
|
190
|
+
halt_with_json_response(500, INVALID_JOB, "job failed: #{ex}")
|
191
|
+
end
|
192
|
+
|
193
|
+
return { job_id: job_id }.to_json
|
194
|
+
end
|
195
|
+
|
196
|
+
# Get the complete JobRecord for the given job ID.
|
197
|
+
app.get '/:id' do
|
198
|
+
authorized_for!(params[:name], Operations::READ)
|
199
|
+
|
200
|
+
org = Organization.find_by(name: params[:name])
|
201
|
+
halt_with_json_response(404, INVALID_ORG, 'organization does not exist') \
|
202
|
+
if org.nil?
|
203
|
+
|
204
|
+
begin
|
205
|
+
job_record = org.job_records.find(params[:id])
|
206
|
+
halt_with_json_response(404, INVALID_JOB, 'job does not exist') \
|
207
|
+
if job_record.nil?
|
208
|
+
rescue StandardError
|
209
|
+
halt_with_json_response(404, INVALID_JOB, 'job does not exist')
|
210
|
+
end
|
211
|
+
|
212
|
+
job = job_record.serializable_hash
|
213
|
+
job[:job_id] = job.delete :id
|
214
|
+
|
215
|
+
# XXX The "job" itself is a serialised internal representation and
|
216
|
+
# probably not very useful to the user, so we might want to process
|
217
|
+
# it into something more helpful here.
|
218
|
+
return job.to_json
|
219
|
+
end
|
220
|
+
|
221
|
+
# Get the current status of the given job ID.
|
222
|
+
app.get '/:id/status' do
|
223
|
+
authorized_for!(params[:name], Operations::READ)
|
224
|
+
|
225
|
+
org = Organization.find_by(name: params[:name])
|
226
|
+
halt_with_json_response(404, INVALID_ORG, 'organization does not exist') \
|
227
|
+
if org.nil?
|
228
|
+
|
229
|
+
job_record = org.job_records.find(params[:id])
|
230
|
+
halt_with_json_response(404, INVALID_JOB, 'job does not exist') \
|
231
|
+
if job_record.nil?
|
232
|
+
|
233
|
+
hash = {}
|
234
|
+
hash[:job_id] = job_record.id
|
235
|
+
hash[:status] = job_record.status
|
236
|
+
|
237
|
+
return hash.to_json
|
238
|
+
end
|
239
|
+
|
240
|
+
# Get the current complete log of the given job ID.
|
241
|
+
app.get '/:id/log' do
|
242
|
+
authorized_for!(params[:name], Operations::READ)
|
243
|
+
|
244
|
+
org = Organization.find_by(name: params[:name])
|
245
|
+
halt_with_json_response(404, INVALID_ORG, 'organization does not exist') \
|
246
|
+
if org.nil?
|
247
|
+
|
248
|
+
job_record = org.job_records.find(params[:id])
|
249
|
+
halt_with_json_response(404, INVALID_JOB, 'job does not exist') \
|
250
|
+
if job_record.nil?
|
251
|
+
|
252
|
+
hash = {}
|
253
|
+
hash[:job_id] = job_record.id
|
254
|
+
hash[:log] = job_record.log
|
255
|
+
|
256
|
+
return hash.to_json
|
257
|
+
end
|
258
|
+
|
259
|
+
app.helpers do
|
260
|
+
include Job::Helpers
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|