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,107 @@
|
|
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
|
+
require 'mist/config'
|
17
|
+
require 'mist/pool'
|
18
|
+
require 'mist/client'
|
19
|
+
|
20
|
+
# Top level module for the core Cyclid code.
|
21
|
+
module Cyclid
|
22
|
+
# Module for the Cyclid API
|
23
|
+
module API
|
24
|
+
# Module for Cyclid Plugins
|
25
|
+
module Plugins
|
26
|
+
# Mist build host
|
27
|
+
class MistHost < BuildHost
|
28
|
+
# SSH is the only acceptable Transport
|
29
|
+
def transports
|
30
|
+
['ssh']
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Mist builder. Calls out to Mist to obtain a build host instance.
|
35
|
+
class Mist < Builder
|
36
|
+
def initialize
|
37
|
+
mist_config_file = ENV.fetch('MIST_CONFIG', File.join(%w(/ etc mist config)))
|
38
|
+
@config = ::Mist::Config.new(mist_config_file)
|
39
|
+
|
40
|
+
pool = ::Mist::Pool.get(@config.servers)
|
41
|
+
@client = ::Mist::Client.new(pool)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Create & return a build host
|
45
|
+
def get(args = {})
|
46
|
+
args.symbolize_keys!
|
47
|
+
|
48
|
+
Cyclid.logger.debug "mist: args=#{args}"
|
49
|
+
|
50
|
+
# If there is one, split the 'os' into a 'distro' and 'release'
|
51
|
+
if args.key? :os
|
52
|
+
match = args[:os].match(/\A(\w*)_(.*)\Z/)
|
53
|
+
distro = match[1] if match
|
54
|
+
release = match[2] if match
|
55
|
+
else
|
56
|
+
# No OS was specified; use the default
|
57
|
+
# XXX Defaults should be configurable
|
58
|
+
distro = 'ubuntu'
|
59
|
+
release = 'trusty'
|
60
|
+
end
|
61
|
+
|
62
|
+
begin
|
63
|
+
result = @client.call(:create, distro: distro, release: release)
|
64
|
+
Cyclid.logger.debug "mist result=#{result}"
|
65
|
+
|
66
|
+
raise "failed to create build host: #{result['message']}" \
|
67
|
+
unless result['status']
|
68
|
+
|
69
|
+
buildhost = MistHost.new(name: result['name'],
|
70
|
+
host: result['ip'],
|
71
|
+
username: result['username'],
|
72
|
+
workspace: "/home/#{result['username']}",
|
73
|
+
password: nil,
|
74
|
+
key: @config.ssh_private_key,
|
75
|
+
server: result['server'],
|
76
|
+
distro: distro,
|
77
|
+
release: release)
|
78
|
+
rescue MessagePack::RPC::TimeoutError => ex
|
79
|
+
Cyclid.logger.error "Mist create call timedout: #{ex}"
|
80
|
+
raise "mist failed: #{ex}"
|
81
|
+
rescue StandardError => ex
|
82
|
+
Cyclid.logger.error "couldn't get a build host from Mist: #{ex}"
|
83
|
+
raise "mist failed: #{ex}"
|
84
|
+
end
|
85
|
+
|
86
|
+
Cyclid.logger.debug "mist buildhost=#{buildhost.inspect}"
|
87
|
+
return buildhost
|
88
|
+
end
|
89
|
+
|
90
|
+
# Destroy the build host
|
91
|
+
def release(_transport, buildhost)
|
92
|
+
name = buildhost[:name]
|
93
|
+
server = buildhost[:server]
|
94
|
+
|
95
|
+
begin
|
96
|
+
@client.call(:destroy, name: name, server: server)
|
97
|
+
rescue MessagePack::RPC::TimeoutError => ex
|
98
|
+
Cyclid.logger.error "Mist destroy timed out: #{ex}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Register this plugin
|
103
|
+
register_plugin 'mist'
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,89 @@
|
|
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 the core Cyclid code.
|
17
|
+
module Cyclid
|
18
|
+
# Module for the Cyclid API
|
19
|
+
module API
|
20
|
+
# Module for Cyclid Plugins
|
21
|
+
module Plugins
|
22
|
+
# Base class for Dispatcher
|
23
|
+
class Dispatcher < Base
|
24
|
+
# Return the 'human' name for the plugin type
|
25
|
+
def self.human_name
|
26
|
+
'dispatcher'
|
27
|
+
end
|
28
|
+
|
29
|
+
# Dispatch a job to a worker and return some opaque data that can be
|
30
|
+
# used to identify that job (E.g. an ID, UUID etc.)
|
31
|
+
def dispatch(job, record, callback = nil)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# A Runner may be running locally (within the API application context)
|
36
|
+
# or remotely. A job runner needs to send updates about the job status
|
37
|
+
# but obviously, a remote runner can't just update the JobRecord
|
38
|
+
# directly: they may put a message on a queue, which a job at the API
|
39
|
+
# application would consume and update the JobRecord.
|
40
|
+
#
|
41
|
+
# A Notifier provides an abstract method to update the JobRecord
|
42
|
+
# status and can also proxy LogBuffer writes.
|
43
|
+
#
|
44
|
+
module Notifier
|
45
|
+
# Base class for Notifiers
|
46
|
+
class Base
|
47
|
+
def initialize(job_id, callback_object)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Update the JobRecord status
|
51
|
+
def status=(status)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Update the JobRecord ended field
|
55
|
+
def ended=(time)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Notify any callbacks that the job has completed
|
59
|
+
def completion(success)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Proxy data to the log buffer
|
63
|
+
def write(data)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Plugins may create a Callback instance that contains callbacks which
|
68
|
+
# are called by the Notifier when something happens; the Plugin can
|
69
|
+
# then take whatever action they need (E.g. updating an external
|
70
|
+
# status)
|
71
|
+
class Callback
|
72
|
+
# Called when the job completes
|
73
|
+
def completion(_job_id, _status)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Called whenever the job status changes
|
77
|
+
def status_changed(_job_id, _status)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Called whenever any data is written to the job record log
|
81
|
+
def log_write(_job_id, _data)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
require_rel 'dispatcher/*.rb'
|
@@ -0,0 +1,167 @@
|
|
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
|
+
require 'sidekiq'
|
17
|
+
|
18
|
+
# Top level module for the core Cyclid code.
|
19
|
+
module Cyclid
|
20
|
+
# Module for the Cyclid API
|
21
|
+
module API
|
22
|
+
# Module for Cyclid Plugins
|
23
|
+
module Plugins
|
24
|
+
# Local Sidekiq based dispatcher
|
25
|
+
class Local < Dispatcher
|
26
|
+
# Queue the job to be run asynchronously.
|
27
|
+
def dispatch(job, record, callback = nil)
|
28
|
+
Cyclid.logger.debug "dispatching job: #{job}"
|
29
|
+
|
30
|
+
job_definition = job.to_hash.to_json
|
31
|
+
|
32
|
+
record.job_name = job.name
|
33
|
+
record.job_version = job.version
|
34
|
+
record.job = job_definition
|
35
|
+
record.save!
|
36
|
+
|
37
|
+
# The callback instance has to be serailised into JSON to survive the
|
38
|
+
# trip through Redis to Sidekiq
|
39
|
+
callback_json = callback.nil? ? nil : Oj.dump(callback)
|
40
|
+
|
41
|
+
# Create a SideKiq worker and pass in the job
|
42
|
+
Worker::Local.perform_async(job_definition, record.id, callback_json)
|
43
|
+
|
44
|
+
# The JobRecord ID is as good a job identifier as anything
|
45
|
+
return record.id
|
46
|
+
end
|
47
|
+
|
48
|
+
# Healthcheck; ensure that Sinatra is available and not under duress
|
49
|
+
require 'sidekiq/api'
|
50
|
+
extend Health::Helpers
|
51
|
+
|
52
|
+
# Perform a health check; for this plugin that means:
|
53
|
+
#
|
54
|
+
# Is Sidekiq running?
|
55
|
+
# Is the queue size healthy?
|
56
|
+
def self.status
|
57
|
+
stats = Sidekiq::Stats.new
|
58
|
+
if stats.processes_size.zero?
|
59
|
+
health_status(:error,
|
60
|
+
'no Sidekiq process is running')
|
61
|
+
elsif stats.enqueued > 10
|
62
|
+
health_status(:warning,
|
63
|
+
"Sidekiq queue length is too high: #{stats.enqueued}")
|
64
|
+
elsif stats.default_queue_latency > 60
|
65
|
+
health_status(:warning,
|
66
|
+
"Sidekiq queue latency is too high: #{stats.default_queue_latency}")
|
67
|
+
else
|
68
|
+
health_status(:ok,
|
69
|
+
'sidekiq is okay')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Register this plugin
|
74
|
+
register_plugin 'local'
|
75
|
+
end
|
76
|
+
|
77
|
+
# A Runner may be running locally (within the API application context)
|
78
|
+
# or remotely. A job runner needs to send updates about the job status
|
79
|
+
# but obviously, a remote runner can't just update the JobRecord
|
80
|
+
# directly: they may put a message on a queue, which a job at the API
|
81
|
+
# application would consume and update the JobRecord.
|
82
|
+
#
|
83
|
+
# A Notifier provides an abstract method to update the JobRecord
|
84
|
+
# status and can also proxy LogBuffer writes.
|
85
|
+
#
|
86
|
+
module Notifier
|
87
|
+
# This is a local Notifier, so it can just pass updates directly on to
|
88
|
+
# the JobRecord & LogBuffer
|
89
|
+
class Local < Base
|
90
|
+
def initialize(job_id, callback)
|
91
|
+
@job_id = job_id
|
92
|
+
@job_record = JobRecord.find(job_id)
|
93
|
+
|
94
|
+
# Create a LogBuffer
|
95
|
+
@log_buffer = LogBuffer.new(@job_record)
|
96
|
+
|
97
|
+
@callback = callback
|
98
|
+
end
|
99
|
+
|
100
|
+
# Set the JobRecord status
|
101
|
+
def status=(status)
|
102
|
+
@job_record.status = status
|
103
|
+
@job_record.save!
|
104
|
+
|
105
|
+
# Ping the callback status_changed hook, if required
|
106
|
+
@callback.status_changed(@job_id, status) if @callback
|
107
|
+
end
|
108
|
+
|
109
|
+
# Set the JobRecord ended
|
110
|
+
def ended=(time)
|
111
|
+
@job_record.ended = time
|
112
|
+
@job_record.save!
|
113
|
+
end
|
114
|
+
|
115
|
+
# Ping the callback completion hook, if required
|
116
|
+
def completion(success)
|
117
|
+
@callback.completion(@job_id, success) if @callback
|
118
|
+
end
|
119
|
+
|
120
|
+
# Write data to the log buffer
|
121
|
+
def write(data)
|
122
|
+
@log_buffer.write data
|
123
|
+
|
124
|
+
# Ping the callback log_write hook, if required
|
125
|
+
@callback.log_write(@job_id, data) if @callback
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Namespace for any asyncronous workers
|
131
|
+
module Worker
|
132
|
+
# Local Sidekiq based worker
|
133
|
+
class Local
|
134
|
+
include Sidekiq::Worker
|
135
|
+
|
136
|
+
sidekiq_options retry: false
|
137
|
+
|
138
|
+
# Run a job Runner asynchronously
|
139
|
+
def perform(job, job_id, callback_object)
|
140
|
+
begin
|
141
|
+
# Unserialize the callback object, if there is one
|
142
|
+
callback = callback_object.nil? ? nil : Oj.load(callback_object)
|
143
|
+
|
144
|
+
notifier = Notifier::Local.new(job_id, callback)
|
145
|
+
rescue StandardError => ex
|
146
|
+
Cyclid.logger.debug "couldn't create notifier: #{ex}"
|
147
|
+
return false
|
148
|
+
end
|
149
|
+
|
150
|
+
begin
|
151
|
+
runner = Cyclid::API::Job::Runner.new(job_id, job, notifier)
|
152
|
+
success = runner.run
|
153
|
+
rescue StandardError => ex
|
154
|
+
Cyclid.logger.error "job runner failed: #{ex}"
|
155
|
+
success = false
|
156
|
+
end
|
157
|
+
|
158
|
+
# Notify completion
|
159
|
+
notifier.completion(success)
|
160
|
+
|
161
|
+
return success
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,40 @@
|
|
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 the core Cyclid code.
|
17
|
+
module Cyclid
|
18
|
+
# Module for the Cyclid API
|
19
|
+
module API
|
20
|
+
# Module for Cyclid Plugins
|
21
|
+
module Plugins
|
22
|
+
# Base class for Provisioner plugins
|
23
|
+
class Provisioner < Base
|
24
|
+
# Return the 'human' name for the plugin type
|
25
|
+
def self.human_name
|
26
|
+
'provisioner'
|
27
|
+
end
|
28
|
+
|
29
|
+
# Process the environment, performing all of the steps necasary to
|
30
|
+
# configure the host according to the given environment; this can
|
31
|
+
# include adding repositories, installing packages etc.
|
32
|
+
def prepare(_transport, _buildhost, _env = {})
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
require_rel 'provisioner/*.rb'
|
@@ -0,0 +1,90 @@
|
|
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 the core Cyclid code.
|
17
|
+
module Cyclid
|
18
|
+
# Module for the Cyclid API
|
19
|
+
module API
|
20
|
+
# Module for Cyclid Plugins
|
21
|
+
module Plugins
|
22
|
+
# Debian provisioner
|
23
|
+
class Debian < Provisioner
|
24
|
+
# Prepare a Debian based build host
|
25
|
+
def prepare(transport, buildhost, env = {})
|
26
|
+
transport.export_env('DEBIAN_FRONTEND' => 'noninteractive')
|
27
|
+
|
28
|
+
if env.key? :repos
|
29
|
+
env[:repos].each do |repo|
|
30
|
+
next unless repo.key? :url
|
31
|
+
|
32
|
+
url = repo[:url]
|
33
|
+
match = url.match(/\A(http|https):.*\Z/)
|
34
|
+
next unless match
|
35
|
+
|
36
|
+
case match[1]
|
37
|
+
when 'http', 'https'
|
38
|
+
add_http_repository(transport, url, repo, buildhost)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
success = transport.exec 'sudo apt-get update'
|
43
|
+
raise 'failed to update repositories' unless success
|
44
|
+
end
|
45
|
+
|
46
|
+
env[:packages].each do |package|
|
47
|
+
success = transport.exec \
|
48
|
+
"sudo -E apt-get install -y #{package}"
|
49
|
+
raise "failed to install package #{package}" unless success
|
50
|
+
end if env.key? :packages
|
51
|
+
rescue StandardError => ex
|
52
|
+
Cyclid.logger.error "failed to provision #{buildhost[:name]}: #{ex}"
|
53
|
+
raise
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def add_http_repository(transport, url, repo, buildhost)
|
59
|
+
raise 'an HTTP repository must provide a list of components' \
|
60
|
+
unless repo.key? :components
|
61
|
+
|
62
|
+
# Create a sources.list.d fragment
|
63
|
+
release = buildhost[:release]
|
64
|
+
components = repo[:components]
|
65
|
+
fragment = "deb #{url} #{release} #{components}"
|
66
|
+
|
67
|
+
success = transport.exec \
|
68
|
+
"echo '#{fragment}' | sudo tee -a /etc/apt/sources.list.d/cyclid.list"
|
69
|
+
raise "failed to add repository #{url}" unless success
|
70
|
+
|
71
|
+
if repo.key? :key_id
|
72
|
+
# Import the signing key
|
73
|
+
key_id = repo[:key_id]
|
74
|
+
|
75
|
+
success = transport.exec \
|
76
|
+
"gpg --keyserver keyserver.ubuntu.com --recv-keys #{key_id}"
|
77
|
+
raise "failed to import key #{key_id}" unless success
|
78
|
+
|
79
|
+
success = transport.exec \
|
80
|
+
"gpg -a --export #{key_id} | sudo apt-key add -"
|
81
|
+
raise "failed to add repository key #{key_id}" unless success
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Register this plugin
|
86
|
+
register_plugin 'debian'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|