diff --git a/.github/workflows/deploy-to-openshift-frontend-dev.yml b/.github/workflows/deploy-to-openshift-frontend-dev.yml index 8dd75bf2..902342c2 100644 --- a/.github/workflows/deploy-to-openshift-frontend-dev.yml +++ b/.github/workflows/deploy-to-openshift-frontend-dev.yml @@ -147,7 +147,7 @@ jobs: oc login --token=${{ env.OPENSHIFT_TOKEN }} --server=${{ env.OPENSHIFT_SERVER }} oc project ${{ env.OPENSHIFT_NAMESPACE }} # Cancel any rollouts in progress - oc rollout cancel dc/${{ env.APP_NAME }}-${{ env.IMAGE_NAME }}-${{ env.APP_ENVIRONMENT }} 2> /dev/null \ + oc rollout cancel deployment/${{ env.APP_NAME }}-${{ env.IMAGE_NAME }}-${{ env.APP_ENVIRONMENT }} 2> /dev/null \ || true && echo "No rollout in progress" # Create the image stream if it doesn't exist @@ -160,7 +160,7 @@ jobs: # Process and apply deployment template oc process \ - -f tools/openshift/frontend.dc.yaml \ + -f tools/openshift/frontend.deployment.yaml \ -p APP_NAME=${{ env.APP_NAME }} \ -p REPO_NAME=${{ env.REPO_NAME }} \ -p BRANCH=${{ env.BRANCH }} \ @@ -183,12 +183,12 @@ jobs: | oc apply -f - # Start rollout (if necessary) and follow it - oc rollout latest dc/${{ env.APP_NAME }}-${{ env.IMAGE_NAME }}-${{ env.APP_ENVIRONMENT }} 2> /dev/null \ + oc rollout restart deployment/${{ env.APP_NAME }}-${{ env.IMAGE_NAME }}-${{ env.APP_ENVIRONMENT }} 2> /dev/null \ || true && echo "Rollout in progress" # Get status, returns 0 if rollout is successful - oc rollout status dc/${{ env.APP_NAME }}-${{ env.IMAGE_NAME }}-${{ env.APP_ENVIRONMENT }} - + oc rollout status deployment/${{ env.APP_NAME }}-${{ env.IMAGE_NAME }}-${{ env.APP_ENVIRONMENT }} + - name: ZAP Scan uses: zaproxy/action-full-scan@v0.8.0 with: diff --git a/tools/openshift/frontend.deployment.yaml b/tools/openshift/frontend.deployment.yaml new file mode 100644 index 00000000..8bd6e6b4 --- /dev/null +++ b/tools/openshift/frontend.deployment.yaml @@ -0,0 +1,237 @@ +--- + apiVersion: template.openshift.io/v1 + kind: Template + labels: + template: "${REPO_NAME}-template" + metadata: + name: "${REPO_NAME}-frontend-dc" + objects: + - apiVersion: apps/v1 + kind: Deployment + metadata: + annotations: + openshift.io/generated-by: OpenShiftNewApp + creationTimestamp: + labels: + app: "${APP_NAME}-${BRANCH}" + branch: "${BRANCH}" + name: "${APP_NAME}-frontend-${APP_ENVIRONMENT}" + spec: + replicas: ${{MIN_REPLICAS}} + selector: + matchLabels: + app: "${APP_NAME}-${BRANCH}" + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 25% + maxSurge: 25% + template: + metadata: + annotations: + openshift.io/generated-by: OpenShiftNewApp + creationTimestamp: + labels: + app: "${APP_NAME}-${BRANCH}" + deploymentconfig: "${APP_NAME}-frontend-${APP_ENVIRONMENT}" + spec: + containers: + - image: image-registry.openshift-image-registry.svc:5000/${NAMESPACE}/${REPO_NAME}-frontend-${BRANCH}:${TAG} + imagePullPolicy: Always + volumeMounts: + - name: tls-certs + mountPath: "/etc/tls-certs" + readOnly: true + - name: config-env + mountPath: "/srv/js/config" + livenessProbe: + failureThreshold: 3 + httpGet: + path: "/" + port: 2015 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: "${APP_NAME}-frontend-${APP_ENVIRONMENT}" + ports: + - containerPort: 2015 + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: "/" + port: 2015 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: "${MIN_CPU}" + memory: "${MIN_MEM}" + limits: + cpu: "${MAX_CPU}" + memory: "${MAX_MEM}" + volumes: + - name: tls-certs + secret: + secretName: ccof-frontend-cert + - name: config-env + configMap: + name: ccof-frontend-${APP_ENVIRONMENT}-config-map + test: false + - apiVersion: v1 + kind: Service + metadata: + annotations: + service.alpha.openshift.io/serving-cert-secret-name: "ccof-frontend-cert" + openshift.io/generated-by: OpenShiftNewApp + creationTimestamp: + labels: + app: "${APP_NAME}-${BRANCH}" + name: "${APP_NAME}-frontend-${APP_ENVIRONMENT}" + spec: + ports: + - name: 2015-tcp + port: 2015 + protocol: TCP + targetPort: 2015 + selector: + matchLabels: + app: "${APP_NAME}-${BRANCH}" + - apiVersion: v1 + kind: Route + metadata: + annotations: + openshift.io/host.generated: 'true' + labels: + app: "${APP_NAME}-${BRANCH}" + name: "${APP_NAME}-frontend-${APP_ENVIRONMENT}" + spec: + host: "${HOST_ROUTE}" + port: + targetPort: 2015-tcp + tls: + caCertificate: "${CA_CERT}" + certificate: "${CERTIFICATE}" + insecureEdgeTerminationPolicy: Redirect + key: "${PRIVATE_KEY}" + termination: edge + to: + kind: Service + name: "${APP_NAME}-frontend-${APP_ENVIRONMENT}" + weight: 100 + wildcardPolicy: None + - apiVersion: autoscaling/v2 + kind: HorizontalPodAutoscaler + metadata: + name: "${APP_NAME}-frontend-${APP_ENVIRONMENT}-cpu-autoscaler" + spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: "${APP_NAME}-frontend-${APP_ENVIRONMENT}" + minReplicas: ${{MIN_REPLICAS}} + maxReplicas: ${{MAX_REPLICAS}} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 90 + - apiVersion: v1 + kind: ConfigMap + metadata: + name: ccof-frontend-${APP_ENVIRONMENT}-config-map + namespace: '${NAMESPACE}' + data: + config.js: | + const config = { + VUE_APP_META_DATA: [ { name: 'robots', content: 'noindex,nofollow' } ], + BANNER_COLOR: '${BANNER_COLOR}', + BANNER_ENVIRONMENT: '${BANNER_ENVIRONMENT}', + VUE_APP_BCEID_REG_URL: '${VUE_APP_BCEID_REG_URL}', + DECB_VALIDATION_BYPASS: true, + }; + snowplow.js: |- + // + ;(function(p,l,o,w,i,n,g){if(!p[i]){p.GlobalSnowplowNamespace=p.GlobalSnowplowNamespace||[]; + p.GlobalSnowplowNamespace.push(i);p[i]=function(){(p[i].q=p[i].q||[]).push(arguments) + };p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1; + n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","https://www2.gov.bc.ca/StaticWebResources/static/sp/sp-2-14-0.js","snowplow")); + var collector = 'spm.apps.gov.bc.ca'; + window.snowplow('newTracker','rt',collector, { + appId: 'Snowplow_standalone_CCFRI', + cookieLifetime: 86400 * 548, + platform: 'web', + post: true, + forceSecureTracker: true, + contexts: { + webPage: true, + performanceTiming: true + } + }); + window.snowplow('enableActivityTracking', 30, 30); // Ping every 30 seconds after 30 seconds + window.snowplow('enableLinkClickTracking'); + window.snowplow('trackPageView'); + // + parameters: + - name: REPO_NAME + description: Application repository name + required: true + - name: BRANCH + description: Job identifier (i.e. 'pr-5' OR 'master') + required: true + - name: NAMESPACE + description: Target namespace reference (i.e. 'k8vopl-dev') + required: true + - name: APP_NAME + description: Application name + required: true + - name: HOST_ROUTE + description: The host the route will use to expose service outside cluster + required: true + - name: TAG + description: The identifying tag for this specific deployment + required: true + - name: MIN_REPLICAS + description: The minimum amount of replicas + required: true + - name: MAX_REPLICAS + description: The maximum amount of replicas + required: true + - name: MIN_CPU + description: The minimum amount of cpu + required: true + - name: MAX_CPU + description: The maximum amount of cpu + required: true + - name: MIN_MEM + description: The minimum amount of memory + required: true + - name: MAX_MEM + description: The maximum amount of memory + required: true + - name: CA_CERT + description: The CA Certificate + required: true + - name: CERTIFICATE + description: The Certificate + required: true + - name: PRIVATE_KEY + description: The private key + required: true + - name: BANNER_ENVIRONMENT + description: Environment label for the portal banner + required: true + - name: BANNER_COLOR + description: The color for the environment label in the portal + required: true + - name: VUE_APP_BCEID_REG_URL + description: The bceid registration URL + required: true + - name: APP_ENVIRONMENT + description: The environment being created ('dev', 'qa', 'uat', 'prod') + required: true