cpflow 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/check_cpln_links.yml +19 -0
- data/.github/workflows/command_docs.yml +24 -0
- data/.github/workflows/rspec-shared.yml +56 -0
- data/.github/workflows/rspec.yml +28 -0
- data/.github/workflows/rubocop.yml +24 -0
- data/.gitignore +18 -0
- data/.overcommit.yml +16 -0
- data/.rubocop.yml +22 -0
- data/.simplecov_spawn.rb +10 -0
- data/CHANGELOG.md +259 -0
- data/CONTRIBUTING.md +73 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +126 -0
- data/LICENSE +21 -0
- data/README.md +546 -0
- data/Rakefile +21 -0
- data/bin/cpflow +6 -0
- data/cpflow +6 -0
- data/cpflow.gemspec +41 -0
- data/docs/assets/grafana-alert.png +0 -0
- data/docs/assets/memcached.png +0 -0
- data/docs/assets/sidekiq-pre-stop-hook.png +0 -0
- data/docs/commands.md +454 -0
- data/docs/dns.md +15 -0
- data/docs/migrating.md +262 -0
- data/docs/postgres.md +436 -0
- data/docs/redis.md +128 -0
- data/docs/secrets-and-env-values.md +42 -0
- data/docs/tips.md +150 -0
- data/docs/troubleshooting.md +6 -0
- data/examples/circleci.yml +104 -0
- data/examples/controlplane.yml +159 -0
- data/lib/command/apply_template.rb +209 -0
- data/lib/command/base.rb +540 -0
- data/lib/command/build_image.rb +49 -0
- data/lib/command/cleanup_images.rb +136 -0
- data/lib/command/cleanup_stale_apps.rb +79 -0
- data/lib/command/config.rb +48 -0
- data/lib/command/copy_image_from_upstream.rb +108 -0
- data/lib/command/delete.rb +149 -0
- data/lib/command/deploy_image.rb +56 -0
- data/lib/command/doctor.rb +47 -0
- data/lib/command/env.rb +22 -0
- data/lib/command/exists.rb +23 -0
- data/lib/command/generate.rb +45 -0
- data/lib/command/info.rb +222 -0
- data/lib/command/latest_image.rb +19 -0
- data/lib/command/logs.rb +49 -0
- data/lib/command/maintenance.rb +42 -0
- data/lib/command/maintenance_off.rb +62 -0
- data/lib/command/maintenance_on.rb +62 -0
- data/lib/command/maintenance_set_page.rb +34 -0
- data/lib/command/no_command.rb +23 -0
- data/lib/command/open.rb +33 -0
- data/lib/command/open_console.rb +26 -0
- data/lib/command/promote_app_from_upstream.rb +38 -0
- data/lib/command/ps.rb +41 -0
- data/lib/command/ps_restart.rb +37 -0
- data/lib/command/ps_start.rb +51 -0
- data/lib/command/ps_stop.rb +82 -0
- data/lib/command/ps_wait.rb +40 -0
- data/lib/command/run.rb +573 -0
- data/lib/command/setup_app.rb +113 -0
- data/lib/command/test.rb +23 -0
- data/lib/command/version.rb +18 -0
- data/lib/constants/exit_code.rb +7 -0
- data/lib/core/config.rb +316 -0
- data/lib/core/controlplane.rb +552 -0
- data/lib/core/controlplane_api.rb +170 -0
- data/lib/core/controlplane_api_direct.rb +112 -0
- data/lib/core/doctor_service.rb +104 -0
- data/lib/core/helpers.rb +26 -0
- data/lib/core/shell.rb +100 -0
- data/lib/core/template_parser.rb +76 -0
- data/lib/cpflow/version.rb +6 -0
- data/lib/cpflow.rb +288 -0
- data/lib/deprecated_commands.json +9 -0
- data/lib/generator_templates/Dockerfile +27 -0
- data/lib/generator_templates/controlplane.yml +62 -0
- data/lib/generator_templates/entrypoint.sh +8 -0
- data/lib/generator_templates/templates/app.yml +21 -0
- data/lib/generator_templates/templates/postgres.yml +176 -0
- data/lib/generator_templates/templates/rails.yml +36 -0
- data/rakelib/create_release.rake +81 -0
- data/script/add_command +37 -0
- data/script/check_command_docs +3 -0
- data/script/check_cpln_links +45 -0
- data/script/rename_command +43 -0
- data/script/update_command_docs +62 -0
- data/templates/app.yml +13 -0
- data/templates/daily-task.yml +32 -0
- data/templates/maintenance.yml +25 -0
- data/templates/memcached.yml +24 -0
- data/templates/postgres.yml +32 -0
- data/templates/rails.yml +27 -0
- data/templates/redis.yml +21 -0
- data/templates/redis2.yml +37 -0
- data/templates/sidekiq.yml +38 -0
- metadata +341 -0
data/docs/redis.md
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
# Migrating Redis databases
|
2
|
+
|
3
|
+
There are two templates examples in this repo:
|
4
|
+
- `redis` - basic non-persistent template. It is good for review-apps or staging or where no persistence is required
|
5
|
+
- `redis2` - basic persistent template. Good for production where persistence is needed, but cluster is overkill.
|
6
|
+
|
7
|
+
## Option 1: use SLAVEOF (easier way)
|
8
|
+
|
9
|
+
1. create a redis workload that will accept data
|
10
|
+
2. execute `SLAVEOF source_host source_port`, if needed use `masterauth` to provide auth details
|
11
|
+
3. wait for replication to pick up all changes (usually quickly), use `INFO` or `DBSIZE` to check progress
|
12
|
+
4. stop app completely and ensure nothing is writing to any of redises
|
13
|
+
5. execute `SLAVEOF no one` to disconnect replication
|
14
|
+
6. switch `REDIS_URL` in the app to point to new server
|
15
|
+
7. start the app
|
16
|
+
|
17
|
+
## Option 2: use Redis-RIOT (harder way, where option 1 is not possible)
|
18
|
+
|
19
|
+
### General considerations:
|
20
|
+
|
21
|
+
1. Heroku uses self-signed TLS certificates, which are not verifiable. It needs special handling by setting
|
22
|
+
The tool that satisfies those criteria is [Redis-RIOT](https://developer.redis.com/riot/riot-redis/index.html)
|
23
|
+
|
24
|
+
2. We are moving to private Redis that don't have a public URL, so have to do it from a Control Plane GVC container.
|
25
|
+
|
26
|
+
The tool that satisfies those criteria is [Redis-RIOT](https://developer.redis.com/riot/riot-redis/index.html)
|
27
|
+
|
28
|
+
### Heroku Redis:
|
29
|
+
|
30
|
+
As Redis-RIOT says, master redis should have keyspace-notifications set to `KA` to be able to do live replication.
|
31
|
+
To do that:
|
32
|
+
|
33
|
+
```sh
|
34
|
+
heroku redis:keyspace-notifications -c KA -a my-app
|
35
|
+
```
|
36
|
+
|
37
|
+
Connect to heroku Redis CLI:
|
38
|
+
```sh
|
39
|
+
heroku redis:cli -a my-app
|
40
|
+
```
|
41
|
+
|
42
|
+
### Control Plane Redis:
|
43
|
+
|
44
|
+
Connect to Control Plane Redis CLI:
|
45
|
+
|
46
|
+
```sh
|
47
|
+
# open cpflow interactive shell
|
48
|
+
cpflow run bash -a my-app
|
49
|
+
|
50
|
+
# install redis CLI if you don't have it in Docker
|
51
|
+
apt-get update
|
52
|
+
apt-get install redis -y
|
53
|
+
|
54
|
+
# connect to local cloud Redis
|
55
|
+
redis-cli -u MY_CONTROL_PLANE_REDIS_URL -p 6379
|
56
|
+
```
|
57
|
+
|
58
|
+
### Useful Redis CLI commands:
|
59
|
+
|
60
|
+
Quick-check keys qty:
|
61
|
+
```
|
62
|
+
info keyspace
|
63
|
+
|
64
|
+
# Keyspace
|
65
|
+
db0:keys=9496,expires=2941,avg_ttl=77670114535
|
66
|
+
```
|
67
|
+
|
68
|
+
### Create a Control Plane sync workload
|
69
|
+
|
70
|
+
```
|
71
|
+
name: riot-redis
|
72
|
+
|
73
|
+
suspend: true
|
74
|
+
min/max scale: 1/1
|
75
|
+
|
76
|
+
firewall: all firewalls off
|
77
|
+
|
78
|
+
image: fieldengineering/riot-redis
|
79
|
+
|
80
|
+
CPU: 1 Core
|
81
|
+
RAM: 1 GB
|
82
|
+
|
83
|
+
command args:
|
84
|
+
--info
|
85
|
+
-u
|
86
|
+
rediss://...your_heroku_redis_url...
|
87
|
+
--tls-verify=NONE
|
88
|
+
replicate
|
89
|
+
-h
|
90
|
+
...your_control_plane_redis_host...
|
91
|
+
--mode
|
92
|
+
live
|
93
|
+
```
|
94
|
+
|
95
|
+
### Sync process
|
96
|
+
|
97
|
+
1. open 1st terminal window with heroku redis CLI, check keys qty
|
98
|
+
2. open 2nd terminal window with controlplane redis CLI, check keys qty
|
99
|
+
3. start sync container
|
100
|
+
4. open logs with `cpflow logs -a my-app -w riot-redis`
|
101
|
+
4. re-check keys sync qty again
|
102
|
+
5. stop sync container
|
103
|
+
|
104
|
+
Result:
|
105
|
+
```
|
106
|
+
Setting commit interval to default value (1)
|
107
|
+
Setting commit interval to default value (1)
|
108
|
+
Job: [SimpleJob: [name=snapshot-replication]] launched with the following parameters: [{}]
|
109
|
+
Executing step: [snapshot-replication]
|
110
|
+
Scanning 0% ╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0/8891 (0:00:00 / ?)Job: [SimpleJob: [name=scan-reader]] launched with the following parameters: [{}]
|
111
|
+
Executing step: [scan-reader]
|
112
|
+
Scanning 61% ━━━━━━━━━━━━━━━━╸━━━━━━━━━━ 5460/8891 (0:00:07 / 0:00:04) 780.0/sStep: [scan-reader] executed in 7s918ms
|
113
|
+
Closing with items still in queue
|
114
|
+
Job: [SimpleJob: [name=scan-reader]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 7s925ms
|
115
|
+
Scanning 100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9482/9482 (0:00:11 / 0:00:00) 862.0/s
|
116
|
+
Step: [snapshot-replication] executed in 13s333ms
|
117
|
+
Executing step: [verification]
|
118
|
+
Verifying 0% ╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0/8942 (0:00:00 / ?)Job: [SimpleJob: [name=RedisItemReader]] launched with the following parameters: [{}]
|
119
|
+
Executing step: [RedisItemReader]
|
120
|
+
Verifying 2% ╺━━━━━━━━━━━━━━━━━ 220/8942 (0:00:00 / 0:00:19) ?/s >0 T0 ≠Step: [RedisItemReader] executed in 7s521ms
|
121
|
+
Closing with items still in queue
|
122
|
+
Job: [SimpleJob: [name=RedisItemReader]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 7s522ms
|
123
|
+
Verification completed - all OK
|
124
|
+
Step: [verification] executed in 7s776ms
|
125
|
+
Job: [SimpleJob: [name=snapshot-replication]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 21s320ms
|
126
|
+
```
|
127
|
+
|
128
|
+
Total sync time ~1min
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Secrets and ENV Values
|
2
|
+
|
3
|
+
You can store ENV values used by a container (within a workload) within Control Plane at the following levels:
|
4
|
+
|
5
|
+
1. Workload Container
|
6
|
+
2. GVC
|
7
|
+
|
8
|
+
For your "review apps," it is convenient to have simple ENVs stored in plain text in your source code. You will want to
|
9
|
+
keep some ENVs, like the Rails' `SECRET_KEY_BASE`, out of your source code. For staging and production apps, you will
|
10
|
+
set these values directly at the GVC or workload levels, so none of these ENV values are committed to the source code.
|
11
|
+
|
12
|
+
For storing ENVs in the source code, we can use a level of indirection so that you can store an ENV value in your source
|
13
|
+
code like `cpln://secret/my-app-review-env-secrets.SECRET_KEY_BASE` and then have the secret value stored at the org
|
14
|
+
level, which applies to your GVCs mapped to that org.
|
15
|
+
|
16
|
+
For setting up secrets, you'll need:
|
17
|
+
|
18
|
+
- **Org-level Secret:** This is where the values will be stored.
|
19
|
+
- **GVC Identity:** An identity that must be associated with each workload that requires access to the secret.
|
20
|
+
- **Org-level Policy:** A policy that binds the identity to the secret, granting the necessary permissions for the workload to access the secret.
|
21
|
+
|
22
|
+
You can do this during the initial app setup, like this:
|
23
|
+
|
24
|
+
1. Add the template for `app` to `.controlplane/templates`
|
25
|
+
2. Ensure that the `app` template is listed in `setup_app_templates` for the app in `.controlplane/controlplane.yml`
|
26
|
+
3. Run `cpflow setup-app -a $APP_NAME`
|
27
|
+
4. The secrets, secrets policy and identity will be automatically created, along with the proper binding
|
28
|
+
5. In the Control Plane console, upper left "Manage Org" menu, click on "Secrets"
|
29
|
+
6. Find the created secret (it will be in the `$APP_PREFIX-secrets` format) and add the secret env vars there
|
30
|
+
7. Use `cpln://secret/...` in the app to access the secret env vars (e.g., `cpln://secret/$APP_PREFIX-secrets.SOME_VAR`)
|
31
|
+
|
32
|
+
Here are the manual steps for reference. We recommend that you follow the steps above:
|
33
|
+
|
34
|
+
1. In the upper left of the Control Plane console, "Manage Org" menu, click on "Secrets"
|
35
|
+
2. Create a secret with `Secret Type: Dictionary` (e.g., `my-secrets`) and add the secret env vars there
|
36
|
+
3. In the upper left "Manage GVC" menu, click on "Identities"
|
37
|
+
4. Create an identity (e.g., `my-identity`)
|
38
|
+
5. Navigate to the workload that you want to associate with the identity created
|
39
|
+
6. Click "Identity" on the left menu and select the identity created
|
40
|
+
7. In the lower left "Access Control" menu, click on "Policies"
|
41
|
+
8. Create a policy with `Target Kind: Secret` and add a binding with the `reveal` permission for the identity created
|
42
|
+
9. Use `cpln://secret/...` in the app to access the secret env vars (e.g., `cpln://secret/my-secrets.SOME_VAR`)
|
data/docs/tips.md
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
# Tips
|
2
|
+
|
3
|
+
1. [GVCs vs. Orgs](#gvcs-vs-orgs)
|
4
|
+
2. [RAM](#ram)
|
5
|
+
3. [Remote IP](#remote-ip)
|
6
|
+
4. [Secrets and ENV Values](/docs/secrets-and-env-values.md)
|
7
|
+
5. [CI](#ci)
|
8
|
+
6. [Memcached](#memcached)
|
9
|
+
7. [Sidekiq](#sidekiq)
|
10
|
+
- [Quieting Non-Critical Workers During Deployments](#quieting-non-critical-workers-during-deployments)
|
11
|
+
- [Setting Up a Pre Stop Hook](#setting-up-a-pre-stop-hook)
|
12
|
+
- [Setting Up a Liveness Probe](#setting-up-a-liveness-probe)
|
13
|
+
8. [Useful Links](#useful-links)
|
14
|
+
|
15
|
+
## GVCs vs. Orgs
|
16
|
+
|
17
|
+
- A "GVC" roughly corresponds to a Heroku "app."
|
18
|
+
- Images are available at the org level.
|
19
|
+
- Multiple GVCs within an org can use the same image.
|
20
|
+
- You can have different images within a GVC and even within a workload. This flexibility is one of the key differences
|
21
|
+
compared to Heroku apps.
|
22
|
+
|
23
|
+
## RAM
|
24
|
+
|
25
|
+
Any workload replica that reaches the max memory is terminated and restarted. You can configure alerts for workload
|
26
|
+
restarts and the percentage of memory used in the Control Plane UX.
|
27
|
+
|
28
|
+
Here are the steps for configuring an alert for the percentage of memory used:
|
29
|
+
|
30
|
+
1. Navigate to the workload that you want to configure the alert for
|
31
|
+
2. Click "Metrics" on the left menu to go to Grafana
|
32
|
+
3. On Grafana, go to the alerting page by clicking on the alert icon in the sidebar
|
33
|
+
4. Click on "New alert rule"
|
34
|
+
5. In the "Set a query and alert condition" section, select "Grafana managed alert"
|
35
|
+
6. There should be a default query named `A`
|
36
|
+
7. Change the data source of the query to `metrics`
|
37
|
+
8. Click "Code" on the top right of the query and enter `mem_used{workload="workload_name"} / mem_reserved{workload="workload_name"} * 100`
|
38
|
+
(replace `workload_name` with the name of the workload)
|
39
|
+
9. There should be a default expression named `B`, with the type `Reduce`, the function `Last`, and the input `A` (this
|
40
|
+
ensures that we're getting the last data point of the query)
|
41
|
+
10. There should be a default expression named `C`, with the type `Threshold`, and the input `B` (this is where you
|
42
|
+
configure the condition for firing the alert, e.g., `IS ABOVE 95`)
|
43
|
+
11. You can then preview the alert and check if it's firing or not based on the example time range of the query
|
44
|
+
12. In the "Alert evaluation behavior" section, you can configure how often the alert should be evaluated and for how
|
45
|
+
long the condition should be true before firing (for example, you might want the alert only to be fired if the
|
46
|
+
percentage has been above `95` for more than 20 seconds)
|
47
|
+
13. In the "Add details for your alert" section, fill out the name, folder, group, and summary for your alert
|
48
|
+
14. In the "Notifications" section, you can configure a label for the alert if you're using a custom notification policy,
|
49
|
+
but there should be a default root route for all alerts
|
50
|
+
15. Once you're done, save and exit in the top right of the page
|
51
|
+
16. Click "Contact points" on the top menu
|
52
|
+
17. Edit the `grafana-default-email` contact point and add the email where you want to receive notifications
|
53
|
+
18. You should now receive notifications for the alert in your email
|
54
|
+
|
55
|
+
![](assets/grafana-alert.png)
|
56
|
+
|
57
|
+
The steps for configuring an alert for workload restarts are almost identical, but the code for the query would be
|
58
|
+
`container_restarts`.
|
59
|
+
|
60
|
+
For more information on Grafana alerts, see: https://grafana.com/docs/grafana/latest/alerting/
|
61
|
+
|
62
|
+
## Remote IP
|
63
|
+
|
64
|
+
The actual remote IP of the workload container is in the 127.0.0.x network, so that will be the value of the
|
65
|
+
`REMOTE_ADDR` env var.
|
66
|
+
|
67
|
+
However, Control Plane additionally sets the `x-forwarded-for` and `x-envoy-external-address` headers (and others - see:
|
68
|
+
https://shakadocs.controlplane.com/concepts/security#headers). On Rails, the `ActionDispatch::RemoteIp` middleware should
|
69
|
+
pick those up and automatically populate `request.remote_ip`.
|
70
|
+
|
71
|
+
So `REMOTE_ADDR` should not be used directly, only `request.remote_ip`.
|
72
|
+
|
73
|
+
## CI
|
74
|
+
|
75
|
+
**Note:** Docker builds much slower on Apple Silicon, so try configuring CI to build the images when using Apple
|
76
|
+
hardware.
|
77
|
+
|
78
|
+
Make sure to create a profile on CI before running any `cpln` or `cpflow` commands.
|
79
|
+
|
80
|
+
```sh
|
81
|
+
CPLN_TOKEN=...
|
82
|
+
cpln profile create default --token ${CPLN_TOKEN}
|
83
|
+
```
|
84
|
+
|
85
|
+
Also, log in to the Control Plane Docker repository if building and pushing an image.
|
86
|
+
|
87
|
+
```sh
|
88
|
+
cpln image docker-login
|
89
|
+
```
|
90
|
+
|
91
|
+
## Memcached
|
92
|
+
|
93
|
+
On the workload container for Memcached (using the `memcached:alpine` image), configure the command with the args
|
94
|
+
`-l 0.0.0.0`.
|
95
|
+
|
96
|
+
To do this:
|
97
|
+
|
98
|
+
1. Navigate to the workload container for Memcached
|
99
|
+
2. Click "Command" on the top menu
|
100
|
+
3. Add the args and save
|
101
|
+
|
102
|
+
![](assets/memcached.png)
|
103
|
+
|
104
|
+
## Sidekiq
|
105
|
+
|
106
|
+
### Quieting Non-Critical Workers During Deployments
|
107
|
+
|
108
|
+
To avoid locks in migrations, we can quiet non-critical workers during deployments. Doing this early enough in the CI
|
109
|
+
allows all workers to finish jobs gracefully before deploying the new image.
|
110
|
+
|
111
|
+
There's no need to unquiet the workers, as that will happen automatically after deploying the new image.
|
112
|
+
|
113
|
+
```sh
|
114
|
+
cpflow run 'rails runner "Sidekiq::ProcessSet.new.each { |w| w.quiet! unless w[%q(hostname)].start_with?(%q(criticalworker.)) }"' -a my-app
|
115
|
+
```
|
116
|
+
|
117
|
+
### Setting Up a Pre Stop Hook
|
118
|
+
|
119
|
+
By setting up a pre stop hook in the lifecycle of the workload container for Sidekiq, which sends "QUIET" to the workers,
|
120
|
+
we can ensure that all workers will finish jobs gracefully before Control Plane stops the replica. That also works
|
121
|
+
nicely for multiple replicas.
|
122
|
+
|
123
|
+
A couple of notes:
|
124
|
+
|
125
|
+
- We can't use the process name as regex because it's Ruby, not Sidekiq.
|
126
|
+
- We need to add a space after `sidekiq`; otherwise, it sends `TSTP` to the `sidekiqswarm` process as well, and for some
|
127
|
+
reason, that doesn't work.
|
128
|
+
|
129
|
+
So with `^` and `\s`, we guarantee it's sent only to worker processes.
|
130
|
+
|
131
|
+
```sh
|
132
|
+
pkill -TSTP -f ^sidekiq\s
|
133
|
+
```
|
134
|
+
|
135
|
+
To do this:
|
136
|
+
|
137
|
+
1. Navigate to the workload container for Sidekiq
|
138
|
+
2. Click "Lifecycle" on the top menu
|
139
|
+
3. Add the command and args below "Pre Stop Hook" and save
|
140
|
+
|
141
|
+
![](assets/sidekiq-pre-stop-hook.png)
|
142
|
+
|
143
|
+
### Setting Up a Liveness Probe
|
144
|
+
|
145
|
+
To set up a liveness probe on port 7433, see: https://github.com/arturictus/sidekiq_alive
|
146
|
+
|
147
|
+
## Useful Links
|
148
|
+
|
149
|
+
- For best practices for the app's Dockerfile, see: https://lipanski.com/posts/dockerfile-ruby-best-practices
|
150
|
+
- For migrating from Heroku Postgres to RDS, see: https://pelle.io/posts/hetzner-rds-postgres
|
@@ -0,0 +1,6 @@
|
|
1
|
+
# Troubleshooting
|
2
|
+
|
3
|
+
|
4
|
+
## App Web Page Shows `upstream request timeout`
|
5
|
+
|
6
|
+
If you get a blank screen showing the message `upstream request timeout` on your app after running `cpflow open -a my-app-name`, check out the application logs. Your image has been promoted and your app crashing when starting.
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# Example config for staging app:
|
2
|
+
# - triggers on push to master
|
3
|
+
# - static app name
|
4
|
+
# - no resource provisioning
|
5
|
+
# - no database setup, only migration
|
6
|
+
build-staging:
|
7
|
+
docker:
|
8
|
+
- image: cimg/ruby:3.1-node
|
9
|
+
resource_class: large
|
10
|
+
environment:
|
11
|
+
CPLN_ORG: my-org
|
12
|
+
APP_NAME: my-app-staging
|
13
|
+
steps:
|
14
|
+
- checkout
|
15
|
+
- setup_remote_docker:
|
16
|
+
docker_layer_caching: true
|
17
|
+
- run:
|
18
|
+
name: Install Control Plane tools
|
19
|
+
command: |
|
20
|
+
sudo npm install -g @controlplane/cli && cpln --version
|
21
|
+
cpln profile create default --token ${CPLN_TOKEN} --org ${CPLN_ORG} --gvc ${APP_NAME}
|
22
|
+
cpln image docker-login
|
23
|
+
|
24
|
+
gem install cpflow
|
25
|
+
- run:
|
26
|
+
name: Containerize and push image
|
27
|
+
command: cpflow build-image -a ${APP_NAME}
|
28
|
+
- run:
|
29
|
+
name: Database tasks
|
30
|
+
command: cpflow run -a ${APP_NAME} --image latest -- rails db:migrate
|
31
|
+
- run:
|
32
|
+
name: Deploy image
|
33
|
+
command: cpflow deploy-image -a ${APP_NAME}
|
34
|
+
|
35
|
+
# Example config for review app:
|
36
|
+
# - triggers manually if needed
|
37
|
+
# - dynamic app name based on PR number
|
38
|
+
# - resources provisioning for new apps
|
39
|
+
# - initial database setup or migration
|
40
|
+
build-review-app:
|
41
|
+
docker:
|
42
|
+
- image: cimg/ruby:3.1-node
|
43
|
+
resource_class: large
|
44
|
+
environment:
|
45
|
+
CPLN_ORG: my-org
|
46
|
+
steps:
|
47
|
+
- checkout
|
48
|
+
- setup_remote_docker:
|
49
|
+
docker_layer_caching: true
|
50
|
+
- run:
|
51
|
+
name: Setup environment
|
52
|
+
command: |
|
53
|
+
PR_NUM=$(echo $CIRCLE_PULL_REQUEST | grep -Eo '[0-9]+$')
|
54
|
+
echo "export APP_NAME=hichee-review-$PR_NUM" >> $BASH_ENV
|
55
|
+
- run:
|
56
|
+
name: Install Control Plane tools
|
57
|
+
command: |
|
58
|
+
sudo npm install -g @controlplane/cli && cpln --version
|
59
|
+
cpln profile create default --token ${CPLN_TOKEN} --org ${CPLN_ORG} --gvc ${APP_NAME}
|
60
|
+
cpln image docker-login
|
61
|
+
|
62
|
+
gem install cpflow
|
63
|
+
- run:
|
64
|
+
name: Provision review app if needed
|
65
|
+
command: |
|
66
|
+
if ! cpflow exists -a ${APP_NAME}; then
|
67
|
+
cpflow setup-app -a ${APP_NAME}
|
68
|
+
echo "export NEW_APP=true" >> $BASH_ENV
|
69
|
+
fi
|
70
|
+
- run:
|
71
|
+
name: Containerize and push image
|
72
|
+
command: |
|
73
|
+
cpflow build-image -a ${APP_NAME} --commit ${CIRCLE_SHA1::7}
|
74
|
+
- run:
|
75
|
+
name: Database tasks
|
76
|
+
command: |
|
77
|
+
if [ -n "${NEW_APP}" ]; then
|
78
|
+
cpflow run -a ${APP_NAME} --image latest -- rails db:reset
|
79
|
+
else
|
80
|
+
cpflow run -a ${APP_NAME} --image latest -- rails db:migrate
|
81
|
+
fi
|
82
|
+
- run:
|
83
|
+
name: Deploy image
|
84
|
+
command: cpflow deploy-image -a ${APP_NAME}
|
85
|
+
|
86
|
+
review-app:
|
87
|
+
jobs:
|
88
|
+
- start:
|
89
|
+
filters:
|
90
|
+
branches:
|
91
|
+
ignore: master
|
92
|
+
type: approval
|
93
|
+
- build-review-app:
|
94
|
+
filters:
|
95
|
+
branches:
|
96
|
+
ignore: master
|
97
|
+
requires:
|
98
|
+
- start
|
99
|
+
staging:
|
100
|
+
jobs:
|
101
|
+
- build-staging:
|
102
|
+
filters:
|
103
|
+
branches:
|
104
|
+
only: master
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# Keys beginning with "cpln_" correspond to your settings in Control Plane.
|
2
|
+
|
3
|
+
# Global settings that apply to `cpflow` usage.
|
4
|
+
# You can opt out of allowing the use of CPLN_ORG and CPLN_APP env vars
|
5
|
+
# to avoid any accidents with the wrong org / app.
|
6
|
+
allow_org_override_by_env: true
|
7
|
+
allow_app_override_by_env: true
|
8
|
+
|
9
|
+
aliases:
|
10
|
+
common: &common
|
11
|
+
# Organization for staging and QA apps is typically set as an alias.
|
12
|
+
# Production apps will use a different organization, specified in `apps`, for security.
|
13
|
+
# Change this value to your organization name
|
14
|
+
# or set the CPLN_ORG env var and it will override this for all `cpflow` commands
|
15
|
+
# (provided that `allow_org_override_by_env` is set to `true`).
|
16
|
+
cpln_org: my-org-staging
|
17
|
+
|
18
|
+
# Control Plane offers the ability to use multiple locations.
|
19
|
+
# default_location is used for commands that require a location
|
20
|
+
# including `ps`, `run`, `apply-template`.
|
21
|
+
# This can be overridden with option --location=<location> and
|
22
|
+
# CPLN_LOCATION environment variable.
|
23
|
+
# TODO: Allow specification of multiple locations.
|
24
|
+
default_location: aws-us-east-2
|
25
|
+
|
26
|
+
# Allows running the command `cpflow setup-app`
|
27
|
+
# instead of `cpflow apply-template app redis postgres memcached rails sidekiq`.
|
28
|
+
#
|
29
|
+
# Note:
|
30
|
+
# 1. These names correspond to files in the `./controlplane/templates` directory.
|
31
|
+
# 2. Each file can contain many objects, such as in the case of templates that create a resource, like `postgres`.
|
32
|
+
# 3. While the naming often corresponds to a workload or other object name, the naming is arbitrary.
|
33
|
+
# Naming does not need to match anything other than the file name without the `.yml` extension.
|
34
|
+
setup_app_templates:
|
35
|
+
- app
|
36
|
+
- redis
|
37
|
+
- postgres
|
38
|
+
- memcached
|
39
|
+
- rails
|
40
|
+
- sidekiq
|
41
|
+
|
42
|
+
# Uncomment next line to skips secrets setup when running `cpflow setup-app`.
|
43
|
+
# skip_secrets_setup: true
|
44
|
+
|
45
|
+
# Only needed if using a custom secrets name.
|
46
|
+
# The default is '{APP_PREFIX}-secrets'. For example:
|
47
|
+
# - for an app 'my-app-staging' with `match_if_app_name_starts_with` set to `false`,
|
48
|
+
# it would be 'my-app-staging-secrets'
|
49
|
+
# - for an app 'my-app-review-1234' with `match_if_app_name_starts_with` set to `true`,
|
50
|
+
# it would be 'my-app-review-secrets'
|
51
|
+
secrets_name: my-secrets
|
52
|
+
|
53
|
+
# Only needed if using a custom secrets policy name.
|
54
|
+
# The default is '{APP_SECRETS}-policy'. For example:
|
55
|
+
# - for an app 'my-app-staging' with `match_if_app_name_starts_with` set to `false`,
|
56
|
+
# it would be 'my-app-staging-secrets-policy'
|
57
|
+
# - for an app 'my-app-review-1234' with `match_if_app_name_starts_with` set to `true`,
|
58
|
+
# it would be 'my-app-review-secrets-policy'
|
59
|
+
secrets_policy_name: my-secrets-policy
|
60
|
+
|
61
|
+
# Configure the workload name used as a template for one-off scripts, like a Heroku one-off dyno.
|
62
|
+
one_off_workload: rails
|
63
|
+
|
64
|
+
# Workloads that are for the application itself and are using application Docker images.
|
65
|
+
# These are updated with the new image when running the `deploy-image` command,
|
66
|
+
# and are also used by the `info` and `ps:` commands in order to get all of the defined workloads.
|
67
|
+
# On the other hand, if you have a workload for Redis, that would NOT use the application Docker image
|
68
|
+
# and not be listed here.
|
69
|
+
app_workloads:
|
70
|
+
- rails
|
71
|
+
- sidekiq
|
72
|
+
|
73
|
+
# Additional "service type" workloads, using non-application Docker images.
|
74
|
+
# These are only used by the `info` and `ps:` commands in order to get all of the defined workloads.
|
75
|
+
additional_workloads:
|
76
|
+
- redis
|
77
|
+
- postgres
|
78
|
+
- memcached
|
79
|
+
|
80
|
+
# Configure the workload name used when maintenance mode is on (defaults to "maintenance").
|
81
|
+
maintenance_workload: maintenance
|
82
|
+
|
83
|
+
# Fixes the remote terminal size to match the local terminal size
|
84
|
+
# when running `cpflow run`.
|
85
|
+
fix_terminal_size: true
|
86
|
+
|
87
|
+
# Sets a default CPU size for `cpflow run` jobs (can be overridden per job through `--cpu`).
|
88
|
+
# If not specified, defaults to "1" (1 core).
|
89
|
+
runner_job_default_cpu: "2"
|
90
|
+
|
91
|
+
# Sets a default memory size for `cpflow run` jobs (can be overridden per job through `--memory`).
|
92
|
+
# If not specified, defaults to "2Gi" (2 gibibytes).
|
93
|
+
runner_job_default_memory: "4Gi"
|
94
|
+
|
95
|
+
# Sets the maximum number of seconds that `cpflow run` jobs can execute before being stopped.
|
96
|
+
# If not specified, defaults to 21600 (6 hours).
|
97
|
+
runner_job_timeout: 1000
|
98
|
+
|
99
|
+
# Apps with a deployed image created before this amount of days will be listed for deletion
|
100
|
+
# when running the command `cpflow cleanup-stale-apps`.
|
101
|
+
stale_app_image_deployed_days: 5
|
102
|
+
|
103
|
+
# Images that exceed this quantity will be listed for deletion
|
104
|
+
# when running the command `cpflow cleanup-images`.
|
105
|
+
image_retention_max_qty: 20
|
106
|
+
|
107
|
+
# Images created before this amount of days will be listed for deletion
|
108
|
+
# when running the command `cpflow cleanup-images` (`image_retention_max_qty` takes precedence).
|
109
|
+
image_retention_days: 5
|
110
|
+
|
111
|
+
apps:
|
112
|
+
my-app-staging:
|
113
|
+
# Use the values from the common section above.
|
114
|
+
<<: *common
|
115
|
+
|
116
|
+
my-app-review:
|
117
|
+
<<: *common
|
118
|
+
|
119
|
+
# If `match_if_app_name_starts_with` is `true`, then use this config for app names starting with this name,
|
120
|
+
# e.g., "my-app-review-pr123", "my-app-review-anything-goes", etc.
|
121
|
+
match_if_app_name_starts_with: true
|
122
|
+
|
123
|
+
# Hooks can be either a script path that exists in the app image or a command.
|
124
|
+
# They're run in the context of `cpflow run` with the latest image.
|
125
|
+
hooks:
|
126
|
+
# Used by the command `cpflow setup-app` to run a hook after creating the app.
|
127
|
+
post_creation: bundle exec rake db:prepare
|
128
|
+
|
129
|
+
# Used by the command `cpflow delete` to run a hook before deleting the app.
|
130
|
+
pre_deletion: bundle exec rake db:drop
|
131
|
+
|
132
|
+
my-app-production:
|
133
|
+
<<: *common
|
134
|
+
|
135
|
+
# You can also opt out of allowing the use of CPLN_ORG and CPLN_APP env vars per app.
|
136
|
+
# It's recommended to leave this off for production, to avoid any accidents.
|
137
|
+
allow_org_override_by_env: false
|
138
|
+
allow_app_override_by_env: false
|
139
|
+
|
140
|
+
# Use a different organization for production.
|
141
|
+
cpln_org: my-org-production
|
142
|
+
|
143
|
+
# Allows running the command `cpflow promote-app-from-upstream -a my-app-production`
|
144
|
+
# to promote the staging app to production.
|
145
|
+
upstream: my-app-staging
|
146
|
+
|
147
|
+
# Used by the command `cpflow promote-app-from-upstream` to run a release script before deploying.
|
148
|
+
# This is relative to the `.controlplane/` directory.
|
149
|
+
release_script: release_script
|
150
|
+
|
151
|
+
# default_domain is used for commands that require a domain
|
152
|
+
# including `maintenance`, `maintenance:on`, `maintenance:off`.
|
153
|
+
default_domain: domain.com
|
154
|
+
|
155
|
+
my-app-other:
|
156
|
+
<<: *common
|
157
|
+
|
158
|
+
# You can specify a different `Dockerfile` relative to the `.controlplane/` directory (defaults to "Dockerfile").
|
159
|
+
dockerfile: ../some_other/Dockerfile
|