Repository navigation
Operations
This document provides explanation of the Touchpoints agile development process and the production deployment process.
Touchpoints is currently deployed in at least 3 environments.
- Production - the live site
- Staging - an integration environment for the development team and product owner to validate functionality before pushing to production
- Demo - a lightweight copy of Production for Touchpoint's gov customers to test in. Demo and Production always share the same code but feature flags on Demo may be set to expose in-progress features.
These environments are managed via cloud.gov, and are currently configured to run on Cloud Foundry, but can run elsewhere with relatively little modification.
Each Developer is expected to be able to develop Touchpoints on their own cloud.gov instance as well.
Software Product teams develop conventions related to the development process. The steps below represent the flow of code from a developer's machine through multiple gates, to production.
Touchpoints follows a version of GitHub Flow.
Key points:
- There is a single long-lived branch named
main. - Developers work in feature branches, created off of
main. -
mainis to be treated as always deployable. Developers should not merge anything tomainthat they would be uncomfortable being deployed immediately to production. - Notwithstanding the previous point, we are not doing continuous deployment. Code that is merged to
mainwill generally be verified in staging before it is pushed to production. - The Staging environment always reflects the HEAD of
main. - The Production environment always reflects the latest release tag applied to
main.
- LOCAL DEVELOPMENT
- Feature branches are submitted as Pull Requests and reviewed in GitHub.
- All commits are pushed here, to this Touchpoints code repository.
- Upon creation of PR, CircleCI runs automated tests.
- Upon creation of PR, CodeQL runs automated code scans.
- When a feature branch is code reviewed, it may be merged to
main. - Green (passing)
mainbuilds get deployed to Staging automatically via CircleCI. - ON STAGING...
- The Product Owner reviews the changes on Staging.
- If fixes are required, the process starts over from LOCAL DEVELOPMENT.
- Once the Product Owner is happy,
mainis tagged with a release tag. - RELEASE TO PRODUCTION...
- Pushing a release tag triggers a deployment to Production automatically via CircleCI.
- Pushing a release tag also triggers a deployment to Demo automatically via CircleCI.
Deploying Touchpoints requires pushing a directory of code (the Touchpoints repo or CI build output) to an application instance in cloud.gov's Cloud Foundry using the cf push command.
This can be done 1) manually via command line or 2) in an automated way, using a continuous integration tool like CircleCI.
cf push references a local manifest.yml that declares one or more applications.
Thus, having a "properly configured" manifest.yml file is a prerequisite dependency; a requirement.
To deploy a specific instance, use cf push.
For example: cf push touchpoints-staging.
Touchpoints loads its runtime secrets from a Cloud Foundry user-provided service named touchpoints-secrets, one per environment. At boot, the application reads VCAP_SERVICES, finds this service, validates that every required secret is present, and copies the values into environment variables.
This replaces the previous approach of setting secrets individually with cf set-env.
- All application secrets live in one place per environment.
- Secrets are bound to the app rather than injected ad hoc, so they are not lost on
cf push. - The application fails fast at startup if a required secret is missing, instead of failing later at runtime.
- The same mechanism works for the web app, the Sidekiq worker, and the migration task.
- Each app's manifest binds the environment-specific secrets service under the binding name
touchpoints-secrets. - On boot,
config/vcap_services.rbruns early inconfig/application.rb, before the Rails environment configuration reads any requiredENVvalues. - It locates the bound service named
touchpoints-secretsinVCAP_SERVICES, reads itscredentials, validates all required keys, and sets the corresponding environment variables. - If the service, its credentials, or any required value is missing, startup raises an error and the app does not come up.
The service credentials must be a JSON object with this nested shape:
{
"turnstile": {
"secret_key": "...",
"site_key": "..."
},
"api": {
"username": "...",
"password": "..."
},
"login_gov": {
"private_key": "..."
},
"ses": {
"region": "...",
"access_key_id": "...",
"secret_access_key": "..."
},
"new_relic": {
"key": "..."
},
"rails_active_record": {
"primary_key": "...",
"deterministic_key": "...",
"key_derivation_salt": "..."
},
"email_sender": "...",
"gtm_container_id": "..."
}| Credential path | Environment variable |
|---|---|
turnstile.secret_key |
TURNSTILE_SECRET_KEY |
turnstile.site_key |
TURNSTILE_SITE_KEY |
api.username |
API_HTTP_USERNAME |
api.password |
API_HTTP_PASSWORD |
login_gov.private_key |
LOGIN_GOV_PRIVATE_KEY |
ses.region |
AWS_SES_REGION |
ses.access_key_id |
AWS_SES_ACCESS_KEY_ID |
ses.secret_access_key |
AWS_SES_SECRET_ACCESS_KEY |
email_sender |
TOUCHPOINTS_EMAIL_SENDER |
new_relic.key |
NEW_RELIC_KEY |
rails_active_record.primary_key |
RAILS_ACTIVE_RECORD_PRIMARY_KEY |
rails_active_record.deterministic_key |
RAILS_ACTIVE_RECORD_DETERMINISTIC_KEY |
rails_active_record.key_derivation_salt |
RAILS_ACTIVE_RECORD_KEY_DERIVATION_SALT |
gtm_container_id |
TOUCHPOINTS_GTM_CONTAINER_ID |
All of the keys above are required. If any is missing, the app logs which ones were absent and refuses to start.
S3 and Redis credentials are not part of this service. They are supplied by their own bound services and mapped separately.
Each environment has its own service instance, bound under the common binding name touchpoints-secrets:
| Environment | Service instance name |
|---|---|
| Production | touchpoints-prod-secrets |
| Staging | touchpoints-staging-secrets |
| Demo | touchpoints-demo-secrets |
The binding name the application looks up is always touchpoints-secrets, regardless of the underlying instance name.
Create the user-provided service once per environment. Store the credentials JSON in a local file (do not commit it), then pass it with -p:
# secrets.json contains the credential structure shown above
cf create-user-provided-service touchpoints-prod-secrets -p ./secrets.jsonTargeting:
cf target -o <org> -s <space>Use the matching instance name for the space you are targeting (touchpoints-staging-secrets, touchpoints-demo-secrets, etc.).
To rotate or change a value, update the whole credentials object:
cf update-user-provided-service touchpoints-prod-secrets -p ./secrets.jsoncf update-user-provided-service replaces the entire credentials payload, so include all required keys in the file, not just the one you are changing.
After updating, restart the bound apps so they pick up the new values:
cf restart touchpoints
cf restart touchpoints-production-sidekiq-workerThe next deployment will also pick up the new values when the apps restage.
The deployment manifests already bind the service. For reference, the manifest entry uses name (the instance) and binding_name (what the app looks up):
services:
- name: touchpoints-prod-secrets
binding_name: touchpoints-secretsIf you add a new app that boots Rails, it must bind the environment's secrets service under the binding name touchpoints-secrets, or it will fail to start.
To view the bound credentials for an app (values are sensitive, so do this carefully):
cf env touchpointsTo view the service instance directly:
cf service touchpoints-prod-secretsLocal development and test do not use VCAP_SERVICES. When VCAP_SERVICES is absent, the loader is skipped and the app falls back to loading .env via Dotenv. Set the same variables in your local .env as needed.
-
required startup secrets service is missingThe app is not bound totouchpoints-secrets. Bind the environment's service and restage. -
required startup secrets credentials are missingThe service exists but has an empty or malformedcredentialsobject. Re-create or update it with a valid payload. -
required startup secrets are missing: <names>One or more required keys are absent from the credentials. The message lists the missing environment variable names. Add them and update the service. -
App boots locally but fails in Cloud Foundry Confirm the service is bound in the target space and that the credentials JSON matches the required structure exactly, including the nested objects.
Touchpoints uses Cloud.gov Service Accounts to deploy the application from the Continuous Integration server.
-
cf create-service cloud-gov-service-account space-deployer touchpoints-staging-deployer- creates the Service Account service -
cf create-service-key touchpoints-staging-deployer staging-deploy-key- creates the Service Account's Service Key -
cf service-key touchpoints-staging-deployer staging-deploy-key- diplays the username & password of the Service Account's Service Key
The Staging Service Account's username and password are used on CI to deploy to Staging and Demo.
cf create-service cloud-gov-service-account space-deployer touchpoints-deployercf create-service-key touchpoints-deployer staging-deploy-keycf service-key touchpoints-deployer staging-deploy-key
Touchpoints uses s3 buckets, which are provided as Cloud.gov service.
Touchpoints stores Organization logo image files, and .pdfs of Service Maps for a given Touchpoint.
Here's how to configure cloud.gov S3 for Touchpoints:
- Create a public S3 service in cloud.gov
- Bind that s3 instance to your Touchpoints application application
- Then look at the VCAP settings of that cloud.gov application instance, and set those configs in Touchpoints /config/initializers/carrierwave.rb
aws_access_key_idaws_secret_access_keyregionhost-
fog_directory(the name of the S3 bucket)
Touchpoints has a Maintenance Page for display when the app is under extended maintenance or otherwise experiencing downtime.
The maintenance page is a separate application; with 1 html file. 1 maintenance page is maintained for each Cloud.gov space. In cloud.gov, the web application simply updates a virtual route to point the maintenance page app. When complete, the virtual route is pointed back at the web application.
Because Touchpoints maintains 3 "Spaces" (dev, staging, prod), the 3 maintenance pages exist as:
- touchpoints-dev-maintenance-page
- touchpoints-staging-maintenance-page
- touchpoints-prod-maintenance-page
Because Worker instances do not have publicly accessible HTTP endpoints, health checks should be disabled for the app. Otherwise, cloud.gov will kill the application instance when it fails a health check.
- Staging -
cf set-health-check touchpoints-staging-sidekiq-worker none - Demo -
cf set-health-check touchpoints-demo-sidekiq-worker none - Production -
cf set-health-check touchpoints-production-sidekiq-worker none
See the README at https://github.com/gsa/touchpoints-maintenance-page for information about the specific commands to create and use the maintenance page.
To scale Touchpoints instances on cloud.gov:
- To view how many instances are set to run -
cf scale instance-name- example:
cf scale touchpoints
- example:
- To set how many instances should run -
cf scale instance-name -i number-of-instances- example:
cf scale touchpoints-staging -i 2
- example:
- Form Elements
- Form Templates
- Form Markup
- Delivery Options
- Creating a Form Template
- Deploying via GTM
- Analytics Events
- Organization Form Approval
- Tagging Responses
- Response Fields
- Data Collections
- CX Data Collection Format
- CX Performance Data
- Data Collection Rating
- A-11 PRA Guidance
- CSCRM Data Collection
- IDC Services Data Collection