cpflow 3.0.0
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 +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
|
+

|
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
|
+

|
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
|
+

|
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
|