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