Skip to content

Commit 41a5bde

Browse files
authored
[minor] Add provider-specific cloud-compose entrypoints (#14)
1 parent da659a3 commit 41a5bde

81 files changed

Lines changed: 3788 additions & 615 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/cloud-smoke.yml

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,61 @@ jobs:
3030
- name: Run Terraform lint
3131
run: make terraform-lint-check
3232

33+
config-management-lint:
34+
name: Ansible and Salt lint
35+
runs-on: ubuntu-24.04
36+
steps:
37+
- name: Checkout repository
38+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
39+
40+
- name: Run Ansible and Salt smoke checks
41+
run: make config-management-smoke
42+
43+
config-management-cloud-smoke:
44+
name: Linode ${{ matrix.method }} Drupal
45+
needs:
46+
- terraform-lint
47+
- config-management-lint
48+
runs-on: ubuntu-24.04
49+
timeout-minutes: 150
50+
if: github.event.pull_request.head.repo.full_name == github.repository
51+
strategy:
52+
fail-fast: false
53+
matrix:
54+
method:
55+
- ansible
56+
- salt
57+
concurrency:
58+
group: cloud-compose-config-management-${{ matrix.method }}-drupal
59+
cancel-in-progress: false
60+
env:
61+
CLOUD_COMPOSE_SMOKE_AUTO_APPROVE: "true"
62+
CLOUD_COMPOSE_SMOKE_DESTROY_TIMEOUT: "1800"
63+
CLOUD_COMPOSE_SMOKE_SWEEP_ORPHANS: "true"
64+
CLOUD_COMPOSE_SMOKE_RUN_ID: ${{ github.run_id }}
65+
LINODE_TOKEN: ${{ secrets.LINODE_TOKEN }}
66+
steps:
67+
- name: Checkout repository
68+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
69+
70+
- name: Install Terraform
71+
uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4
72+
with:
73+
terraform_version: 1.14.6
74+
terraform_wrapper: false
75+
76+
- name: Run Linode config-management smoke test
77+
run: make config-management-cloud-smoke METHOD=${{ matrix.method }}
78+
79+
- name: Destroy Linode config-management smoke resources
80+
if: always()
81+
run: ci/config-management-cloud-smoke.sh destroy-${{ matrix.method }}-drupal
82+
3383
smoke:
3484
name: ${{ matrix.name }}
35-
needs: terraform-lint
85+
needs:
86+
- terraform-lint
87+
- config-management-lint
3688
runs-on: ubuntu-24.04
3789
timeout-minutes: 150
3890
if: github.event.pull_request.head.repo.full_name == github.repository
@@ -108,6 +160,30 @@ jobs:
108160
if: always() && matrix.provider == 'gcp' && (env.GCLOUD_OIDC_POOL == '' || env.GSA == '' || env.GCLOUD_PROJECT == '')
109161
run: echo "Skipping GCP destroy because GCP smoke secrets are not configured and no GCP resources were applied."
110162

163+
config-management-cleanup:
164+
name: Destroy Linode ${{ matrix.method }} Drupal config-management
165+
needs: config-management-cloud-smoke
166+
if: always() && (failure() || cancelled()) && github.event.pull_request.head.repo.full_name == github.repository
167+
runs-on: ubuntu-24.04
168+
strategy:
169+
fail-fast: false
170+
matrix:
171+
method:
172+
- ansible
173+
- salt
174+
concurrency:
175+
group: cloud-compose-config-management-${{ matrix.method }}-drupal
176+
cancel-in-progress: false
177+
env:
178+
CLOUD_COMPOSE_SMOKE_RUN_ID: ${{ github.run_id }}
179+
LINODE_TOKEN: ${{ secrets.LINODE_TOKEN }}
180+
steps:
181+
- name: Checkout repository
182+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
183+
184+
- name: Sweep Linode config-management smoke resources
185+
run: ci/config-management-cloud-smoke.sh sweep-${{ matrix.method }}-drupal
186+
111187
cleanup:
112188
name: Destroy ${{ matrix.name }}
113189
needs: smoke

Makefile

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
.PHONY: lint lint-check terraform-fmt terraform-fmt-check terraform-validate terraform-lint-check shell-lint terraform-docs smoke-test-clouds smoke-test smoke-test-digitalocean-isle smoke-test-linode-wp smoke-test-gcp-wp destroy-smoke destroy-smoke-digitalocean-isle destroy-smoke-linode-wp destroy-smoke-gcp-wp docs docs-docker-build docs-build docs-serve docs-preview docs-clean
1+
.PHONY: lint lint-check shell-lint config-management-smoke
2+
.PHONY: terraform-fmt terraform-fmt-check terraform-validate terraform-lint-check terraform-docs
3+
.PHONY: config-management-cloud-smoke config-management-cloud-smoke-ansible-drupal config-management-cloud-smoke-salt-drupal
4+
.PHONY: destroy-config-management-cloud-smoke destroy-config-management-cloud-smoke-ansible-drupal destroy-config-management-cloud-smoke-salt-drupal
5+
.PHONY: smoke-test-clouds smoke-test smoke-test-digitalocean-isle smoke-test-linode-wp smoke-test-gcp-wp
6+
.PHONY: destroy-smoke destroy-smoke-digitalocean-isle destroy-smoke-linode-wp destroy-smoke-gcp-wp
7+
.PHONY: docs docs-docker-build docs-build docs-serve docs-preview docs-clean
28

39
DOCS_IMAGE ?= cloud-compose-docs
410
DOCS_PORT ?= 8888
@@ -25,6 +31,29 @@ shell-lint:
2531
-path "./docs/site" -prune -o \
2632
-type f -name "*.sh" -print0 | xargs -0 shellcheck
2733

34+
config-management-smoke:
35+
ci/config-management-smoke.sh
36+
37+
config-management-cloud-smoke:
38+
@test -n "$(METHOD)" || { echo "METHOD is required"; exit 2; }
39+
ci/config-management-cloud-smoke.sh $(METHOD)-drupal
40+
41+
config-management-cloud-smoke-ansible-drupal:
42+
$(MAKE) config-management-cloud-smoke METHOD=ansible
43+
44+
config-management-cloud-smoke-salt-drupal:
45+
$(MAKE) config-management-cloud-smoke METHOD=salt
46+
47+
destroy-config-management-cloud-smoke:
48+
@test -n "$(METHOD)" || { echo "METHOD is required"; exit 2; }
49+
ci/config-management-cloud-smoke.sh destroy-$(METHOD)-drupal
50+
51+
destroy-config-management-cloud-smoke-ansible-drupal:
52+
$(MAKE) destroy-config-management-cloud-smoke METHOD=ansible
53+
54+
destroy-config-management-cloud-smoke-salt-drupal:
55+
$(MAKE) destroy-config-management-cloud-smoke METHOD=salt
56+
2857
terraform-docs:
2958
terraform-docs markdown table --sort-by required --output-file README.md .
3059

README.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
# cloud-compose
22

3-
Deploy Docker Compose projects to cloud VMs. The root module targets Google
4-
Cloud; provider modules under `modules/` cover DigitalOcean and Linode.
3+
Deploy Docker Compose projects to VMs. Provider-specific Terraform entrypoints
4+
under `providers/` cover GCP, DigitalOcean, and Linode without loading unused
5+
cloud providers. Existing Debian/Ubuntu hosts can consume the same runtime
6+
contract through the Ansible role or Salt formula.
7+
8+
Template defaults live in `templates/apps.json` and are shared by Terraform,
9+
Ansible, and Salt. The default deployment shape is one app per VM or host; pass
10+
`runtime.compose.projects` when several apps should share the same machine.
511

612
<!-- BEGIN_TF_DOCS -->
713
## Requirements
814

915
| Name | Version |
10-
|------|---------|
16+
| ---- | ------- |
1117
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.2.4 |
1218

1319
## Providers
@@ -17,7 +23,7 @@ No providers.
1723
## Modules
1824

1925
| Name | Source | Version |
20-
|------|--------|---------|
26+
| ---- | ------ | ------- |
2127
| <a name="module_digitalocean"></a> [digitalocean](#module\_digitalocean) | ./modules/digitalocean | n/a |
2228
| <a name="module_gcp"></a> [gcp](#module\_gcp) | ./modules/gcp | n/a |
2329
| <a name="module_linode"></a> [linode](#module\_linode) | ./modules/linode | n/a |
@@ -29,7 +35,7 @@ No resources.
2935
## Inputs
3036

3137
| Name | Description | Type | Default | Required |
32-
|------|-------------|------|---------|:--------:|
38+
| ---- | ----------- | ---- | ------- | :------: |
3339
| <a name="input_name"></a> [name](#input\_name) | Deployment name. | `string` | n/a | yes |
3440
| <a name="input_cloud_provider"></a> [cloud\_provider](#input\_cloud\_provider) | Cloud provider to deploy to. Supported values are gcp, digitalocean, and linode. | `string` | `"gcp"` | no |
3541
| <a name="input_digitalocean"></a> [digitalocean](#input\_digitalocean) | DigitalOcean infrastructure settings. | <pre>object({<br/> region = optional(string, "tor1")<br/> tags = optional(list(string), ["cloud-compose"])<br/><br/> droplet = optional(object({<br/> size = optional(string, "s-2vcpu-4gb")<br/> image = optional(string, "ubuntu-24-04-x64")<br/> ssh_keys = optional(list(string), [])<br/> vpc_uuid = optional(string, null)<br/> monitoring = optional(bool, true)<br/> ipv6 = optional(bool, true)<br/> backups = optional(bool, false)<br/> }), {})<br/><br/> ssh = optional(object({<br/> cloud_compose_keys = optional(list(string), [])<br/> users = optional(map(list(string)), {})<br/> }), {})<br/><br/> volumes = optional(object({<br/> data_size_gb = optional(number, 50)<br/> docker_volumes_size_gb = optional(number, 100)<br/> }), {})<br/><br/> firewall = optional(object({<br/> enabled = optional(bool, true)<br/> ssh_source_addresses = optional(list(string), ["0.0.0.0/0", "::/0"])<br/> web_source_addresses = optional(list(string), ["0.0.0.0/0", "::/0"])<br/> }), {})<br/> })</pre> | `{}` | no |
@@ -41,7 +47,7 @@ No resources.
4147
## Outputs
4248

4349
| Name | Description |
44-
|------|-------------|
50+
| ---- | ----------- |
4551
| <a name="output_appGsa"></a> [appGsa](#output\_appGsa) | The Google Service Account the app can use for app-scoped auth. |
4652
| <a name="output_backend"></a> [backend](#output\_backend) | Backend service ID for attaching Cloud Run ingress to an external HTTPS load balancer. |
4753
| <a name="output_cloud_provider"></a> [cloud\_provider](#output\_cloud\_provider) | Selected cloud provider. |

ansible.cfg

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[defaults]
2+
inventory = ansible/inventory.example.yml
3+
interpreter_python = auto_silent
4+
retry_files_enabled = False
5+
roles_path = ansible/roles

ansible/README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# cloud-compose Ansible Adapter
2+
3+
This role deploys the cloud-compose runtime onto an existing Debian or Ubuntu
4+
host. Use it when Terraform should not create the VM, disks, firewall, or DNS.
5+
6+
The role consumes the same `templates/apps.json` template defaults used by the
7+
Terraform provider modules.
8+
9+
The packaged runtime currently uses fixed host paths: `/home/cloud-compose`,
10+
`/mnt/disks/data`, and `/mnt/disks/volumes`.
11+
12+
The normal on-prem shape is one app per machine. Put each machine in the
13+
`cloud_compose` inventory group and set that host's template/runtime variables.
14+
For multiple apps on one host, pass the same
15+
`cloud_compose_runtime.compose.projects` shape used by Terraform.
16+
17+
Example inventory:
18+
19+
```yaml
20+
all:
21+
children:
22+
cloud_compose:
23+
hosts:
24+
isle-prod.example.edu:
25+
ansible_user: debian
26+
cloud_compose_name: isle-prod
27+
cloud_compose_template: isle
28+
cloud_compose_runtime:
29+
compose:
30+
ingress:
31+
domain: isle.example.edu
32+
acme_email: admin@example.edu
33+
sitectl:
34+
environment: production
35+
wp-prod.example.edu:
36+
ansible_user: debian
37+
cloud_compose_name: wp-prod
38+
cloud_compose_template: wp
39+
cloud_compose_runtime:
40+
compose:
41+
ingress:
42+
domain: wp.example.edu
43+
acme_email: admin@example.edu
44+
```
45+
46+
Run:
47+
48+
```bash
49+
ansible-playbook ansible/playbooks/site.yml
50+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
cloud_compose_provider: onprem

ansible/inventory.example.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
all:
2+
children:
3+
cloud_compose:
4+
hosts:
5+
isle-prod.example.edu:
6+
ansible_user: debian
7+
cloud_compose_name: isle-prod
8+
cloud_compose_template: isle
9+
cloud_compose_runtime:
10+
compose:
11+
ingress:
12+
domain: isle.example.edu
13+
acme_email: admin@example.edu
14+
sitectl:
15+
environment: production
16+
wp-prod.example.edu:
17+
ansible_user: debian
18+
cloud_compose_name: wp-prod
19+
cloud_compose_template: wp
20+
cloud_compose_runtime:
21+
compose:
22+
ingress:
23+
domain: wp.example.edu
24+
acme_email: admin@example.edu
25+
sitectl:
26+
environment: production
27+
drupal-prod.example.edu:
28+
ansible_user: debian
29+
cloud_compose_name: drupal-prod
30+
cloud_compose_template: drupal
31+
cloud_compose_runtime:
32+
compose:
33+
ingress:
34+
domain: drupal.example.edu
35+
acme_email: admin@example.edu
36+
sitectl:
37+
environment: production

ansible/playbooks/site.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
- name: Deploy cloud-compose runtime
2+
hosts: cloud_compose
3+
become: true
4+
roles:
5+
- role: cloud_compose
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
cloud_compose_name: cloud-compose
2+
cloud_compose_provider: onprem
3+
cloud_compose_template: ""
4+
5+
cloud_compose_templates_file: "{{ role_path }}/../../../templates/apps.json"
6+
cloud_compose_rootfs_source: "{{ role_path }}/../../../rootfs"
7+
8+
# The packaged rootfs and systemd units currently hard-code these values.
9+
cloud_compose_user: cloud-compose
10+
cloud_compose_group: cloud-compose
11+
cloud_compose_home: /home/cloud-compose
12+
cloud_compose_data_dir: /mnt/disks/data
13+
cloud_compose_volumes_dir: /mnt/disks/volumes
14+
15+
cloud_compose_docker_compose_version: v5.2.0
16+
cloud_compose_docker_buildx_version: v0.35.0
17+
cloud_compose_sitectl_version: latest
18+
19+
cloud_compose_managed_runtime_enabled: true
20+
cloud_compose_internal_services_enabled: false
21+
cloud_compose_internal_services_auto_update: false
22+
cloud_compose_managed_artifacts: []
23+
24+
cloud_compose_install_packages: true
25+
cloud_compose_reload_systemd: true
26+
cloud_compose_run_bootstrap: true
27+
cloud_compose_force_bootstrap: false
28+
cloud_compose_bootstrap_timeout: 3600
29+
cloud_compose_bootstrap_poll_interval: 15
30+
31+
cloud_compose_extra_env: {}
32+
cloud_compose_runtime: {}
33+
34+
cloud_compose_default_ingress:
35+
letsencrypt: false
36+
bot_mitigation: false
37+
mode: ""
38+
domain: ""
39+
acme_email: ""
40+
trusted_ips: []
41+
max_upload_size: ""
42+
upload_timeout: ""
43+
44+
cloud_compose_default_init:
45+
- 'sitectl config set-context "${SITECTL_CONTEXT_NAME}" --type local --project-dir "${DOCKER_COMPOSE_DIR}" --site "${CLOUD_COMPOSE_INSTANCE_NAME}" --plugin "${SITECTL_PLUGIN}" --environment "${SITECTL_ENVIRONMENT}" --project-name "${CLOUD_COMPOSE_INSTANCE_NAME}" --compose-project-name "${COMPOSE_PROJECT_NAME}" --docker-socket /var/run/docker.sock --env-file .env --default'
46+
cloud_compose_default_up:
47+
- 'sitectl compose --context "${SITECTL_CONTEXT_NAME}" up -d --remove-orphans'
48+
- 'sitectl healthcheck --context "${SITECTL_CONTEXT_NAME}" --persist --timeout "${SITECTL_HEALTHCHECK_TIMEOUT}" --interval "${SITECTL_HEALTHCHECK_INTERVAL}"'
49+
- 'if [ "${SITECTL_ENVIRONMENT}" != "production" ]; then sitectl verify --context "${SITECTL_CONTEXT_NAME}" ${SITECTL_VERIFY_ARGS:-}; fi'
50+
cloud_compose_default_down:
51+
- 'sitectl compose --context "${SITECTL_CONTEXT_NAME}" down'
52+
cloud_compose_default_rollout:
53+
- 'TARGET_REF="${GIT_REF:-${GIT_BRANCH:-${DOCKER_COMPOSE_BRANCH:-main}}}"'
54+
- 'if [ -x ./scripts/rollout.sh ]; then ./scripts/rollout.sh; else sitectl deploy --context "${SITECTL_CONTEXT_NAME}" --branch "$TARGET_REF"; fi'
55+
- 'sitectl healthcheck --context "${SITECTL_CONTEXT_NAME}" --persist --timeout "${SITECTL_HEALTHCHECK_TIMEOUT}" --interval "${SITECTL_HEALTHCHECK_INTERVAL}"'
56+
- 'if [ "${SITECTL_ENVIRONMENT}" != "production" ]; then sitectl verify --context "${SITECTL_CONTEXT_NAME}" ${SITECTL_VERIFY_ARGS:-}; fi'

0 commit comments

Comments
 (0)