cyclid 0.2.3 → 0.2.4
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 +4 -4
- data/README.md +15 -33
- data/app/cyclid/job/runner.rb +5 -3
- data/app/cyclid/plugins/action/log.rb +44 -0
- data/app/cyclid/plugins/builder/google.rb +200 -0
- data/app/cyclid/plugins/provisioner/debian.rb +4 -4
- data/app/cyclid/plugins/provisioner/ubuntu.rb +8 -5
- data/app/cyclid/plugins/source/git.rb +1 -1
- data/app/cyclid/plugins/transport/ssh.rb +5 -1
- data/bin/cyclid-db-migrate +61 -0
- data/db/migrate/20160208175128_create_organization.rb +8 -0
- data/db/migrate/20160209092652_create_user.rb +10 -0
- data/db/migrate/20160211172351_create_joins.rb +8 -0
- data/db/migrate/20160214105837_create_user_permissions.rb +12 -0
- data/db/migrate/20160216165211_create_stage.rb +10 -0
- data/db/migrate/20160216165454_create_step.rb +10 -0
- data/db/migrate/20160225133151_create_job_record.rb +19 -0
- data/db/migrate/20160308145556_create_plugin_config.rb +12 -0
- data/db/migrate/20160325120748_add_encryption_to_organizations.rb +10 -0
- data/db/migrate/20160715161156_add_name_to_users.rb +5 -0
- data/db/migrate/20161218133843_modify_job_log.rb +7 -0
- data/db/schema.rb +2 -2
- data/lib/cyclid/version.rb +1 -1
- metadata +48 -20
- data/app/cyclid/plugins/builder/mist.rb +0 -107
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 051d2d610dc0eb351961625c4b7803648425b8e3
|
4
|
+
data.tar.gz: f318c016a0c6e3de32faea08f8cb4ac046c781ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2b8cbf10d103bc69384c92eb32b9cb48944f6debe6bcbf2a1c37f0bcdd710c97f2077c82371373e6d57d30b15df487d0fbdd18ec9df60252335af21e0996a951
|
7
|
+
data.tar.gz: 3a518369356ca89ed5a5ab340779493ec7dd1862a4822735c596366063a9f70fd8e81934dd6a725d78e93d7c5c91e6e05d1171b792d7c7f2db715c2dd1a43228
|
data/README.md
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
Cyclid
|
2
2
|
======
|
3
|
+
# Documentation
|
4
|
+
|
5
|
+
See http://docs.cyclid.io/en/latest/ for full documentation, including [installation instructions](http://docs.cyclid.io/en/latest/server.html#installation).
|
6
|
+
|
7
|
+
# Developement
|
8
|
+
|
9
|
+
Cyclid is an Open Source project and we welcome contributions. These instructions will help you get your development environment set up to develop & test Cyclid.
|
3
10
|
|
4
|
-
|
11
|
+
## Getting started
|
5
12
|
```
|
6
|
-
$ rbenv install 2.3.
|
13
|
+
$ rbenv install 2.3.1
|
7
14
|
$ bundle install --path vendor/bundle
|
8
15
|
$ bundle exec rake db:migrate
|
9
16
|
$ CYCLID_CONFIG=config/development bundle exec bin/cyclid-db-init
|
@@ -12,43 +19,18 @@ Cyclid requires a Redis server for Sidekiq; you can either run a Redis server on
|
|
12
19
|
|
13
20
|
You can start Cyclid under Webrick with `bundle exec rake rackup`, and then start Sidekiq with `bundle exec rake sidekiq`, or you can run both under Guard with `bundle exec rake guard`.
|
14
21
|
|
15
|
-
|
22
|
+
The Cyclid development server will run on localhost port 8361, which is the standard Cyclid API port.
|
16
23
|
|
17
|
-
|
24
|
+
`cyclid-db-init` will create sqlite3 database and the initial `admin` user and `admins` group. You'll need to make a note of the HMAC secret that is automatically generated & printed so that you can configure the Cyclid client to connect to your development server.
|
25
|
+
|
26
|
+
## Testing
|
18
27
|
|
19
28
|
RSpec tests are included. Run `bundle exec rake spec` to run the tests and generate a coverage report into the `coverage` directory. The tests do not affect any databases and external API calls are mocked.
|
20
29
|
|
21
30
|
The Cyclid source code is also expected to pass Rubocop; run `bundle exec rake rubocop` to lint the code.
|
22
31
|
|
23
|
-
|
32
|
+
## Documentation
|
24
33
|
|
25
34
|
Cyclid uses YARD to generate documentation for both the Ruby API (internal Modules, Classes & Methods) and the REST API. Run `bundle exec rake doc` to generate both sets of documentation. The Ruby documentation is placed in the `doc/api` directory, and the REST API documentation is placed in the `doc/rest` directory.
|
26
35
|
|
27
|
-
|
28
|
-
|
29
|
-
The `test/db-loader.rb` script creates two users ('admin' and 'test'), and two organizations ('admins' and 'test).
|
30
|
-
|
31
|
-
### Users
|
32
|
-
|
33
|
-
An admin (Super Admin) user is configured with the following attributes:
|
34
|
-
|
35
|
-
* Username: admin
|
36
|
-
* Email: admin@example.com
|
37
|
-
* Password: password
|
38
|
-
* Secret: aasecret55
|
39
|
-
|
40
|
-
A test user is configured with the following attributes:
|
41
|
-
|
42
|
-
* Username: test
|
43
|
-
* Email: test@example.com
|
44
|
-
* Password: aapassword55
|
45
|
-
* Secret: aasecret55
|
46
|
-
|
47
|
-
### Organizations
|
48
|
-
|
49
|
-
Two Organizations are created:
|
50
|
-
|
51
|
-
* admins
|
52
|
-
* test
|
53
|
-
|
54
|
-
The admin user is a member of admins, and the test user is a member of test.
|
36
|
+
User documentation is hosted on [Read the Docs](https://readthedocs.org). The source can be found in the [Cyclid-docs](https://github.com/Cyclid/Cyclid-docs) repository.
|
data/app/cyclid/job/runner.rb
CHANGED
@@ -68,7 +68,7 @@ module Cyclid
|
|
68
68
|
|
69
69
|
# Connect a transport to the build host; the notifier is a proxy
|
70
70
|
# to the log buffer
|
71
|
-
@transport = create_transport(@build_host, @notifier)
|
71
|
+
@transport = create_transport(@build_host, @notifier, @ctx)
|
72
72
|
|
73
73
|
# Prepare the host
|
74
74
|
provisioner = create_provisioner(@build_host)
|
@@ -83,6 +83,7 @@ module Cyclid
|
|
83
83
|
end
|
84
84
|
rescue StandardError => ex
|
85
85
|
Cyclid.logger.error "job runner failed: #{ex}"
|
86
|
+
Cyclid.logger.error ex.backtrace.join("\n")
|
86
87
|
|
87
88
|
@notifier.status = FAILED
|
88
89
|
@notifier.ended = Time.now.to_s
|
@@ -190,7 +191,7 @@ module Cyclid
|
|
190
191
|
|
191
192
|
# Find a transport that can be used with the build host, create one and
|
192
193
|
# connect them together
|
193
|
-
def create_transport(build_host, log_buffer)
|
194
|
+
def create_transport(build_host, log_buffer, ctx)
|
194
195
|
# Create a Transport & connect it to the build host
|
195
196
|
host, username, password, key = build_host.connect_info
|
196
197
|
Cyclid.logger.debug "create_transport: host: #{host} " \
|
@@ -213,7 +214,8 @@ module Cyclid
|
|
213
214
|
user: username,
|
214
215
|
password: password,
|
215
216
|
key: key,
|
216
|
-
log: log_buffer
|
217
|
+
log: log_buffer,
|
218
|
+
ctx: ctx)
|
217
219
|
raise 'failed to connect the transport' unless transport
|
218
220
|
|
219
221
|
return transport
|
@@ -0,0 +1,44 @@
|
|
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
|
+
# "Log" plugin; will always succeed. Simply emits a message to the log.
|
23
|
+
class Log < Action
|
24
|
+
def initialize(args = {})
|
25
|
+
args.symbolize_keys!
|
26
|
+
|
27
|
+
# There must be a message to log.
|
28
|
+
raise 'a log action requires a message' unless args.include? :message
|
29
|
+
|
30
|
+
@message = args[:message]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Write the log message, with the context data interpolated
|
34
|
+
def perform(log)
|
35
|
+
log.write(@message % @ctx)
|
36
|
+
true
|
37
|
+
end
|
38
|
+
|
39
|
+
# Register this plugin
|
40
|
+
register_plugin 'log'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,200 @@
|
|
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 'fog/google'
|
17
|
+
require 'securerandom'
|
18
|
+
|
19
|
+
# Top level module for the core Cyclid code.
|
20
|
+
module Cyclid
|
21
|
+
# Module for the Cyclid API
|
22
|
+
module API
|
23
|
+
# Module for Cyclid Plugins
|
24
|
+
module Plugins
|
25
|
+
# Google build host
|
26
|
+
class GoogleHost < BuildHost
|
27
|
+
# SSH is the only acceptable Transport
|
28
|
+
def transports
|
29
|
+
['ssh']
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Google builder. Creates Google Compute instances.
|
34
|
+
class Google < Builder
|
35
|
+
def initialize
|
36
|
+
@config = load_google_config(Cyclid.config.plugins)
|
37
|
+
Cyclid.logger.debug "config=#{@config.inspect}"
|
38
|
+
@api = Fog::Compute.new(provider: 'Google',
|
39
|
+
google_project: @config[:project],
|
40
|
+
google_client_email: @config[:client_email],
|
41
|
+
google_json_key_location: @config[:json_key_location])
|
42
|
+
end
|
43
|
+
|
44
|
+
# Create & return a build host
|
45
|
+
def get(args = {})
|
46
|
+
args.symbolize_keys!
|
47
|
+
|
48
|
+
Cyclid.logger.debug "google: 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
|
+
name = create_name
|
63
|
+
|
64
|
+
source_image, size = find_source_image(distro, release)
|
65
|
+
disk = create_disk(name, size, source_image)
|
66
|
+
instance = create_instance(name, disk)
|
67
|
+
|
68
|
+
Cyclid.logger.debug "instance=#{instance.inspect}"
|
69
|
+
|
70
|
+
GoogleHost.new(name: name,
|
71
|
+
host: instance.public_ip_address,
|
72
|
+
username: @config[:username],
|
73
|
+
workspace: "/home/#{@config[:username]}",
|
74
|
+
password: nil,
|
75
|
+
key: @config[:ssh_private_key],
|
76
|
+
distro: distro,
|
77
|
+
release: release)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Destroy the build host
|
81
|
+
def release(_transport, buildhost)
|
82
|
+
name = buildhost['name']
|
83
|
+
|
84
|
+
instance = @api.servers.get(name)
|
85
|
+
raise "instance #{name} does not exist" unless instance
|
86
|
+
|
87
|
+
Cyclid.logger.info "destroying #{name}"
|
88
|
+
raise 'failed to destroy instance' unless instance.destroy
|
89
|
+
end
|
90
|
+
|
91
|
+
# Register this plugin
|
92
|
+
register_plugin 'google'
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
# Load the config for the Google plugin and set defaults if they're not
|
97
|
+
# in the config
|
98
|
+
def load_google_config(config)
|
99
|
+
config.symbolize_keys!
|
100
|
+
|
101
|
+
google_config = config[:google] || {}
|
102
|
+
google_config.symbolize_keys!
|
103
|
+
Cyclid.logger.debug "config=#{google_config}"
|
104
|
+
|
105
|
+
raise 'the Google project must be provided' \
|
106
|
+
unless google_config.key? :project
|
107
|
+
|
108
|
+
raise 'the Google client email must be provided' \
|
109
|
+
unless google_config.key? :client_email
|
110
|
+
|
111
|
+
raise 'the JSON key location must be provided' \
|
112
|
+
unless google_config.key? :json_key_location
|
113
|
+
|
114
|
+
# Set defaults
|
115
|
+
google_config[:zone] = 'us-central1-a' unless google_config.key? :zone
|
116
|
+
google_config[:machine_type] = 'f1-micro' unless google_config.key? :machine_type
|
117
|
+
google_config[:network] = 'default' unless google_config.key? :network
|
118
|
+
google_config[:username] = 'build' unless google_config.key? :username
|
119
|
+
google_config[:ssh_private_key] = File.join(%w(/ etc cyclid id_rsa_build)) \
|
120
|
+
unless google_config.key? :ssh_private_key
|
121
|
+
google_config[:ssh_public_key] = File.join(%w(/ etc cyclid id_rsa_build.pub)) \
|
122
|
+
unless google_config.key? :ssh_public_key
|
123
|
+
google_config[:instance_name] = 'cyclid-build' \
|
124
|
+
unless google_config.key? :instance_name
|
125
|
+
|
126
|
+
return google_config
|
127
|
+
end
|
128
|
+
|
129
|
+
# Generate a unique name for the build host
|
130
|
+
def create_name
|
131
|
+
base = @config[:instance_name]
|
132
|
+
"#{base}-#{SecureRandom.hex(16)}"
|
133
|
+
end
|
134
|
+
|
135
|
+
# Map the distro & release to a source image
|
136
|
+
def find_source_image(distro, release)
|
137
|
+
Cyclid.logger.debug 'attempting to find source image'
|
138
|
+
|
139
|
+
source_image = nil
|
140
|
+
disk_size = 0
|
141
|
+
|
142
|
+
@api.images.all.each do |image|
|
143
|
+
next if image.deprecated
|
144
|
+
|
145
|
+
match = image.name.match(/^#{distro}-((\d*)-(.*)-v.*$|(\d*)-v.*$)/)
|
146
|
+
next unless match
|
147
|
+
next unless match[2] == release or match[3] == release
|
148
|
+
|
149
|
+
# Found one
|
150
|
+
Cyclid.logger.info "found image #{image.name} for #{distro}:#{release}"
|
151
|
+
source_image = image.name
|
152
|
+
disk_size = image.disk_size_gb
|
153
|
+
|
154
|
+
break
|
155
|
+
end
|
156
|
+
|
157
|
+
# If we didn't find a disk, we have to stop now
|
158
|
+
raise "could not find suitable source image for #{distro}:#{release}" \
|
159
|
+
unless source_image
|
160
|
+
|
161
|
+
return source_image, disk_size
|
162
|
+
end
|
163
|
+
|
164
|
+
# Create a new disk image from the source
|
165
|
+
def create_disk(name, size, source_image)
|
166
|
+
disk = @api.disks.create(name: name,
|
167
|
+
size_gb: size,
|
168
|
+
zone_name: @config[:zone],
|
169
|
+
source_image: source_image)
|
170
|
+
|
171
|
+
Cyclid.logger.info 'waiting for disk...'
|
172
|
+
disk.wait_for { disk.ready? }
|
173
|
+
|
174
|
+
return disk
|
175
|
+
end
|
176
|
+
|
177
|
+
# Create a compute instance
|
178
|
+
def create_instance(name, disk)
|
179
|
+
Cyclid.logger.info "creating instance #{name}"
|
180
|
+
instance = @api.servers.bootstrap(name: name,
|
181
|
+
disks: [disk],
|
182
|
+
machine_type: @config[:machine_type],
|
183
|
+
zone_name: @config[:zone],
|
184
|
+
network: @config[:network],
|
185
|
+
username: @config[:username],
|
186
|
+
public_key_path: @config[:ssh_public_key],
|
187
|
+
private_key_path: @config[:ssh_private_key],
|
188
|
+
tags: ['build', 'build-host'])
|
189
|
+
|
190
|
+
device_name = instance.disks[0]['deviceName']
|
191
|
+
instance.set_disk_auto_delete(true, device_name)
|
192
|
+
|
193
|
+
instance.wait_for { instance.sshable? }
|
194
|
+
|
195
|
+
return instance
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
@@ -39,13 +39,13 @@ module Cyclid
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
success = transport.exec '
|
42
|
+
success = transport.exec 'apt-get update'
|
43
43
|
raise 'failed to update repositories' unless success
|
44
44
|
end
|
45
45
|
|
46
46
|
if env.key? :packages
|
47
47
|
success = transport.exec \
|
48
|
-
"
|
48
|
+
"apt-get install -y #{env[:packages].join(' ')}" \
|
49
49
|
|
50
50
|
raise "failed to install packages #{env[:packages].join(' ')}" unless success
|
51
51
|
end
|
@@ -66,7 +66,7 @@ module Cyclid
|
|
66
66
|
fragment = "deb #{url} #{release} #{components}"
|
67
67
|
|
68
68
|
success = transport.exec \
|
69
|
-
"echo '#{fragment}' |
|
69
|
+
"sh -c \"echo '#{fragment}' | tee -a /etc/apt/sources.list.d/cyclid.list\""
|
70
70
|
raise "failed to add repository #{url}" unless success
|
71
71
|
|
72
72
|
return unless repo.key? :key_id
|
@@ -79,7 +79,7 @@ module Cyclid
|
|
79
79
|
raise "failed to import key #{key_id}" unless success
|
80
80
|
|
81
81
|
success = transport.exec \
|
82
|
-
"gpg -a --export #{key_id} |
|
82
|
+
"sh -c 'gpg -a --export #{key_id} | apt-key add -'"
|
83
83
|
raise "failed to add repository key #{key_id}" unless success
|
84
84
|
end
|
85
85
|
|
@@ -26,6 +26,9 @@ module Cyclid
|
|
26
26
|
transport.export_env('DEBIAN_FRONTEND' => 'noninteractive')
|
27
27
|
|
28
28
|
if env.key? :repos
|
29
|
+
# Ensure apt-get-repository is available
|
30
|
+
transport.exec 'apt-get install -y software-properties-common'
|
31
|
+
|
29
32
|
env[:repos].each do |repo|
|
30
33
|
next unless repo.key? :url
|
31
34
|
|
@@ -42,13 +45,13 @@ module Cyclid
|
|
42
45
|
end
|
43
46
|
end
|
44
47
|
|
45
|
-
success = transport.exec '
|
48
|
+
success = transport.exec 'apt-get update'
|
46
49
|
raise 'failed to update repositories' unless success
|
47
50
|
end
|
48
51
|
|
49
52
|
if env.key? :packages
|
50
53
|
success = transport.exec \
|
51
|
-
"
|
54
|
+
"apt-get install -y #{env[:packages].join(' ')}" \
|
52
55
|
|
53
56
|
raise "failed to install packages #{env[:packages].join(' ')}" unless success
|
54
57
|
end
|
@@ -60,7 +63,7 @@ module Cyclid
|
|
60
63
|
private
|
61
64
|
|
62
65
|
def add_ppa_repository(transport, url)
|
63
|
-
success = transport.exec "
|
66
|
+
success = transport.exec "apt-add-repository -y #{url}"
|
64
67
|
raise "failed to add repository #{url}" unless success
|
65
68
|
end
|
66
69
|
|
@@ -74,7 +77,7 @@ module Cyclid
|
|
74
77
|
fragment = "deb #{url} #{release} #{components}"
|
75
78
|
|
76
79
|
success = transport.exec \
|
77
|
-
"echo '#{fragment}' |
|
80
|
+
"sh -c \"echo '#{fragment}' | tee -a /etc/apt/sources.list.d/cyclid.list\""
|
78
81
|
raise "failed to add repository #{url}" unless success
|
79
82
|
|
80
83
|
return unless repo.key? :key_id
|
@@ -87,7 +90,7 @@ module Cyclid
|
|
87
90
|
raise "failed to import key #{key_id}" unless success
|
88
91
|
|
89
92
|
success = transport.exec \
|
90
|
-
"gpg -a --export #{key_id} |
|
93
|
+
"sh -c 'gpg -a --export #{key_id} | apt-key add -'"
|
91
94
|
raise "failed to add repository key #{key_id}" unless success
|
92
95
|
end
|
93
96
|
|
@@ -40,7 +40,7 @@ module Cyclid
|
|
40
40
|
# username
|
41
41
|
url.user = source[:token] if source.key? :token
|
42
42
|
|
43
|
-
success = transport.exec
|
43
|
+
success = transport.exec("git clone #{url}", ctx[:workspace])
|
44
44
|
return false unless success
|
45
45
|
|
46
46
|
# If a branch was given, check it out
|
@@ -0,0 +1,61 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
# Copyright 2016 Liqwyd Ltd.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
$LOAD_PATH.push File.expand_path('../../app', __FILE__)
|
18
|
+
|
19
|
+
require 'require_all'
|
20
|
+
require 'logger'
|
21
|
+
require 'active_record'
|
22
|
+
|
23
|
+
ENV['RACK_ENV'] = ENV['RACK_ENV'] || 'development'
|
24
|
+
|
25
|
+
require 'cyclid/config'
|
26
|
+
|
27
|
+
# Top level module for the core Cyclid code; just stub out to provide the
|
28
|
+
# bare minimum required to inject data via. the models.
|
29
|
+
module Cyclid
|
30
|
+
class << self
|
31
|
+
attr_accessor :logger, :config
|
32
|
+
|
33
|
+
begin
|
34
|
+
Cyclid.logger = Logger.new(STDERR)
|
35
|
+
|
36
|
+
config_path = ENV['CYCLID_CONFIG'] || File.join(%w(/ etc cyclid config))
|
37
|
+
Cyclid.config = API::Config.new(config_path)
|
38
|
+
rescue StandardError => ex
|
39
|
+
abort "Failed to initialize: #{ex}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
require 'db'
|
45
|
+
|
46
|
+
version = (ARGV[0].split('=')[1].to_i if ARGV[0] =~ /VERSION=d+/)
|
47
|
+
|
48
|
+
current_version = ActiveRecord::Migrator.current_version
|
49
|
+
|
50
|
+
STDERR.puts '*' * 80
|
51
|
+
STDERR.puts "Current version: #{current_version}"
|
52
|
+
STDERR.puts '*' * 80
|
53
|
+
|
54
|
+
migrations_path = File.expand_path('../../db/migrate', __FILE__)
|
55
|
+
|
56
|
+
ActiveRecord::Migration.verbose = true
|
57
|
+
ActiveRecord::Migrator.migrate(migrations_path, version) do |migration|
|
58
|
+
next unless migration.version > current_version
|
59
|
+
STDERR.puts "#{migration.name}:#{migration.version}"
|
60
|
+
true
|
61
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class CreateUserPermissions < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :userpermissions do |t|
|
4
|
+
t.boolean :admin, null: false, default: false
|
5
|
+
t.boolean :write, null: false, default: false
|
6
|
+
t.boolean :read, null: false, default: false
|
7
|
+
|
8
|
+
t.belongs_to :user, index: true
|
9
|
+
t.belongs_to :organization, index:true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateJobRecord < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :job_records do |t|
|
4
|
+
t.string :job_name, index: true
|
5
|
+
t.string :job_version
|
6
|
+
|
7
|
+
t.datetime :started
|
8
|
+
t.datetime :ended
|
9
|
+
|
10
|
+
t.integer :status
|
11
|
+
t.text :log
|
12
|
+
|
13
|
+
t.text :job
|
14
|
+
|
15
|
+
t.belongs_to :organization, index: true
|
16
|
+
t.belongs_to :user, index: true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class AddEncryptionToOrganizations < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
# RSA keys in DER format
|
4
|
+
add_column :organizations, :rsa_private_key, :binary, null: false, default: 0
|
5
|
+
add_column :organizations, :rsa_public_key, :binary, null: false, default: 0
|
6
|
+
|
7
|
+
# Salt for use with other encryption schemes E.g. AES
|
8
|
+
add_column :organizations, :salt, :string, null: false, default: 0
|
9
|
+
end
|
10
|
+
end
|
data/db/schema.rb
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
#
|
12
12
|
# It's strongly recommended that you check this file into your version control system.
|
13
13
|
|
14
|
-
ActiveRecord::Schema.define(version:
|
14
|
+
ActiveRecord::Schema.define(version: 20161218133843) do
|
15
15
|
|
16
16
|
create_table "job_records", force: :cascade do |t|
|
17
17
|
t.string "job_name"
|
@@ -19,7 +19,7 @@ ActiveRecord::Schema.define(version: 20160715161156) do
|
|
19
19
|
t.datetime "started"
|
20
20
|
t.datetime "ended"
|
21
21
|
t.integer "status"
|
22
|
-
t.text "log"
|
22
|
+
t.text "log", limit: 16777215
|
23
23
|
t.text "job"
|
24
24
|
t.integer "organization_id"
|
25
25
|
t.integer "user_id"
|
data/lib/cyclid/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cyclid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kristian Van Der Vliet
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-12-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oj
|
@@ -192,20 +192,6 @@ dependencies:
|
|
192
192
|
- - "~>"
|
193
193
|
- !ruby/object:Gem::Version
|
194
194
|
version: '4.1'
|
195
|
-
- !ruby/object:Gem::Dependency
|
196
|
-
name: mysql
|
197
|
-
requirement: !ruby/object:Gem::Requirement
|
198
|
-
requirements:
|
199
|
-
- - "~>"
|
200
|
-
- !ruby/object:Gem::Version
|
201
|
-
version: '2.9'
|
202
|
-
type: :runtime
|
203
|
-
prerelease: false
|
204
|
-
version_requirements: !ruby/object:Gem::Requirement
|
205
|
-
requirements:
|
206
|
-
- - "~>"
|
207
|
-
- !ruby/object:Gem::Version
|
208
|
-
version: '2.9'
|
209
195
|
- !ruby/object:Gem::Dependency
|
210
196
|
name: slack-notifier
|
211
197
|
requirement: !ruby/object:Gem::Requirement
|
@@ -319,23 +305,52 @@ dependencies:
|
|
319
305
|
- !ruby/object:Gem::Version
|
320
306
|
version: '0'
|
321
307
|
- !ruby/object:Gem::Dependency
|
322
|
-
name:
|
308
|
+
name: fog-core
|
323
309
|
requirement: !ruby/object:Gem::Requirement
|
324
310
|
requirements:
|
325
311
|
- - "~>"
|
326
312
|
- !ruby/object:Gem::Version
|
327
|
-
version: '
|
313
|
+
version: '1.43'
|
328
314
|
type: :runtime
|
329
315
|
prerelease: false
|
330
316
|
version_requirements: !ruby/object:Gem::Requirement
|
331
317
|
requirements:
|
332
318
|
- - "~>"
|
333
319
|
- !ruby/object:Gem::Version
|
334
|
-
version: '
|
320
|
+
version: '1.43'
|
321
|
+
- !ruby/object:Gem::Dependency
|
322
|
+
name: fog-google
|
323
|
+
requirement: !ruby/object:Gem::Requirement
|
324
|
+
requirements:
|
325
|
+
- - "~>"
|
326
|
+
- !ruby/object:Gem::Version
|
327
|
+
version: '0.4'
|
328
|
+
type: :runtime
|
329
|
+
prerelease: false
|
330
|
+
version_requirements: !ruby/object:Gem::Requirement
|
331
|
+
requirements:
|
332
|
+
- - "~>"
|
333
|
+
- !ruby/object:Gem::Version
|
334
|
+
version: '0.4'
|
335
|
+
- !ruby/object:Gem::Dependency
|
336
|
+
name: google-api-client
|
337
|
+
requirement: !ruby/object:Gem::Requirement
|
338
|
+
requirements:
|
339
|
+
- - "~>"
|
340
|
+
- !ruby/object:Gem::Version
|
341
|
+
version: 0.8.6
|
342
|
+
type: :runtime
|
343
|
+
prerelease: false
|
344
|
+
version_requirements: !ruby/object:Gem::Requirement
|
345
|
+
requirements:
|
346
|
+
- - "~>"
|
347
|
+
- !ruby/object:Gem::Version
|
348
|
+
version: 0.8.6
|
335
349
|
description: The Cyclid CI system
|
336
350
|
email: contact@cyclid.io
|
337
351
|
executables:
|
338
352
|
- cyclid-db-init
|
353
|
+
- cyclid-db-migrate
|
339
354
|
extensions: []
|
340
355
|
extra_rdoc_files: []
|
341
356
|
files:
|
@@ -380,6 +395,7 @@ files:
|
|
380
395
|
- app/cyclid/plugins/action/email.rb
|
381
396
|
- app/cyclid/plugins/action/email/html.erb
|
382
397
|
- app/cyclid/plugins/action/email/text.erb
|
398
|
+
- app/cyclid/plugins/action/log.rb
|
383
399
|
- app/cyclid/plugins/action/script.rb
|
384
400
|
- app/cyclid/plugins/action/slack.rb
|
385
401
|
- app/cyclid/plugins/action/slack/note.erb
|
@@ -394,7 +410,7 @@ files:
|
|
394
410
|
- app/cyclid/plugins/api/github/pull_request.rb
|
395
411
|
- app/cyclid/plugins/api/github/push.rb
|
396
412
|
- app/cyclid/plugins/builder.rb
|
397
|
-
- app/cyclid/plugins/builder/
|
413
|
+
- app/cyclid/plugins/builder/google.rb
|
398
414
|
- app/cyclid/plugins/dispatcher.rb
|
399
415
|
- app/cyclid/plugins/dispatcher/local.rb
|
400
416
|
- app/cyclid/plugins/provisioner.rb
|
@@ -411,6 +427,18 @@ files:
|
|
411
427
|
- app/cyclid/sinatra/warden/strategies/hmac.rb
|
412
428
|
- app/db.rb
|
413
429
|
- bin/cyclid-db-init
|
430
|
+
- bin/cyclid-db-migrate
|
431
|
+
- db/migrate/20160208175128_create_organization.rb
|
432
|
+
- db/migrate/20160209092652_create_user.rb
|
433
|
+
- db/migrate/20160211172351_create_joins.rb
|
434
|
+
- db/migrate/20160214105837_create_user_permissions.rb
|
435
|
+
- db/migrate/20160216165211_create_stage.rb
|
436
|
+
- db/migrate/20160216165454_create_step.rb
|
437
|
+
- db/migrate/20160225133151_create_job_record.rb
|
438
|
+
- db/migrate/20160308145556_create_plugin_config.rb
|
439
|
+
- db/migrate/20160325120748_add_encryption_to_organizations.rb
|
440
|
+
- db/migrate/20160715161156_add_name_to_users.rb
|
441
|
+
- db/migrate/20161218133843_modify_job_log.rb
|
414
442
|
- db/schema.rb
|
415
443
|
- lib/cyclid/app.rb
|
416
444
|
- lib/cyclid/version.rb
|
@@ -1,107 +0,0 @@
|
|
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
|