concourse-deployer 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +306 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/concourse-deployer.gemspec +30 -0
- data/lib/concourse/deployer.rb +363 -0
- data/lib/concourse/deployer/utils.rb +214 -0
- data/lib/concourse/deployer/version.rb +5 -0
- metadata +128 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e35d5f547ba50ab86ab55fa4e7767cf17ac17e051e7fc2908f192e5633528da2
|
4
|
+
data.tar.gz: bfd4742437ff9911aca9b9d7302a3df8ebb3c2e8cf83651120ad779da8ac2c7d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ff1ce32001402f065b54fe410a0f690b729a6d1a4e5ca1a73065c7daadbde7e913c040b462556b1481407781d10aeb3986d342276e623327dce915cf645713db
|
7
|
+
data.tar.gz: 24a6f65a6bd1d0facd44948f89a88c5df47b3969ab6f36e68e897883334b5e18dc48739acf8591e73a3b5711e85348c1b871b5fd811e83c5a1982a0d197548c0
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/CHANGELOG.md
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at mike.dalessio@gmail.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Mike Dalessio
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,306 @@
|
|
1
|
+
# Concourse::Deployer
|
2
|
+
|
3
|
+
Provides easy installation and maintenance of an opinionated [Concourse](https://concourse-ci.org) deployment.
|
4
|
+
|
5
|
+
- external Postgres database
|
6
|
+
- Github auth integration
|
7
|
+
- LetsEncrypt integration for SSL cert management
|
8
|
+
- Windows™ workers
|
9
|
+
|
10
|
+
Today this only supports deployment to GCP.
|
11
|
+
|
12
|
+
|
13
|
+
## TL;DR
|
14
|
+
|
15
|
+
These five commands will give you a full Concourse deployment, with user-friendly prompting for configuration to external resources like a Postgres database and Github auth.
|
16
|
+
|
17
|
+
``` sh
|
18
|
+
rake bbl:gcp:init[GCP_PROJECT_ID]
|
19
|
+
rake bbl:gcp:up
|
20
|
+
rake bosh:init
|
21
|
+
rake bosh:update
|
22
|
+
rake bosh:deploy
|
23
|
+
```
|
24
|
+
|
25
|
+
You can create and deploy a LetsEncrypt SSL cert:
|
26
|
+
|
27
|
+
``` sh
|
28
|
+
rake letsencrypt:create letsencrypt:backup letsencrypt:import
|
29
|
+
rake bosh:deploy
|
30
|
+
```
|
31
|
+
|
32
|
+
## Requirements
|
33
|
+
|
34
|
+
This gem requires:
|
35
|
+
|
36
|
+
* `bbl` ~> 6.9.0 (https://github.com/cloudfoundry/bosh-bootloader/releases)
|
37
|
+
* `bosh` ~> 5.2.0 (https://github.com/cloudfoundry/bosh-cli/releases)
|
38
|
+
* `terraform` (https://www.terraform.io/downloads.html)
|
39
|
+
* `gcloud` (https://cloud.google.com/sdk/downloads)
|
40
|
+
* `direnv` (https://direnv.net/)
|
41
|
+
* `git-crypt` (https://www.agwa.name/projects/git-crypt/)
|
42
|
+
|
43
|
+
|
44
|
+
## Installation
|
45
|
+
|
46
|
+
Add this line to your application's Gemfile:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
gem 'concourse-deployer'
|
50
|
+
```
|
51
|
+
|
52
|
+
And then run `bundle` or else install it directly with `gem install concourse-deployer`.
|
53
|
+
|
54
|
+
|
55
|
+
## Usage
|
56
|
+
|
57
|
+
In your Rakefile:
|
58
|
+
|
59
|
+
``` ruby
|
60
|
+
require "concourse/deployer"
|
61
|
+
|
62
|
+
Concourse::Deployer.new.create_tasks!
|
63
|
+
```
|
64
|
+
|
65
|
+
Available tasks:
|
66
|
+
|
67
|
+
``` sh
|
68
|
+
rake bbl:gcp:init[gcp_project_id] # initialize bosh-bootloader for GCP
|
69
|
+
rake bbl:gcp:up # terraform your environment and deploy the bosh director
|
70
|
+
rake bosh:deploy # deploy concourse
|
71
|
+
rake bosh:init # prepare the concourse bosh deployment
|
72
|
+
rake bosh:update # upload stemcells and releases to the director
|
73
|
+
rake bosh:update:ubuntu_stemcell # upload ubuntu stemcell to the director
|
74
|
+
rake letsencrypt:backup # backup web:/etc/letsencrypt to local disk
|
75
|
+
rake letsencrypt:create # create a cert
|
76
|
+
rake letsencrypt:import # import letsencrypt keys into `secrets.yml` from backup
|
77
|
+
rake letsencrypt:renew # renew the certificate
|
78
|
+
rake letsencrypt:restore # restore web:/etc/letsencrypt from backup
|
79
|
+
```
|
80
|
+
|
81
|
+
See full instructions below.
|
82
|
+
|
83
|
+
|
84
|
+
## A Note on Security
|
85
|
+
|
86
|
+
It's incredibly important that you don't risk leaking your credentials by committing them in the clear to a public git repository. This gem will use `git-crypt` to ensure files are encrypted which contain sensitive credentials.
|
87
|
+
|
88
|
+
Files which contain sensitive data:
|
89
|
+
|
90
|
+
* `service-account.key.json`
|
91
|
+
* `bbl-state.json`
|
92
|
+
* `secrets.yml`
|
93
|
+
* `cluster-creds.yml`
|
94
|
+
* the `vars` subdirectory
|
95
|
+
* `letsencrypt.tar.gz` (if you're using the letsencrypt SSL cert functionality)
|
96
|
+
|
97
|
+
You will see these files listed in `.gitattributes` invoking git-crypt for them.
|
98
|
+
|
99
|
+
|
100
|
+
## Deploying to GCP
|
101
|
+
|
102
|
+
### Step 0: create a GCP project, and create and config a Postgres database
|
103
|
+
|
104
|
+
Spin up a postgres database. Note the following information as you do so:
|
105
|
+
|
106
|
+
* password
|
107
|
+
* IP address
|
108
|
+
|
109
|
+
To set up connectivity to it, we'll first create client SSL certs, then only allow access via SSL, and finally allow inbound connections from any source IP (so long as it's via SSL).
|
110
|
+
|
111
|
+
1. Under "SSL", create a client SSL cert, and download `client-key.pem`, `client-cert.pem`, and `server-ca.pem` for later use.
|
112
|
+
2. Click "Allow only secured connections"
|
113
|
+
3. Under "Authorization", add "0.0.0.0/0" as an allowed network.
|
114
|
+
4. Under "Databases", create a database named `atc`.
|
115
|
+
|
116
|
+
Using an external db is a little annoying to do, but in the opinion of the author, it's worth it to have state persisted outside of the bosh-administered cluster, so that it can be torn down and rebuilt easily when necessary.
|
117
|
+
|
118
|
+
|
119
|
+
### Step 1: Initialize bosh-bootloader and the project directory
|
120
|
+
|
121
|
+
``` sh
|
122
|
+
$ rake bbl:gcp:init[gcp_project_id]
|
123
|
+
```
|
124
|
+
|
125
|
+
This will:
|
126
|
+
|
127
|
+
* check that required dependencies are installed,
|
128
|
+
* create an `.envrc` file with environment variables for bbl to work with GCP,
|
129
|
+
* create `.gitattributes` entries to prevent sensitive files from being committed in the clear,
|
130
|
+
* create a GCP service account, associate it with your project, and give it the necessary permissions,
|
131
|
+
* and save that GCP service account information in `service-account.key.json`
|
132
|
+
|
133
|
+
__NOTE:__ At this point, if you want to use a region/zone besides us-central1, you can edit your `.envrc`.
|
134
|
+
|
135
|
+
__NOTE:__ `service-account.key.json` contains sensitive data.
|
136
|
+
|
137
|
+
|
138
|
+
### Step 2: `bbl up`
|
139
|
+
|
140
|
+
``` sh
|
141
|
+
$ rake bbl:gcp:up
|
142
|
+
```
|
143
|
+
|
144
|
+
Go get a coffee. In about 5 minutes, this will:
|
145
|
+
|
146
|
+
* terraform a GCP environment,
|
147
|
+
* spin up VMs running a bosh director and a jumpbox (a.k.a. "bastion host"),
|
148
|
+
* create a load balancer with an external IP,
|
149
|
+
* and save your state and credentials into `bbl-state.json` and the `vars` subdirectory.
|
150
|
+
|
151
|
+
__NOTE:__ This task is idempotent. If you want to upgrade your bosh director (or stemcell) using a future version of bbl, you can re-run this (but read the bbl upgrade notes first).
|
152
|
+
|
153
|
+
__NOTE:__ `bbl-state.json` and `vars` contain sensitive data.
|
154
|
+
|
155
|
+
|
156
|
+
### Step 3: Prepare the Bosh deployment for Concourse
|
157
|
+
|
158
|
+
``` sh
|
159
|
+
$ rake bosh:init
|
160
|
+
```
|
161
|
+
|
162
|
+
This will:
|
163
|
+
|
164
|
+
* create a git submodule with a clone of [`concourse-bosh-deployment`](https://github.com/concourse/concourse-bosh-deployment)
|
165
|
+
* create a `secrets.yml` file with credentials and external configuration you'll use to access concourse
|
166
|
+
|
167
|
+
You may be prompted for several things at this step, including:
|
168
|
+
|
169
|
+
* database IP and password (noted in Step 0 above)
|
170
|
+
* database server CA, client cert, and client key filenames (downloaded in Step 0 above)
|
171
|
+
* Github OAuth2 credentials
|
172
|
+
|
173
|
+
|
174
|
+
__NOTE:__ `secrets.yml` contains sensitive data.
|
175
|
+
|
176
|
+
__NOTE:__ This task is idempotent! You can re-run this whenever you like.
|
177
|
+
|
178
|
+
__NOTE:__ If you'd like a github user, team, or org to be members of Concourse's admin team, edit `secrets.yml` and add them to the `/main_team` section.
|
179
|
+
|
180
|
+
|
181
|
+
### Step 4: Upload stemcell to the director
|
182
|
+
|
183
|
+
``` sh
|
184
|
+
$ rake bosh:update
|
185
|
+
```
|
186
|
+
|
187
|
+
This will:
|
188
|
+
|
189
|
+
* upload to the director the latest GCP stemcell
|
190
|
+
* upload all necessary Bosh releases
|
191
|
+
|
192
|
+
__NOTE:__ This task is idempotent! If you want to upgrade your releases or stemcell in the future, you should re-run this.
|
193
|
+
|
194
|
+
|
195
|
+
### Step 5: deploy!
|
196
|
+
|
197
|
+
``` sh
|
198
|
+
$ rake bosh:deploy
|
199
|
+
```
|
200
|
+
|
201
|
+
This will:
|
202
|
+
|
203
|
+
* create or update a `cluster-creds.yml` file with automatically-generated cluster credentials,
|
204
|
+
* bosh-deploy Concourse
|
205
|
+
|
206
|
+
__NOTE:__ `cluster-creds.yml` and `secrets.yml` contain sensitive data.
|
207
|
+
|
208
|
+
__NOTE:__ This task is idempotent! Yay Bosh.
|
209
|
+
|
210
|
+
|
211
|
+
## Other Fun Things This Gem Does
|
212
|
+
|
213
|
+
### Scale your Concourse deployment
|
214
|
+
|
215
|
+
Your first deployment will spin up one (1) web VM, and two (2) Linux worker VMs. But you can scale these numbers up as needed by editing the file `scale-vars.yml`, whose default contents looks like:
|
216
|
+
|
217
|
+
```yaml
|
218
|
+
---
|
219
|
+
web_instances: 1
|
220
|
+
worker_instances: 2
|
221
|
+
```
|
222
|
+
|
223
|
+
Edit this file as appropriate for your needs, and re-run `rake bosh:deploy`.
|
224
|
+
|
225
|
+
|
226
|
+
### Manage your letsencrypt SSL cert
|
227
|
+
|
228
|
+
``` sh
|
229
|
+
$ rake letsencrypt:backup
|
230
|
+
$ rake letsencrypt:create
|
231
|
+
$ rake letsencrypt:restore
|
232
|
+
$ rake letsencrypt:import
|
233
|
+
$ rake letsencrypt:renew
|
234
|
+
```
|
235
|
+
|
236
|
+
__NOTE:__ These tasks will create and use `letsencrypt.tar.gz` which contains sensitive data.
|
237
|
+
|
238
|
+
|
239
|
+
### Custom bosh ops files
|
240
|
+
|
241
|
+
If you want to perform any custom operations on the manifest, put them in a file named `operations.yml` and they'll be pulled in as the __final__ ops file during deployment.
|
242
|
+
|
243
|
+
|
244
|
+
## Upgrading `bbl`
|
245
|
+
|
246
|
+
When a new version of bosh-bootloader comes out, just [download it](https://github.com/cloudfoundry/bosh-bootloader/releases) and make sure it's in your path as `bbl` (check by running `bbl -v`) and then:
|
247
|
+
|
248
|
+
``` sh
|
249
|
+
$ rake bbl:gcp:up
|
250
|
+
```
|
251
|
+
|
252
|
+
... which will generate a new plan and then update the jumpbox, director, and cloud config. (See https://github.com/cloudfoundry/bosh-bootloader/blob/master/docs/upgrade.md for details.)
|
253
|
+
|
254
|
+
Make sure to commit into source control all the changes in your project directory (`bbl-state.json`, `vars/`, `bosh-deployment/`, etc.).
|
255
|
+
|
256
|
+
|
257
|
+
## Upgrading `concourse-bosh-deployment`
|
258
|
+
|
259
|
+
If a new version of concourse comes out, and you'd like to upgrade, first read the [release notes for Concourse](https://concourse-ci.org/download.html) to check for any relevant breaking changes.
|
260
|
+
|
261
|
+
Then:
|
262
|
+
|
263
|
+
``` sh
|
264
|
+
$ rake bosh:update:concourse_deployment
|
265
|
+
$ rake bosh:deploy
|
266
|
+
```
|
267
|
+
|
268
|
+
Make sure you commit to source control the updated git submodule.
|
269
|
+
|
270
|
+
|
271
|
+
## Contributing
|
272
|
+
|
273
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/flavorjones/concourse-deployer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
274
|
+
|
275
|
+
|
276
|
+
## License
|
277
|
+
|
278
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
279
|
+
|
280
|
+
|
281
|
+
## TODO
|
282
|
+
|
283
|
+
- [ ] update windows stemcell
|
284
|
+
- [ ] include windows worker in manifest
|
285
|
+
- [ ] deploy windows ruby tools release to the windows vms
|
286
|
+
- [x] + x_frame_options: "SAMEORIGIN"
|
287
|
+
- [x] + container_placement_strategy: random
|
288
|
+
- [ ] enable encryption https://concourse.ci/encryption.html
|
289
|
+
- [ ] allow scaling up/down by locally setting number of VMs (currently hardcoded in gem)
|
290
|
+
- [ ] start using https://github.com/dpb587/caddy-bosh-release instead of the letsencrypt rake tasks
|
291
|
+
|
292
|
+
|
293
|
+
Things to follow up on:
|
294
|
+
|
295
|
+
- [x] upgrading! ZOMG
|
296
|
+
- [ ] consider swapping secrets-wizarding and rake task for deploy for a shell script that's user-modifiable
|
297
|
+
- [ ] bbl feature for suspending/unsuspending the director VM?
|
298
|
+
- [ ] stack driver add-on?
|
299
|
+
- [ ] metrics? https://concourse-ci.org/metrics.html
|
300
|
+
- [ ] credhub for credential management? https://concourse-ci.org/creds.html
|
301
|
+
|
302
|
+
|
303
|
+
Things I'm not immediately planning to do but that might be nice:
|
304
|
+
|
305
|
+
- [ ] ops file to make the cloud-config come in under default GCP quota
|
306
|
+
- [ ] ops files for a few variations on size/cost tradeoffs
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "concourse/deployer"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'concourse/deployer/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "concourse-deployer"
|
8
|
+
spec.version = Concourse::Deployer::VERSION
|
9
|
+
spec.authors = ["Mike Dalessio"]
|
10
|
+
spec.email = ["mike.dalessio@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Rake tasks to help BOSH-deploy a Concourse CI environment.}
|
13
|
+
spec.description = %q{concourse-deployer provides an ease-of-use layer on top of bosh-bootloader and bosh, to ease the initial install process and maintenance.}
|
14
|
+
spec.homepage = "https://github.com/flavorjones/concourse-deployer"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_dependency "term-ansicolor"
|
25
|
+
spec.add_dependency "nokogiri"
|
26
|
+
|
27
|
+
spec.add_development_dependency "bundler", "~> 1.14"
|
28
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
29
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
30
|
+
end
|
@@ -0,0 +1,363 @@
|
|
1
|
+
require "concourse/deployer/version"
|
2
|
+
require "concourse/deployer/utils"
|
3
|
+
require "erb"
|
4
|
+
require "open-uri"
|
5
|
+
require "nokogiri"
|
6
|
+
require "yaml"
|
7
|
+
require "rake"
|
8
|
+
|
9
|
+
module Concourse
|
10
|
+
class Deployer
|
11
|
+
include Rake::DSL
|
12
|
+
include Concourse::Deployer::Utils
|
13
|
+
|
14
|
+
GCP_SERVICE_ACCOUNT_FILE = "service-account.key.json"
|
15
|
+
ENVRC_FILE = ".envrc"
|
16
|
+
|
17
|
+
BBL_STATE_FILE = "bbl-state.json"
|
18
|
+
BBL_VARS_DIR = "vars"
|
19
|
+
|
20
|
+
BOSH_DEPLOYMENT = "concourse"
|
21
|
+
BOSH_SECRETS = "secrets.yml"
|
22
|
+
BOSH_VARS_STORE = "cluster-creds.yml"
|
23
|
+
BOSH_OPERATIONS = "operations.yml"
|
24
|
+
|
25
|
+
CONCOURSE_SCALE_VARS = "scale-vars.yml"
|
26
|
+
|
27
|
+
LETSENCRYPT_BACKUP_FILE = "letsencrypt.tar.gz"
|
28
|
+
|
29
|
+
def bbl_init
|
30
|
+
unless_which "bbl", "https://github.com/cloudfoundry/bosh-bootloader/releases"
|
31
|
+
unless_which "bosh", "https://github.com/cloudfoundry/bosh-cli/releases"
|
32
|
+
unless_which "terraform", "https://www.terraform.io/downloads.html"
|
33
|
+
end
|
34
|
+
|
35
|
+
def bbl_gcp_prompt_for_service_account
|
36
|
+
return true unless File.exist?(GCP_SERVICE_ACCOUNT_FILE)
|
37
|
+
|
38
|
+
overwrite = prompt "A #{GCP_SERVICE_ACCOUNT_FILE} file already exists. Do you want to overwrite it? (y/n)", "n"
|
39
|
+
return !! (overwrite =~ /^y/i)
|
40
|
+
end
|
41
|
+
|
42
|
+
def bbl_gcp_init project_id
|
43
|
+
bbl_init
|
44
|
+
unless_which "gcloud", "https://cloud.google.com/sdk/downloads"
|
45
|
+
ensure_in_gitcrypt GCP_SERVICE_ACCOUNT_FILE
|
46
|
+
ensure_in_envrc "BBL_GCP_PROJECT_ID", project_id
|
47
|
+
ensure_in_envrc "BBL_IAAS", "gcp"
|
48
|
+
ensure_in_envrc "BBL_GCP_REGION", "us-central1"
|
49
|
+
ensure_in_envrc "BBL_GCP_SERVICE_ACCOUNT_KEY", GCP_SERVICE_ACCOUNT_FILE
|
50
|
+
|
51
|
+
if bbl_gcp_prompt_for_service_account
|
52
|
+
service_account_name = "concourse-bbl-service-account"
|
53
|
+
|
54
|
+
sh %Q{gcloud --project=#{project_id} iam service-accounts create '#{service_account_name}'}
|
55
|
+
sh %Q{gcloud --project=#{project_id} iam service-accounts keys create '#{GCP_SERVICE_ACCOUNT_FILE}' --iam-account '#{service_account_name}@#{project_id}.iam.gserviceaccount.com'}
|
56
|
+
sh %Q{gcloud projects add-iam-policy-binding '#{project_id}' --member 'serviceAccount:#{service_account_name}@#{project_id}.iam.gserviceaccount.com' --role 'roles/editor'}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def bbl_gcp_up
|
61
|
+
unless ENV['BBL_GCP_PROJECT_ID']
|
62
|
+
error "Environment variable BBL_GCP_PROJECT_ID is not set. Did you run `rake bbl:gcp:init` and `direnv allow`?"
|
63
|
+
end
|
64
|
+
|
65
|
+
ensure_in_gitcrypt BBL_STATE_FILE
|
66
|
+
ensure_in_gitcrypt "#{BBL_VARS_DIR}/*"
|
67
|
+
ensure_in_envrc 'eval "$(bbl print-env)"'
|
68
|
+
|
69
|
+
note ""
|
70
|
+
note "running `bbl up` on GCP ... go get a coffee."
|
71
|
+
note "(If you get an error about 'Access Not Configured', follow the URL in the error message and enable API access for your project!)"
|
72
|
+
note ""
|
73
|
+
sh "bbl plan --lb-type concourse"
|
74
|
+
sh "bbl up --lb-type concourse"
|
75
|
+
end
|
76
|
+
|
77
|
+
def bosh_init
|
78
|
+
ensure_git_submodule "https://github.com/concourse/concourse-bosh-deployment", "master"
|
79
|
+
ensure_in_gitcrypt BOSH_SECRETS
|
80
|
+
ensure_in_envrc "BOSH_DEPLOYMENT", BOSH_DEPLOYMENT
|
81
|
+
|
82
|
+
bosh_secrets do |v|
|
83
|
+
v["local_user"] = (v["local_user"] || {}).tap do |local_user|
|
84
|
+
local_user["username"] = "concourse"
|
85
|
+
local_user["password"] ||= if which "apg"
|
86
|
+
`apg -n1`.strip
|
87
|
+
else
|
88
|
+
prompt "Please enter a password"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
v["external_dns_name"] ||= prompt("Please enter a DNS name if you have one", bbl_external_ip)
|
93
|
+
|
94
|
+
v["postgres_host"] ||= prompt("External postgres host IP")
|
95
|
+
v["postgres_port"] ||= prompt("External postgres port", 5432)
|
96
|
+
v["postgres_role"] ||= prompt("External postgres role", "postgres")
|
97
|
+
v["postgres_password"] ||= prompt("External postgres password")
|
98
|
+
|
99
|
+
v["postgres_client_cert"] = (v["postgres_client_cert"] || {}).tap do |cert|
|
100
|
+
cert["certificate"] ||= prompt_for_file_contents "Path to client-cert.pem"
|
101
|
+
cert["private_key"] ||= prompt_for_file_contents "Path to client-key.pem"
|
102
|
+
end
|
103
|
+
v["postgres_ca_cert"] = (v["postgres_ca_cert"] || {}).tap do |cert|
|
104
|
+
cert["certificate"] ||= prompt_for_file_contents "Path to server-ca.pem"
|
105
|
+
end
|
106
|
+
|
107
|
+
if v["github_client"].nil?
|
108
|
+
if prompt("Would you like to configure a github oauth2 application", "n") =~ /^y/i
|
109
|
+
v["github_client"] = {}.tap do |gc|
|
110
|
+
gc["username"] = prompt "Github Client ID"
|
111
|
+
gc["password"] = prompt "Github Client Secret"
|
112
|
+
end
|
113
|
+
v["main_team"] ||= {}.tap do |mt|
|
114
|
+
mt["github_users"] ||= []
|
115
|
+
mt["github_orgs"] ||= []
|
116
|
+
mt["github_teams"] ||= []
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def bosh_update_concourse_deployment
|
124
|
+
update_git_submodule "https://github.com/concourse/concourse-bosh-deployment", "master"
|
125
|
+
end
|
126
|
+
|
127
|
+
def bosh_update_ubuntu_stemcell
|
128
|
+
bosh_update_stemcell "bosh-google-kvm-ubuntu-xenial-go_agent"
|
129
|
+
end
|
130
|
+
|
131
|
+
# def bosh_update_windows_stemcell
|
132
|
+
# bosh_update_stemcell "bosh-google-kvm-windows2012R2-go_agent"
|
133
|
+
# end
|
134
|
+
|
135
|
+
# def bosh_update_concourse_windows_release
|
136
|
+
# # bosh_update_from_git_repo "https://github.com/pivotal-cf-experimental/concourse-windows-release"
|
137
|
+
# bosh_update_release "pivotal-cf-experimental/concourse-windows-worker-release"
|
138
|
+
# end
|
139
|
+
|
140
|
+
# def bosh_update_windows_ruby_dev_tools
|
141
|
+
# # bosh_update_from_git_repo "https://github.com/flavorjones/windows-ruby-dev-tools-release"
|
142
|
+
# bosh_update_release "flavorjones/windows-ruby-dev-tools-release"
|
143
|
+
# end
|
144
|
+
|
145
|
+
# def bosh_update_windows_utilities_release
|
146
|
+
# bosh_update_release "cloudfoundry-incubator/windows-utilities-release"
|
147
|
+
# end
|
148
|
+
|
149
|
+
def bosh_deploy
|
150
|
+
unless File.exists?(BOSH_SECRETS)
|
151
|
+
error "File #{BOSH_SECRETS} does not exist. Please run `rake bosh:init` first."
|
152
|
+
end
|
153
|
+
|
154
|
+
ensure_in_gitcrypt BOSH_SECRETS
|
155
|
+
ensure_in_gitcrypt BOSH_VARS_STORE
|
156
|
+
|
157
|
+
ensure_file CONCOURSE_SCALE_VARS do |f|
|
158
|
+
f.write({"web_instances" => 1, "worker_instances" => 2}.to_yaml)
|
159
|
+
end
|
160
|
+
|
161
|
+
external_dns_name = bosh_secrets['external_dns_name']
|
162
|
+
external_url = "https://#{external_dns_name}"
|
163
|
+
|
164
|
+
# command will be run in the bosh deployment submodule's cluster directory
|
165
|
+
command = [].tap do |c|
|
166
|
+
c << "bosh deploy concourse.yml"
|
167
|
+
# c << "--no-redact" # DEBUG
|
168
|
+
c << "-l ../versions.yml"
|
169
|
+
c << "-l ../../#{BOSH_SECRETS}"
|
170
|
+
c << "--vars-store ../../#{BOSH_VARS_STORE}"
|
171
|
+
c << "-o operations/basic-auth.yml"
|
172
|
+
c << "-o operations/privileged-http.yml"
|
173
|
+
c << "-o operations/privileged-https.yml"
|
174
|
+
c << "-o operations/tls.yml"
|
175
|
+
c << "-o operations/tls-vars.yml"
|
176
|
+
c << "-o operations/web-network-extension.yml"
|
177
|
+
c << "-o operations/external-postgres.yml"
|
178
|
+
c << "-o operations/external-postgres-tls.yml"
|
179
|
+
c << "-o operations/external-postgres-client-cert.yml"
|
180
|
+
c << "-o operations/worker-ephemeral-disk.yml"
|
181
|
+
c << "-o operations/x-frame-options-sameorigin.yml"
|
182
|
+
c << "-o operations/container-placement-strategy-random.yml"
|
183
|
+
c << "-o operations/scale.yml"
|
184
|
+
c << "-o ../../#{BOSH_OPERATIONS}" if File.exists?(BOSH_OPERATIONS)
|
185
|
+
c << "-o operations/github-auth.yml" if bosh_secrets["github_client"]
|
186
|
+
c << "--var network_name=default"
|
187
|
+
c << "--var external_host='#{external_dns_name}'"
|
188
|
+
c << "--var external_url='#{external_url}'"
|
189
|
+
c << "--var web_vm_type=default"
|
190
|
+
c << "--var worker_vm_type=default"
|
191
|
+
c << "--var worker_ephemeral_disk=50GB_ephemeral_disk"
|
192
|
+
c << "--var deployment_name=#{BOSH_DEPLOYMENT}"
|
193
|
+
c << "--var web_network_name=private"
|
194
|
+
c << "--var web_network_vm_extension=lb"
|
195
|
+
c << "-l ../../#{CONCOURSE_SCALE_VARS}"
|
196
|
+
end.join(" ")
|
197
|
+
|
198
|
+
Dir.chdir("concourse-bosh-deployment/cluster") do
|
199
|
+
sh command
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def letsencrypt_create
|
204
|
+
external_dns_name = bosh_secrets['external_dns_name']
|
205
|
+
if external_dns_name == bbl_external_ip
|
206
|
+
error "Please set your external DNS name in #{BOSH_SECRETS}"
|
207
|
+
end
|
208
|
+
|
209
|
+
sh "bosh ssh web -c 'sudo chmod 777 /tmp'"
|
210
|
+
sh "bosh ssh web -c 'sudo add-apt-repository -y ppa:certbot/certbot'"
|
211
|
+
sh "bosh ssh web -c 'sudo apt-get update'"
|
212
|
+
sh "bosh ssh web -c 'sudo apt-get install -y certbot'"
|
213
|
+
begin
|
214
|
+
sh "bosh stop web"
|
215
|
+
note "logging you into the web server. run this command: sudo certbot certonly --standalone -d \"#{external_dns_name}\""
|
216
|
+
sh "bosh ssh web"
|
217
|
+
ensure
|
218
|
+
sh "bosh start web"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def letsencrypt_backup
|
223
|
+
ensure_in_gitcrypt LETSENCRYPT_BACKUP_FILE
|
224
|
+
sh %Q{bosh ssh web -c 'sudo tar -zcvf /var/tmp/#{LETSENCRYPT_BACKUP_FILE} -C /etc letsencrypt'}
|
225
|
+
sh %Q{bosh scp web:/var/tmp/#{LETSENCRYPT_BACKUP_FILE} .}
|
226
|
+
end
|
227
|
+
|
228
|
+
def letsencrypt_import
|
229
|
+
ensure_in_gitcrypt LETSENCRYPT_BACKUP_FILE
|
230
|
+
external_dns_name = bosh_secrets['external_dns_name']
|
231
|
+
|
232
|
+
begin
|
233
|
+
sh "tar -zxf #{LETSENCRYPT_BACKUP_FILE}"
|
234
|
+
note "importing certificate and private key for #{external_dns_name} ..."
|
235
|
+
bosh_secrets do |v|
|
236
|
+
v["atc_tls"] ||= {}
|
237
|
+
v["atc_tls"]["certificate"] = File.read "letsencrypt/live/#{external_dns_name}/fullchain.pem"
|
238
|
+
v["atc_tls"]["private_key"] = File.read "letsencrypt/live/#{external_dns_name}/privkey.pem"
|
239
|
+
end
|
240
|
+
ensure
|
241
|
+
sh "rm -rf letsencrypt"
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def letsencrypt_restore
|
246
|
+
ensure_in_gitcrypt LETSENCRYPT_BACKUP_FILE
|
247
|
+
sh "bosh ssh web -c 'sudo rm -rf /etc/letsencrypt /var/tmp/#{LETSENCRYPT_BACKUP_FILE}'"
|
248
|
+
sh "bosh scp #{LETSENCRYPT_BACKUP_FILE} web:/var/tmp"
|
249
|
+
sh "bosh ssh web -c 'sudo tar -zxvf /var/tmp/#{LETSENCRYPT_BACKUP_FILE} -C /etc'"
|
250
|
+
sh "bosh ssh web -c 'sudo chown -R root:root /etc/letsencrypt'"
|
251
|
+
end
|
252
|
+
|
253
|
+
def letsencrypt_renew
|
254
|
+
sh "bosh ssh web -c 'sudo chmod 1777 /tmp'" # see https://github.com/cloudfoundry/bosh-linux-stemcell-builder/issues/39
|
255
|
+
sh "bosh ssh web -c 'sudo add-apt-repository -y ppa:certbot/certbot'"
|
256
|
+
sh "bosh ssh web -c 'sudo apt-get update'"
|
257
|
+
sh "bosh ssh web -c 'sudo apt-get install -y certbot'"
|
258
|
+
begin
|
259
|
+
sh "bosh stop web"
|
260
|
+
sh "bosh ssh web -c 'sudo certbot renew'"
|
261
|
+
ensure
|
262
|
+
sh "bosh start web"
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def create_tasks!
|
267
|
+
namespace "bbl" do
|
268
|
+
namespace "gcp" do
|
269
|
+
desc "initialize bosh-bootloader for GCP"
|
270
|
+
task "init", ["gcp_project_id"] do |t, args|
|
271
|
+
gcp_project_id = args["gcp_project_id"]
|
272
|
+
unless gcp_project_id
|
273
|
+
error "You must specify an existing GCP project id, like `rake #{t.name}[unique-project-name]`"
|
274
|
+
end
|
275
|
+
bbl_gcp_init gcp_project_id
|
276
|
+
end
|
277
|
+
|
278
|
+
desc "terraform your environment and deploy the bosh director"
|
279
|
+
task "up" do
|
280
|
+
bbl_gcp_up
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
namespace "bosh" do
|
286
|
+
desc "prepare the concourse bosh deployment"
|
287
|
+
task "init" do
|
288
|
+
bosh_init
|
289
|
+
end
|
290
|
+
|
291
|
+
desc "macro task for all `update` subtasks"
|
292
|
+
task "update" => [
|
293
|
+
"bosh:update:concourse_deployment",
|
294
|
+
"bosh:update:ubuntu_stemcell",
|
295
|
+
]
|
296
|
+
|
297
|
+
namespace "update" do
|
298
|
+
desc "update the git submodule for concourse-bosh-deployment"
|
299
|
+
task "concourse_deployment" do
|
300
|
+
bosh_update_concourse_deployment
|
301
|
+
end
|
302
|
+
|
303
|
+
desc "upload ubuntu stemcell to the director"
|
304
|
+
task "ubuntu_stemcell" do
|
305
|
+
bosh_update_ubuntu_stemcell
|
306
|
+
end
|
307
|
+
|
308
|
+
# desc "upload windows stemcell to the director"
|
309
|
+
# task "windows_stemcell" do
|
310
|
+
# bosh_update_windows_stemcell
|
311
|
+
# end
|
312
|
+
|
313
|
+
# desc "upload concourse windows release to the director"
|
314
|
+
# task "concourse_windows_release" do
|
315
|
+
# bosh_update_concourse_windows_release
|
316
|
+
# end
|
317
|
+
|
318
|
+
# desc "upload windows-ruby-dev-tools release to the director"
|
319
|
+
# task "windows_ruby_dev_tools" do
|
320
|
+
# bosh_update_windows_ruby_dev_tools
|
321
|
+
# end
|
322
|
+
|
323
|
+
# desc "upload windows-utilities release to the director"
|
324
|
+
# task "windows_utilities_release" do
|
325
|
+
# bosh_update_windows_utilities_release
|
326
|
+
# end
|
327
|
+
end
|
328
|
+
|
329
|
+
desc "deploy concourse"
|
330
|
+
task "deploy" do
|
331
|
+
bosh_deploy
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
namespace "letsencrypt" do
|
336
|
+
desc "create a cert"
|
337
|
+
task "create" do
|
338
|
+
letsencrypt_create
|
339
|
+
end
|
340
|
+
|
341
|
+
desc "backup web:/etc/letsencrypt to local disk"
|
342
|
+
task "backup" do
|
343
|
+
letsencrypt_backup
|
344
|
+
end
|
345
|
+
|
346
|
+
desc "import letsencrypt keys into `#{BOSH_SECRETS}` from backup"
|
347
|
+
task "import" do
|
348
|
+
letsencrypt_import
|
349
|
+
end
|
350
|
+
|
351
|
+
desc "restore web:/etc/letsencrypt from backup"
|
352
|
+
task "restore" do
|
353
|
+
letsencrypt_restore
|
354
|
+
end
|
355
|
+
|
356
|
+
desc "renew the certificate"
|
357
|
+
task "renew" do
|
358
|
+
letsencrypt_renew
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
@@ -0,0 +1,214 @@
|
|
1
|
+
require 'term/ansicolor'
|
2
|
+
|
3
|
+
module Concourse
|
4
|
+
class Deployer
|
5
|
+
module Utils
|
6
|
+
include Term::ANSIColor
|
7
|
+
|
8
|
+
GITIGNORE_FILE = ".gitignore"
|
9
|
+
GITATTRIBUTES_FILE = ".gitattributes"
|
10
|
+
|
11
|
+
def sh command
|
12
|
+
running "(in #{Dir.pwd}) #{command}"
|
13
|
+
super command, verbose: false
|
14
|
+
end
|
15
|
+
|
16
|
+
def running message
|
17
|
+
print bold, red, "RUNNING: ", reset, message, "\n"
|
18
|
+
end
|
19
|
+
|
20
|
+
def note message
|
21
|
+
print bold, green, "NOTE: ", reset, message, "\n"
|
22
|
+
end
|
23
|
+
|
24
|
+
def important message
|
25
|
+
print bold, "NOTE: ", message, reset, "\n"
|
26
|
+
end
|
27
|
+
|
28
|
+
def error message, continue=false
|
29
|
+
print red, bold, "ERROR: #{message}", reset, "\n"
|
30
|
+
exit 1 unless continue
|
31
|
+
end
|
32
|
+
|
33
|
+
def ensure_file filename, &block
|
34
|
+
return if File.exist?(filename)
|
35
|
+
File.open(filename, "w") do |f|
|
36
|
+
block.call f
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def ensure_in_gitignore file_glob
|
41
|
+
if File.exist?(GITIGNORE_FILE)
|
42
|
+
if File.read(GITIGNORE_FILE).split("\n").include?(file_glob)
|
43
|
+
note "found '#{file_glob}' already present in #{GITIGNORE_FILE}"
|
44
|
+
return
|
45
|
+
end
|
46
|
+
end
|
47
|
+
note "adding '#{file_glob}' to #{GITIGNORE_FILE}"
|
48
|
+
File.open(GITIGNORE_FILE, "a") { |f| f.puts file_glob }
|
49
|
+
end
|
50
|
+
|
51
|
+
def ensure_in_gitcrypt file_glob
|
52
|
+
crypt_entry = "#{file_glob} filter=git-crypt diff=git-crypt"
|
53
|
+
if File.exist?(GITATTRIBUTES_FILE)
|
54
|
+
if File.read(GITATTRIBUTES_FILE).split("\n").include?(crypt_entry)
|
55
|
+
note "found '#{file_glob}' already git-crypted in #{GITATTRIBUTES_FILE}"
|
56
|
+
return
|
57
|
+
end
|
58
|
+
end
|
59
|
+
note "adding '#{file_glob}' as git-crypted to #{GITATTRIBUTES_FILE}"
|
60
|
+
File.open(GITATTRIBUTES_FILE, "a") { |f| f.puts crypt_entry }
|
61
|
+
end
|
62
|
+
|
63
|
+
def ensure_in_envrc entry_key, entry_value=nil
|
64
|
+
entries = if File.exist?(ENVRC_FILE)
|
65
|
+
File.read(ENVRC_FILE).split("\n")
|
66
|
+
else
|
67
|
+
Array.new
|
68
|
+
end
|
69
|
+
|
70
|
+
if entry_value
|
71
|
+
#
|
72
|
+
# set an env var
|
73
|
+
#
|
74
|
+
entry_match = /^export #{entry_key}=/
|
75
|
+
entry_contents = "export #{entry_key}=#{entry_value}"
|
76
|
+
|
77
|
+
found_entry = entries.grep(entry_match).first
|
78
|
+
|
79
|
+
if found_entry.nil?
|
80
|
+
note "adding '#{entry_key}=#{entry_value}' to #{ENVRC_FILE}"
|
81
|
+
File.open(ENVRC_FILE, "a") { |f| f.puts entry_contents }
|
82
|
+
else
|
83
|
+
if found_entry == entry_contents
|
84
|
+
note "found '#{entry_key}=#{entry_value}' already present in #{ENVRC_FILE}"
|
85
|
+
return
|
86
|
+
else
|
87
|
+
note "overwriting '#{entry_key}' entry with '#{entry_value}' in #{ENVRC_FILE}"
|
88
|
+
entries.map! do |jentry|
|
89
|
+
jentry =~ entry_match ? entry_contents : jentry
|
90
|
+
end
|
91
|
+
File.open(ENVRC_FILE, "w") { |f| f.puts entries.join("\n") }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
else
|
95
|
+
#
|
96
|
+
# add a line of bash
|
97
|
+
#
|
98
|
+
entry_contents = entry_key
|
99
|
+
found_entry = entries.find { |line| line == entry_contents }
|
100
|
+
|
101
|
+
if found_entry.nil?
|
102
|
+
note "adding '#{entry_contents}' to #{ENVRC_FILE}"
|
103
|
+
File.open(ENVRC_FILE, "a") { |f| f.puts entry_contents }
|
104
|
+
else
|
105
|
+
note "found '#{entry_contents}' already present in #{ENVRC_FILE}"
|
106
|
+
return
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def ensure_git_submodule repo_url, commitish
|
112
|
+
repo_name = File.basename repo_url
|
113
|
+
sh "git submodule add '#{repo_url}'" unless Dir.exists?(repo_name)
|
114
|
+
Dir.chdir(repo_name) do
|
115
|
+
sh "git checkout '#{commitish}'"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def update_git_submodule repo_url, commitish
|
120
|
+
ensure_git_submodule repo_url, commitish
|
121
|
+
|
122
|
+
repo_name = File.basename repo_url
|
123
|
+
Dir.chdir(repo_name) do
|
124
|
+
sh "git remote update"
|
125
|
+
sh "git pull --rebase"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def which command
|
130
|
+
found = `which #{command}`
|
131
|
+
return $?.success? ? found : nil
|
132
|
+
end
|
133
|
+
|
134
|
+
def unless_which command, whereto
|
135
|
+
if which command
|
136
|
+
note "found command '#{command}'"
|
137
|
+
return
|
138
|
+
end
|
139
|
+
error "please install '#{command}' by visiting #{whereto}"
|
140
|
+
end
|
141
|
+
|
142
|
+
def prompt query, default=nil
|
143
|
+
loop do
|
144
|
+
message = query
|
145
|
+
message += " [#{default}]" if default
|
146
|
+
message += ": "
|
147
|
+
print bold, message, reset
|
148
|
+
answer = STDIN.gets.chomp.strip
|
149
|
+
if answer.empty?
|
150
|
+
return default if default
|
151
|
+
error "Please provide an answer.", true
|
152
|
+
else
|
153
|
+
return answer
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def prompt_for_file_contents query
|
159
|
+
loop do
|
160
|
+
path = prompt query
|
161
|
+
return File.read(path) if File.exists?(path)
|
162
|
+
error("File '#{path}' does not exist.", true)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def bbl_external_ip
|
167
|
+
`bbl lbs`.split(":").last.strip
|
168
|
+
end
|
169
|
+
|
170
|
+
def bosh_secrets &block
|
171
|
+
vars = File.exists?(BOSH_SECRETS) ? YAML.load_file(BOSH_SECRETS) : {}
|
172
|
+
return vars unless block_given?
|
173
|
+
|
174
|
+
yield vars
|
175
|
+
File.open(BOSH_SECRETS, "w") { |f| f.write vars.to_yaml }
|
176
|
+
vars
|
177
|
+
end
|
178
|
+
|
179
|
+
def bosh_update_stemcell name
|
180
|
+
doc = Nokogiri::XML(open("https://bosh.io/stemcells/#{name}"))
|
181
|
+
url = doc.at_xpath("//a[contains(text(), 'Light Stemcell')]/@href")
|
182
|
+
if url.nil?
|
183
|
+
error "Could not find the latest stemcell `#{name}`"
|
184
|
+
end
|
185
|
+
sh "bosh upload-stemcell #{url}"
|
186
|
+
end
|
187
|
+
|
188
|
+
def bosh_update_release repo
|
189
|
+
doc = Nokogiri::XML(open("https://bosh.io/releases/github.com/#{repo}?all=1"))
|
190
|
+
url = doc.at_xpath("//a[contains(text(), 'Release Tarball')]/@href")
|
191
|
+
if url.nil?
|
192
|
+
error "Could not find the latest release `#{repo}`"
|
193
|
+
end
|
194
|
+
if url.value =~ %r{\A/}
|
195
|
+
url = "https://bosh.io#{url}"
|
196
|
+
end
|
197
|
+
sh "bosh upload-release #{url}"
|
198
|
+
end
|
199
|
+
|
200
|
+
def bosh_update_from_git_repo git
|
201
|
+
dirname = File.basename(git)
|
202
|
+
Dir.mktmpdir do |dir|
|
203
|
+
Dir.chdir dir do
|
204
|
+
sh "git clone '#{git}'"
|
205
|
+
Dir.chdir dirname do
|
206
|
+
sh "bosh create-release"
|
207
|
+
sh "bosh upload-release"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
metadata
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: concourse-deployer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mike Dalessio
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-01-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: term-ansicolor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nokogiri
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.14'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.14'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
description: concourse-deployer provides an ease-of-use layer on top of bosh-bootloader
|
84
|
+
and bosh, to ease the initial install process and maintenance.
|
85
|
+
email:
|
86
|
+
- mike.dalessio@gmail.com
|
87
|
+
executables: []
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- ".gitignore"
|
92
|
+
- ".rspec"
|
93
|
+
- CHANGELOG.md
|
94
|
+
- CODE_OF_CONDUCT.md
|
95
|
+
- Gemfile
|
96
|
+
- LICENSE.txt
|
97
|
+
- README.md
|
98
|
+
- Rakefile
|
99
|
+
- bin/console
|
100
|
+
- bin/setup
|
101
|
+
- concourse-deployer.gemspec
|
102
|
+
- lib/concourse/deployer.rb
|
103
|
+
- lib/concourse/deployer/utils.rb
|
104
|
+
- lib/concourse/deployer/version.rb
|
105
|
+
homepage: https://github.com/flavorjones/concourse-deployer
|
106
|
+
licenses:
|
107
|
+
- MIT
|
108
|
+
metadata: {}
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubygems_version: 3.0.1
|
125
|
+
signing_key:
|
126
|
+
specification_version: 4
|
127
|
+
summary: Rake tasks to help BOSH-deploy a Concourse CI environment.
|
128
|
+
test_files: []
|