Skip to content

Merge pull request #1383 from bcgov/dev #3149

Merge pull request #1383 from bcgov/dev

Merge pull request #1383 from bcgov/dev #3149

name: Build and Deploy
on:
push:
branches: [feature/*, dev, test]
env:
REGISTRY: ghcr.io
REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }}
REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Docker meta
id: docker_meta
uses: docker/metadata-action@v3
with:
images: ${{ env.REGISTRY }}/bcgov/api-services-portal/api-services-portal
- name: Set DEPLOY_ID which will deploy a custom deploy to 'dev' environment
run: |
echo '::set-output name=DEPLOY_ID::${{ steps.docker_meta.outputs.version }}'
echo '::set-output name=APP_VERSION::${{ fromJSON(steps.docker_meta.outputs.json).labels['org.opencontainers.image.version'] }}'
echo '::set-output name=APP_REVISION::${{ fromJSON(steps.docker_meta.outputs.json).labels['org.opencontainers.image.revision'] }}'
id: set-deploy-id
- name: Get deploy ID
run: echo "The DEPLOY_ID is ${{ steps.set-deploy-id.outputs.DEPLOY_ID }}"
- uses: actions/checkout@v2
- name: Install oc
uses: redhat-actions/oc-installer@v1
with:
version: '4.6'
- name: Authenticate to silver and set context
uses: redhat-actions/oc-login@v1
with:
openshift_server_url: ${{ secrets.OPENSHIFT_SERVER }}
openshift_token: ${{ secrets.OPENSHIFT_TOKEN }}
# Disables SSL cert checking. Use this if you don't have the certificate authority data.
insecure_skip_tls_verify: true
namespace: ${{ env.OPENSHIFT_NAMESPACE }}
- name: Login to DockerHub
uses: docker/login-action@v1
with:
registry: ${{ env.REGISTRY }}
username: ${{ env.REGISTRY_USERNAME }}
password: ${{ env.REGISTRY_PASSWORD }}
- uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
- name: Build
uses: docker/build-push-action@v2
with:
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
context: .
file: Dockerfile
tags: ${{ steps.docker_meta.outputs.tags }}
load: true
build-args: |
GITHUB_API_TOKEN=${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
APP_VERSION=${{ steps.set-deploy-id.outputs.APP_VERSION }}
APP_REVISION=${{ steps.set-deploy-id.outputs.APP_REVISION }}
- name: Push
run: docker push ${{ steps.docker_meta.outputs.tags }}
- name: 'Get Helm'
if: github.ref != 'refs/heads/dev'
run: |
curl -L -O https://get.helm.sh/helm-v3.4.2-linux-amd64.tar.gz
tar -xf helm-v3.4.2-linux-amd64.tar.gz
- name: 'Deploy Database'
if: github.ref != 'refs/heads/dev'
run: |
export PATH=$PATH:`pwd`/linux-amd64
DEPLOY_ID="${{ steps.set-deploy-id.outputs.DEPLOY_ID }}"
DB_NAME="proto-asp-${DEPLOY_ID}-db"
# ConfigMap to create Keystone user and database on first Postgres start
oc create configmap "${DB_NAME}-init" --from-literal=1-init.sql="CREATE ROLE keystonejsuser WITH LOGIN PASSWORD 'keystonejsuser'; CREATE DATABASE keystonejs OWNER keystonejsuser;" --dry-run=client -o yaml | oc apply -f -
# ConfigMap with Keystone schema (run by Job after Postgres is up)
oc create configmap "${DB_NAME}-keystone-schema" --from-file=keystone-init.sql=local/db/keystone-init.sql --dry-run=client -o yaml | oc apply -f -
# PVC for Postgres data (persistence)
cat <<EOF | oc apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ${DB_NAME}-data
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: ${{ startsWith(github.ref_name, 'feature/') && '1Gi' || '2Gi' }}
EOF
# Postgres 15 Deployment (public image)
cat <<EOF | oc apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${DB_NAME}
spec:
replicas: 1
selector:
matchLabels:
app: ${DB_NAME}
strategy:
type: Recreate
template:
metadata:
labels:
app: ${DB_NAME}
spec:
containers:
- name: postgres
image: postgres:15
ports:
- containerPort: 5432
env:
- name: POSTGRES_USER
value: postgres
- name: POSTGRES_PASSWORD
value: "s3cr3t"
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
- name: init
mountPath: /docker-entrypoint-initdb.d
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
memory: 256Mi
volumes:
- name: data
persistentVolumeClaim:
claimName: ${DB_NAME}-data
- name: init
configMap:
name: ${DB_NAME}-init
readinessProbe:
exec:
command: [pg_isready, -U, postgres]
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 5
livenessProbe:
exec:
command: [pg_isready, -U, postgres]
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
EOF
# Service for Postgres
cat <<EOF | oc apply -f -
apiVersion: v1
kind: Service
metadata:
name: ${DB_NAME}
spec:
ports:
- port: 5432
targetPort: 5432
name: postgres
selector:
app: ${DB_NAME}
EOF
# Wait for Postgres to be ready
oc rollout status deployment/${DB_NAME} --timeout=300s
# Run Keystone schema (Job)
cat <<EOF | oc apply -f -
apiVersion: batch/v1
kind: Job
metadata:
name: ${DB_NAME}-keystone-init
spec:
ttlSecondsAfterFinished: 300
backoffLimit: 5
template:
spec:
restartPolicy: OnFailure
containers:
- name: run-schema
image: postgres:15
command:
- /bin/sh
- -c
- |
until PGPASSWORD=keystonejsuser psql -h ${DB_NAME} -U keystonejsuser -d keystonejs -c '\q' 2>/dev/null; do echo "Waiting for DB..."; sleep 2; done
PGPASSWORD=keystonejsuser psql -h ${DB_NAME} -U keystonejsuser -d keystonejs -f /schema/keystone-init.sql
env:
- name: PGPASSWORD
value: "keystonejsuser"
volumeMounts:
- name: schema
mountPath: /schema
volumes:
- name: schema
configMap:
name: ${DB_NAME}-keystone-schema
EOF
oc wait --for=condition=complete job/${DB_NAME}-keystone-init --timeout=300s
- name: 'Deploy Backend'
if: github.ref != 'refs/heads/dev'
run: |
export PATH=$PATH:`pwd`/linux-amd64
echo "
podAnnotations:
sha: $GITHUB_SHA
replicaCount: 1
rollingUpdate:
maxUnavailable: 100%
maxSurge: 0%
image:
repository: ${{ env.REGISTRY }}/bcgov/api-services-portal/api-services-portal
tag: ${{ steps.set-deploy-id.outputs.DEPLOY_ID }}
pullPolicy: Always
imagePullSecrets:
- name: dev-github-read-packages-creds
podSecurityContext:
fsGroup: ${{ secrets.RUNNING_UID_GID }}
securityContext:
runAsUser: ${{ secrets.RUNNING_UID_GID }}
containerPort: 3000
resources:
requests:
cpu: 20m
memory: 400M
limits:
cpu: 100m
memory: 800M
serviceAccount:
create: false
name: asp-service-account
oauthProxy:
enabled: true
config:
- filename: oauth2-proxy.cfg
mountPath: /oauth2-proxy.cfg
contents: |-
cookie_expire='24h'
cookie_refresh='3m'
cookie_secure='true'
cookie_samesite='strict'
cookie_secret='not_secretenough'
email_domains='*'
redirect_url='https://api-services-portal-${{ steps.set-deploy-id.outputs.DEPLOY_ID }}.apps.silver.devops.gov.bc.ca/oauth2/callback'
skip_auth_regex='/login|/health|/public|/docs|/redirect|/_next|/images|/devportal|/manager|/about|/maintenance|/admin/session|/ds/api|/feed|/metrics|/signout|/gw/api|/content|^[/]$'
skip_jwt_bearer_tokens='false'
skip_provider_button='true'
whitelist_domains='authz-apps-gov-bc-ca.dev.api.gov.bc.ca'
# redis_connection_url="redis://redis-headless:6379"
# session_store_type="redis"
# redis_password=""
# insecure-oidc-allow-unverified-email: 'true'
# insecure-oidc-skip-issuer-verification: 'true'
# oidc-email-claim: 'sub'
# pass-authorization-header: 'false'
# set-authorization-header: 'false'
- filename: oauth2-proxy.yaml
mountPath: /oauth2-proxy.yaml
yaml:
injectRequestHeaders:
- name: X-Forwarded-Groups
values:
- claim: groups
- name: X-Forwarded-User
values:
- claim: user
- name: X-Forwarded-Email
values:
- claim: email
- name: X-Forwarded-Preferred-Username
values:
- claim: preferred_username
- name: X-Forwarded-Access-Token
values:
- claim: access_token
- name: X-Forwarded-Id-Token
values:
- claim: IDToken
injectResponseHeaders: []
metricsServer:
BindAddress: ""
SecureBindAddress: ""
TLS: null
providers:
- clientID: ${{ secrets.OIDC_CLIENT_ID }}
clientSecret: ${{ secrets.OIDC_CLIENT_SECRET }}
loginURL: ${{ secrets.OIDC_ISSUER }}/protocol/openid-connect/auth
id: oidc=aps-portal
loginURLParameters:
- default:
- force
name: approval_prompt
- allow:
- pattern: ".*$"
name: kc_idp_hint
oidcConfig:
audienceClaims:
- aud
emailClaim: sub
groupsClaim: groups
insecureAllowUnverifiedEmail: true
insecureSkipNonce: true
issuerURL: ${{ secrets.OIDC_ISSUER }}
userIDClaim: sub
profileURL: ${{ secrets.OIDC_ISSUER }}/protocol/openid-connect/userinfo
provider: oidc
redeemURL: ${{ secrets.OIDC_ISSUER }}/protocol/openid-connect/token
scope: openid
validateURL: ${{ secrets.OIDC_ISSUER }}/protocol/openid-connect/userinfo
server:
BindAddress: 0.0.0.0:7999
SecureBindAddress: ""
TLS: null
upstreamConfig:
upstreams:
- flushInterval: 1s
id: /
passHostHeader: true
path: /
proxyWebSockets: true
timeout: 30s
uri: http://127.0.0.1:3000
env:
SESSION_SECRET:
value: '234873290483290'
secure: true
AUTH_STRATEGY:
value: Oauth2Proxy
KONG_URL:
value: '${{ secrets.KONG_URL_DEV}}'
ADAPTER:
value: knex
KNEX_HOST:
value: 'proto-asp-${{ steps.set-deploy-id.outputs.DEPLOY_ID }}-db'
KNEX_PORT:
value: '5432'
KNEX_USER:
value: keystonejsuser
secure: true
KNEX_PASSWORD:
value: keystonejsuser
secure: true
KNEX_DATABASE:
value: keystonejs
FEEDER_URL:
value: 'http://proto-asp-${{ steps.set-deploy-id.outputs.DEPLOY_ID }}-feeder-generic-api'
GITHUB_API_TOKEN:
value: '${{ secrets.GH_TOKEN_FOR_CONTENT}}'
secure: true
OIDC_ISSUER:
value: '${{ secrets.OIDC_ISSUER }}'
OIDC_CLIENT_ID:
value: '${{ secrets.OIDC_CLIENT_ID }}'
JWKS_URL:
value: '${{ secrets.OIDC_ISSUER }}/protocol/openid-connect/certs'
EXTERNAL_URL:
value: 'https://api-services-portal-${{ steps.set-deploy-id.outputs.DEPLOY_ID }}.apps.silver.devops.gov.bc.ca'
SSR_API_ROOT:
value: 'http://localhost:7999'
NEXT_PUBLIC_API_ROOT:
value: 'https://api-services-portal-${{ steps.set-deploy-id.outputs.DEPLOY_ID }}.apps.silver.devops.gov.bc.ca'
NEXT_PUBLIC_GRAFANA_URL:
value: 'https://grafana-apps-gov-bc-ca.dev.api.gov.bc.ca'
NEXT_PUBLIC_KUBE_CLUSTER:
value: 'feature-silver'
NEXT_PUBLIC_HELP_DESK_URL:
value: 'https://dpdd.atlassian.net/servicedesk/customer/portal/1/group/2'
NEXT_PUBLIC_HELP_CHAT_URL:
value: 'https://chat.developer.gov.bc.ca/channel/aps-ops'
NEXT_PUBLIC_HELP_ISSUE_URL:
value: 'https://github.com/bcgov/api-services-portal/issues'
NEXT_PUBLIC_HELP_API_DOCS_URL:
value: '/ds/api/v3/console/'
NEXT_PUBLIC_HELP_SUPPORT_URL:
value: 'https://developer.gov.bc.ca/docs/default/component/aps-infra-platform-docs/'
NEXT_PUBLIC_HELP_RELEASE_URL:
value: 'https://developer.gov.bc.ca/docs/default/component/aps-infra-platform-docs/reference/releases/'
NEXT_PUBLIC_HELP_STATUS_URL:
value: 'https://status.api.gov.bc.ca/'
NEXT_PUBLIC_DEVELOPER_IDS:
value: 'idir,bceid,bcsc,github'
NEXT_PUBLIC_PROVIDER_IDS:
value: 'idir'
NEXT_PUBLIC_ACCOUNT_BCEID_URL:
value: 'https://www.test.bceid.ca/logon.aspx?returnUrl=/profile_management'
NEXT_PUBLIC_ACCOUNT_BCSC_URL:
value: 'https://id.gov.bc.ca/account/'
GWA_API_URL:
value: 'https://gwa-api-gov-bc-ca.dev.api.gov.bc.ca'
GWA_PROD_ENV_SLUG:
value: 'FB000000'
GWA_RES_SVR_CLIENT_ID:
value: '${{ secrets.OIDC_CLIENT_ID }}'
secure: true
GWA_RES_SVR_CLIENT_SECRET:
value: '${{ secrets.OIDC_CLIENT_SECRET }}'
secure: true
KEYCLOAK_AUTH_URL:
value: '${{ secrets.KEYCLOAK_AUTH }}'
KEYCLOAK_REALM:
value: '${{ secrets.KEYCLOAK_REALM }}'
COOKIE_SECURE:
value: 'true'
LOG_LEVEL:
value: 'debug'
DISABLE_LOGGING:
value: 'true'
EMAIL_ENABLED:
value: 'true'
EMAIL_FROM:
value: 'API.Services.Portal@gov.bc.ca'
EMAIL_HOST:
value: 'apps.smtp.gov.bc.ca'
readinessProbe:
exec:
command:
- sh
- -c
- 'state=\$(curl -XGET -m 2 --silent -f -H \"Accept: application/json\" http://localhost:3000/health | jq -r \".status | ascii_downcase\"); if [ ! \"\$state\" == \"ready\" ]; then exit 1; fi'
timeoutSeconds: 3
periodSeconds: 10
" > values.yaml
helm repo add bcgov http://bcgov.github.io/helm-charts
helm upgrade --install proto-asp-${{ steps.set-deploy-id.outputs.DEPLOY_ID }} -f values.yaml --history-max 3 bcgov/generic-api
- name: 'Deploy Routes'
if: github.ref != 'refs/heads/dev'
run: |
export PATH=$PATH:`pwd`/linux-amd64
echo "
routes:
- host: api-services-portal-${{ steps.set-deploy-id.outputs.DEPLOY_ID }}.apps.silver.devops.gov.bc.ca
targetPort: http
service: proto-asp-${{ steps.set-deploy-id.outputs.DEPLOY_ID }}-generic-api
wildcardPolicy: None
tls:
termination: edge
" > values.yaml
helm repo add bcgov http://bcgov.github.io/helm-charts
helm upgrade --install proto-asp-${{ steps.set-deploy-id.outputs.DEPLOY_ID }}-routes -f values.yaml --history-max 3 bcgov/ocp-route
- name: 'Seed Data'
if: github.ref != 'refs/heads/dev'
run: |
export PATH=$PATH:`pwd`/linux-amd64
cd .github/workflows
SERVICE=proto-asp-${{ steps.set-deploy-id.outputs.DEPLOY_ID }}-feeder-generic-api \
PORTAL_URL=https://api-services-portal-${{ steps.set-deploy-id.outputs.DEPLOY_ID }}.apps.silver.devops.gov.bc.ca \
OIDC_ISSUER=${{ secrets.OIDC_ISSUER }} \
OIDC_CLIENT_ID=${{ secrets.OIDC_CLIENT_ID }} \
OIDC_CLIENT_SECRET=${{ secrets.OIDC_CLIENT_SECRET }} \
./scripts/init.sh
- name: Authenticate to Gold and set context
if: github.ref == 'refs/heads/test'
uses: redhat-actions/oc-login@v1
with:
openshift_server_url: ${{ secrets.OPENSHIFT_GOLD_SERVER }}
openshift_token: ${{ secrets.OPENSHIFT_GOLD_TOKEN }}
insecure_skip_tls_verify: true
namespace: ${{ secrets.OPENSHIFT_GOLD_TEST_NAMESPACE }}
- name: 'Restart Portal in Gold Test Namespace'
if: github.ref == 'refs/heads/test'
run: |
oc rollout restart deployment/bcgov-aps-portal-generic-api -n ${{ secrets.OPENSHIFT_GOLD_TEST_NAMESPACE }}
oc rollout restart deployment/bcgov-aps-portal-batch-generic-api -n ${{ secrets.OPENSHIFT_GOLD_TEST_NAMESPACE }}
- name: 'Create Pull Request for Release'
if: github.ref == 'refs/heads/test'
uses: actions/github-script@v6
with:
script: |
const { repo, owner } = context.repo;
const openPrs = await github.rest.pulls.list({
owner,
repo,
head: '${{ github.ref_name }}',
base: 'main',
state: 'open'
})
if(openPrs.data.length === 0){
await github.rest.pulls.create({
title: 'Create Latest Release',
owner,
repo,
head: '${{ github.ref_name }}',
base: 'main',
body: [
'This Pull Request is auto-created by [actions/github-script](https://github.com/actions/github-script).',
'Please update this PR with appropriate labels to target specific type of release'
].join('\n\n'),
draft: true
});
} else {
console.log("There is already an open Pull Request in place.\n")
openPrs.data.forEach((item) => {
console.log(item.html_url)
})
}