Skip to content

Managevisor: New app documentation (needs review)

Adding a new application to manage.attracs.com / manage-nightly.attracs.com

This guide walks through onboarding a new application to the Attracs management environments (stable and nightly). For adding organizations instead, see the separate organizations doc.


Prerequisites β€” ask the requesting developer

Collect the following before starting: * Repository β€” Does the application already exist? If so, what's the repo URL? * Application name β€” what should the app be called? * Managed or unmanaged? * Sendgrid? β€” Does it send mail via Sendgrid? * Google services β€” Pub/Sub? Storage buckets? Anything else? * Database? * MySQL or Postgres? * Dedicated instance or shared instance? * Dedicated instances are very rare. By default, add a new database to the shared instance. * Health-check path β€” /, /api/ping, /api/health, or something else?


1. Create the GitHub repository

GitHub repositories live under the Attracs organization. Enable branch protection for every new repo.

1a. Create a personal access token (classic)

GitHub β†’ your profile β†’ Settings β†’ Developer settings β†’ Personal access tokens β†’ Tokens (classic) β†’ Generate new token (classic).

Required scopes: * repo (full scope)

If you hit 403 GithubException: You need admin access to the organization…, regenerate the token with the extended scopes: * repo, repo:status, repo_deployment, public_repo, repo:invite, security_events

Copy the token (ghp_…) somewhere safe.

1b. Update the encrypted token file

The Ansible vault password lives in the root of attracs-linux-playbooks as .vault_password_file. If you don't have it, copy "Ansible Vault Encryption keys - linux password file" from 1Password.

Decrypt the token file, replace the value after ansible_token: with your token, then re-encrypt with the same password:

ansible-vault decrypt github.yml --vault-password-file=../../../.vault_password_file
# …edit the file, replace the ansible_token value…
ansible-vault encrypt github.yml --vault-password-file=../../../.vault_password_file

1c. Run the create-repo script

From the root of attracs-linux-playbooks:

./gcp/scripts/github/create_repo.sh <repo-name>

2. Reserve a port in the services_ports Google sheet

Each app needs a unique port for stable and nightly. * Choose the next port number in ascending order. * Do not reuse a previously assigned port, even if it looks unused. * Ports sheet https://docs.google.com/spreadsheets/d/10trjuvrp_N8-OrqwsHchAysTIcrJRNqZ/edit?usp=sharing&ouid=105978654382102392446&rtpof=true&sd=true


3. Create DNS entries

DNS is managed in Cloudflare. Use the helper script:

./gcp/scripts/dns/create_cname_record.sh <recordname> <recordvalue>
  • <recordname> β€” the hostname (e.g. my-app). Do not include .attracs.com or any other suffix β€” it will break the DNS entry.
  • <recordvalue> β€” the load balancer name from the table below.

Load balancer reference

Project Load balancer IP Use
attracs-devops devops-lb 34.98.96.99 DevOps
attracs-services-195009 gcplb 34.120.86.122 TMS (stable)
attracs-services-195009 lb2 34.120.155.57 CORE + others (stable)
att-nightly lb1n 34.117.203.247 TMS (nightly)
att-nightly lb2n 35.244.240.9 CORE + others (nightly)
attracs-logistics road-cc-nightly-lb 34.144.248.151 Road (nightly)
attracs-logistics road-cc-stable-lb 34.49.67.73 Road (stable)
hetzner frontend 95.216.1.224 Road (legacy)

Example β€” create https://my-app.attracs.com pointing at Road's GCP LB:

./gcp/scripts/dns/create_cname_record.sh my-app lb3

Verify

dig my-app.attracs.com

If you mis-create a record

Log in to DNS Made Easy with the meshmoon credentials and fix the entry manually.


4. Create GCP infrastructure with Terraform (optional)

Skip this section if the app needs no GCP services (Pub/Sub, Storage, etc.). 1. In GCP, manually create the project folder under attracs.net β†’ projects/. 2. In the adi-infra repository, create a new branch (e.g. pr-my-app). 3. Create a new directory under adi-infra/terraform/gcp/projects/ and copy the templates from adi-infra/terraform/gcp/templates/ into it. 4. Remove unused template files. The minimum required set is: * api.tf * iam.tf * main.tf * variable.tf 5. Make the necessary changes and validate with terraform plan. 6. Open a PR and get it approved. 7. Run terraform apply.


5. Create the database

Safety first. Test commands against a non-critical database (e.g. a clone of nightly) before touching stable. Use transactions β€” BEGIN; then ROLLBACK; or COMMIT; β€” when running anything destructive. Avoid cloning production databases when possible, as they may contain sensitive data.

Shared instance β€” Postgres

Set up cloud-sql-proxy first (see the cloud-sql-proxy doc). Nightly β€” proxy on port 55435:

psql -h 127.0.0.1 -p 55435 -u postgres -d postgres

Stable β€” same setup, but the proxy port is 55432.

Check your permissions with \du. You need: * Create database * Create role attributes * cloudsqlsuperuser role If you're missing any of these, follow the "grant Postgres superuser" doc before continuing. Create the database and user:

./gcp/scripts/database/create_db_and_user.sh \
  ./config/postgre/<environment-variable-file.yml> \
  <application_database_name>

For an IAM-based user, pass the service-account email as the third argument:

./gcp/scripts/database/create_db_and_user.sh \
  ./config/postgre/<environment-variable-file.yml> \
  <application_database_name> \
  <service-account-name@project-id.iam>

Save the password the playbook prints at the end β€” you'll need it in the next step for Vault.


6. Add Vault secrets

Vault SSO login is currently broken. Use the root token until the ticket below is resolved. 1. Open the kv-v2-nightly secret engine. 2. Click Create secret +. 3. Add the following keys, depending on what the app uses:

Key Value When
database_host Public IP of the nightly DB instance If app uses a DB
database_password Password printed by the create-db playbook If app uses a DB
sendgrid_key Provided by the developer (share via 1Password) If app uses Sendgrid

To fix a typo in a key, use Create new version + rather than starting over.


7. Update attracs-application-configs

Edit the relevant template, e.g.:

attracs-application-configs/application-configs/<app-name>/attracs-nightly.yml.ctmpl

Then push the change from the root of attracs-linux-playbooks:

ansible-playbook vault_agent/vault_agent_config.yml \
  --vault-password-file=.vault_password_file

8. Create the supervisor run config

Supervisor run configs tell Managevisor which applications to run on which backend. * Location: attracs-linux-playbooks/supervisord/run/ * Filename: <application-name>-<deployment-environment>.conf.j2

Example (nightly)

[program:tms-feedback-nightly.attracs.com]
command=/bin/bash /etc/attracs-supervisor/run-process.sh -p tms-feedback-nightly.tar.gz -w server -x server -a -production -nightly -port 5056 -log-level debug -debug-database -reverse-proxy-host https://tms-feedback-nightly.attracs.com -config /etc/tms-feedback/attracs-nightly.yml
directory=/home/syncthing/attracs-builds/tms-feedback/nightly
stopwaitsecs=60
startsecs=5
autorestart=true

What to change

Field Change to
[program:…] example-app-nightly.attracs.com
-p example-app-nightly.tar.gz
-w Path to where main.go lives in the repo, e.g. -w server or -w services/example_app/server
-port The port you reserved in services_ports.xlsx
-reverse-proxy-host
-config /etc/example-app/attracs-nightly.yml
directory= /home/syncthing/attracs-builds/example-app/nightly

Resulting example

[program:example-app-nightly.attracs.com]
command=/bin/bash /etc/attracs-supervisor/run-process.sh -p example-app-nightly.tar.gz -w server -x server -a -production -nightly -port <CHANGEME> -log-level debug -debug-database -reverse-proxy-host https://example-app-nightly.attracs.com -config /etc/example-app/attracs-nightly.yml
directory=/home/syncthing/attracs-builds/example-app/nightly
stopwaitsecs=60
startsecs=5
autorestart=true

9. Add the run config to the backend host(s)

Each backend has a JSON file in attracs-linux-playbooks/supervisord/config/hosts/:

{
  "includes": [
    "/etc/attracs-supervisor/syncthing.conf",
    "/etc/attracs-supervisor/attracs-managevisor-agent.conf",
    "/etc/attracs-supervisor/attracs-managevisor.conf",
    "/etc/attracs-supervisor/attracs-ci.conf",
    "/etc/attracs-supervisor/memcached.conf"
  ]
}

Add a new entry for your app's .conf to the includes array. Watch the trailing commas β€” JSON is strict. Push the change to the servers:

ansible-playbook supervisord/supervisord-update-all-configs.yml \
  -l road-confidential-instance-nightly-1,road-confidential-instance-nightly-2 \
  --vault-password-file=.vault_password_file

Replace the -l host list with the backends you're targeting.


10. Reload Managevisor

After the playbook completes successfully: 1. Open Managevisor. 2. Server actions β†’ Reload Supervisor config.


Incomplete β€” needs more info

These sections were either missing, half-written, or unresolved in the original doc. Fill them in as you learn the details.

Cloudflare DNS automation

No automation exists yet for Cloudflare. The current flow uses create_cname_record.sh, which predates the Cloudflare migration. Document the actual Cloudflare workflow (UI? Terraform? API?) once decided.

Informing Ahola IT about new DNS

The original doc said: *"Inform Ahola IT about the new DNS β€” Where? How? Who?"* Document the contact (person/team/channel/ticket queue) and the format of the notification.

Road-nightly lb3 workaround ticket

"For road nightly, all DNS records go to lb2n until lb3 is fixed for nightly applications." The ticket reference was left blank β€” paste the Jira/Linear ticket link here.

Vault login ticket

Vault SSO login is currently broken; the doc references a ticket but the link was missing. Paste the ticket link here so people can track when normal login is restored.

Decrypting the personal-token file (path)

The fallback note about *"Decrypt the token file located in… TODO"* never specified the path. Confirm the canonical path inside attracs-linux-playbooks and document it.

Shared-instance MySQL setup

Section was empty. Document the equivalent of the Postgres flow: proxy ports (stable/nightly), how to connect with mysql CLI, required privileges, and the create_db_and_user.sh invocation (if there's a MySQL variant).

Dedicated database instances

Open question from the original doc: *"do we use these?"* If yes, document the provisioning steps. If no, delete this section.

Monitoring β€” Zabbix web scenario

Half-finished bullets in the original: Add step (name, path, status codes) Add graph Document the actual Zabbix workflow: which host group, which template, where the web scenario lives, screenshots if helpful.

Monitoring β€” Grafana response-code dashboard

Original noted: *"Add it to the Grafana Response Code dashboard, source Zabbix, override item name to look pretty."* Document the dashboard URL, the exact panel/source, and the naming convention for the override.

GCP load balancer backend additions

The original ended with a list of unfinished steps: Add the named port to the instance group Create a health check for the backend service Create the backend service Create host and path rules Each of these needs the actual GCP console / gcloud commands documented. This is probably the biggest remaining gap β€” the app isn't reachable end-to-end without it.