diff --git a/.github/workflows/deploy_to_eks.yml b/.github/workflows/deploy_to_eks.yml index 9baa02344a..b2f4070704 100644 --- a/.github/workflows/deploy_to_eks.yml +++ b/.github/workflows/deploy_to_eks.yml @@ -35,6 +35,17 @@ jobs: uses: aws-actions/amazon-ecr-login@v2 - name: Setup Docker Buildx uses: docker/setup-buildx-action@v3 + - name: Set env vars with direnv # This allows docker build of FE files to pull from .envrc + uses: HatsuneMiku3939/direnv-action@v1 + - name: Calculate build args + id: calculate-build-args + run: | + echo "VITE_LD_CLIENT_ID=${VITE_LD_CLIENT_ID}" >> $GITHUB_OUTPUT + echo "VITE_OKTA_CLIENT_ID=${VITE_OKTA_CLIENT_ID}" >> $GITHUB_OUTPUT + echo "VITE_OKTA_SERVER_ID=${VITE_OKTA_SERVER_ID}" >> $GITHUB_OUTPUT + echo "VITE_OKTA_ISSUER=${VITE_OKTA_ISSUER}" >> $GITHUB_OUTPUT + echo "VITE_OKTA_DOMAIN=${VITE_OKTA_DOMAIN}" >> $GITHUB_OUTPUT + echo "VITE_OKTA_REDIRECT_URI=${VITE_OKTA_REDIRECT_URI}" >> $GITHUB_OUTPUT - name: Build, tag, and push image to Amazon ECR uses: docker/build-push-action@v6 with: @@ -44,6 +55,13 @@ jobs: cache-to: type=gha,mode=max,scope=${{ env.GIT_REF_NAME }}-easi-frontend cache-from: type=gha,scope=${{ env.GIT_REF_NAME }}-easi-frontend tags: ${{ steps.login-ecr.outputs.registry }}/easi-frontend:${{ env.GIT_HASH }} + build-args: | + VITE_LD_CLIENT_ID=${{ steps.calculate-build-args.outputs.VITE_LD_CLIENT_ID }} + VITE_OKTA_CLIENT_ID=${{ steps.calculate-build-args.outputs.VITE_OKTA_CLIENT_ID }} + VITE_OKTA_SERVER_ID=${{ steps.calculate-build-args.outputs.VITE_OKTA_SERVER_ID }} + VITE_OKTA_ISSUER=${{ steps.calculate-build-args.outputs.VITE_OKTA_ISSUER }} + VITE_OKTA_DOMAIN=${{ steps.calculate-build-args.outputs.VITE_OKTA_DOMAIN }} + VITE_OKTA_REDIRECT_URI=${{ steps.calculate-build-args.outputs.VITE_OKTA_REDIRECT_URI }} - name: Announce failure if: ${{ failure() }} run: | @@ -74,4 +92,9 @@ jobs: run: aws eks update-kubeconfig --name dev-easi-poc-cluster --region us-west-2 - name: Deploy to EKS run: | - ./scripts/deploy_eks_env.sh + . ./scripts/deploy_eks_env.sh + echo "# EKS Ingress URLs" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- [EASi](http://${EASI_INGRESS})" >> $GITHUB_STEP_SUMMARY + echo "- [Mailcatcher](http://${EMAIL_INGRESS})" >> $GITHUB_STEP_SUMMARY + echo "- [Minio Console](http://${MINIO_CONSOLE_INGRESS})" >> $GITHUB_STEP_SUMMARY diff --git a/Dockerfile.backend_k8s b/Dockerfile.backend_k8s deleted file mode 100644 index f030091132..0000000000 --- a/Dockerfile.backend_k8s +++ /dev/null @@ -1,37 +0,0 @@ -FROM golang:1.22.7 AS base - -WORKDIR /easi/ - -COPY config/tls/*.crt /usr/local/share/ca-certificates/ -RUN update-ca-certificates - -COPY go.mod go.sum ./ -RUN go mod download - -FROM base AS build - -COPY cmd/ ./cmd/ -COPY pkg/ ./pkg/ -RUN CGO_ENABLED=0 GOOS=linux go build -a -o bin/easi ./cmd/easi - -FROM gcr.io/distroless/base:latest - -WORKDIR /easi/ - -COPY --from=base /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -COPY --from=build /easi/pkg/email/templates ./templates -COPY --from=build /easi/bin/easi ./ - -ARG ARG_APPLICATION_VERSION -ARG ARG_APPLICATION_DATETIME -ARG ARG_APPLICATION_TS -ENV APPLICATION_VERSION=${ARG_APPLICATION_VERSION} \ - APPLICATION_DATETIME=${ARG_APPLICATION_DATETIME} \ - APPLICATION_TS=${ARG_APPLICATION_TS} \ - EMAIL_TEMPLATE_DIR=/easi/templates - -USER 1000 - -ENTRYPOINT ["/easi/easi"] - -CMD ["serve"] diff --git a/Dockerfile.frontend_k8s b/Dockerfile.frontend_k8s index b1c63f7f14..d8d2e046ae 100644 --- a/Dockerfile.frontend_k8s +++ b/Dockerfile.frontend_k8s @@ -16,4 +16,38 @@ COPY tsconfig.json .eslintrc vite.config.ts apollo.config.js index.html ./ COPY public ./public COPY src ./src -ENTRYPOINT [ "yarn", "start" ] +# Pass in from outside (.envrc) +ARG VITE_LD_CLIENT_ID +ARG VITE_OKTA_CLIENT_ID +ARG VITE_OKTA_SERVER_ID +ARG VITE_OKTA_ISSUER +ARG VITE_OKTA_DOMAIN +ARG VITE_OKTA_REDIRECT_URI + +# Difficult (depend on k8s instantiation of ingress) +# ARG VITE_API_ADDRESS +# ARG VITE_GRAPHQL_ADDRESS + +ENV VITE_OKTA_CLIENT_ID=${VITE_OKTA_CLIENT_ID} \ + VITE_OKTA_SERVER_ID=${VITE_OKTA_SERVER_ID} \ + VITE_LD_CLIENT_ID=${VITE_LD_CLIENT_ID} \ + VITE_APP_ENV=local \ + VITE_OKTA_ISSUER=${VITE_OKTA_ISSUER} \ + # VITE_API_ADDRESS=${VITE_API_ADDRESS} \ + # VITE_GRAPHQL_ADDRESS=${VITE_GRAPHQL_ADDRESS} \ + VITE_LOCAL_AUTH_ENABLED=true \ + VITE_OKTA_DOMAIN=${VITE_OKTA_DOMAIN} \ + VITE_OKTA_REDIRECT_URI=${VITE_OKTA_REDIRECT_URI} + +RUN yarn build + +# +# Final layer to export just built HTML +# +FROM nginx:1.27.1-alpine + +# Copy built files from yarn build layer +COPY --from=build /app/build /usr/share/nginx/html + +# https://github.com/nginxinc/docker-nginx/blob/04d0c5754673d6880b91e94c3cebaa767d9a1af7/Dockerfile +ENTRYPOINT ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/deploy/base/easi/easi-backend_deployment.yaml b/deploy/base/easi/easi-backend_deployment.yaml index 89c86f3903..2d3d8b0e8b 100644 --- a/deploy/base/easi/easi-backend_deployment.yaml +++ b/deploy/base/easi/easi-backend_deployment.yaml @@ -27,6 +27,6 @@ spec: protocol: TCP resources: {} command: - - ./bin/easi + - /easi/easi - serve restartPolicy: Always diff --git a/deploy/base/easi/easi-frontend_configmap.yaml b/deploy/base/easi/easi-frontend_configmap.yaml deleted file mode 100644 index 8a51f94a80..0000000000 --- a/deploy/base/easi/easi-frontend_configmap.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: easi-frontend-configmap - namespace: easi -data: - VITE_APP_ENV: local - VITE_API_ADDRESS: http://easi-backend.localdev.me/api/v1 - VITE_GRAPHQL_ADDRESS: http://easi-backend.localdev.me/api/graph/query - VITE_OKTA_CLIENT_ID: 0oa2e913coDQeG19S297 - VITE_OKTA_DOMAIN: https://test.idp.idm.cms.gov - VITE_OKTA_ISSUER: https://test.idp.idm.cms.gov/oauth2/aus2e96etlbFPnBHt297 - VITE_OKTA_REDIRECT_URI: http://easi-client:3000/implicit/callback - VITE_OKTA_SERVER_ID: aus2e96etlbFPnBHt297 - VITE_LD_ENV_USER: '' - VITE_LD_USER_HASH: '' - VITE_LD_CLIENT_ID: 63231d448bd05a111f06195b - VITE_LOCAL_AUTH_ENABLED: 'true' - CHOKIDAR_USEPOLLING: 'true' - ESLINT_NO_DEV_ERRORS: 'true' diff --git a/deploy/base/easi/easi-frontend_deployment.yaml b/deploy/base/easi/easi-frontend_deployment.yaml index 2844f94585..1c3da1e814 100644 --- a/deploy/base/easi/easi-frontend_deployment.yaml +++ b/deploy/base/easi/easi-frontend_deployment.yaml @@ -19,11 +19,8 @@ spec: - name: easi-frontend image: easi-frontend:latest imagePullPolicy: Never - envFrom: - - configMapRef: - name: easi-frontend-configmap ports: - - containerPort: 3000 + - containerPort: 80 protocol: TCP resources: {} restartPolicy: Always diff --git a/deploy/base/easi/easi-frontend_service.yaml b/deploy/base/easi/easi-frontend_service.yaml index 1b04b7c9a5..1e54f31324 100644 --- a/deploy/base/easi/easi-frontend_service.yaml +++ b/deploy/base/easi/easi-frontend_service.yaml @@ -5,7 +5,7 @@ metadata: name: easi-frontend spec: ports: - - port: 3000 - targetPort: 3000 + - port: 80 + targetPort: 80 selector: app: easi-frontend diff --git a/deploy/base/easi/kustomization.yaml b/deploy/base/easi/kustomization.yaml index 1cacc0500e..6b502c2161 100644 --- a/deploy/base/easi/kustomization.yaml +++ b/deploy/base/easi/kustomization.yaml @@ -12,7 +12,6 @@ resources: - easi-backend_configmap.yaml - easi-backend_deployment.yaml - easi-backend_service.yaml - - easi-frontend_configmap.yaml - easi-frontend_deployment.yaml - easi-frontend_service.yaml - email_deployment.yaml diff --git a/deploy/base/ingress/easi_ingress.yaml b/deploy/base/ingress/easi_ingress.yaml index 6a5448a233..d210d886de 100644 --- a/deploy/base/ingress/easi_ingress.yaml +++ b/deploy/base/ingress/easi_ingress.yaml @@ -17,7 +17,7 @@ spec: service: name: easi-frontend port: - number: 3000 + number: 80 - path: /api pathType: Prefix backend: @@ -25,17 +25,3 @@ spec: name: easi-backend port: number: 8080 - - path: /mail - pathType: Prefix - backend: - service: - name: email - port: - number: 1080 - - path: /minio - pathType: Prefix - backend: - service: - name: minio - port: - number: 9001 diff --git a/deploy/overlays/pr/easi/easi-backend_deployment.yaml b/deploy/overlays/pr/easi/easi-backend_deployment.yaml index 3d22331d22..4917c49010 100644 --- a/deploy/overlays/pr/easi/easi-backend_deployment.yaml +++ b/deploy/overlays/pr/easi/easi-backend_deployment.yaml @@ -2,10 +2,6 @@ path: /spec/template/spec/containers/0/imagePullPolicy value: Always -- op: replace - path: /spec/template/spec/containers/0/command - value: ["/easi/easi", "serve"] - - op: replace path: /spec/template/spec/containers/0/resources value: diff --git a/deploy/overlays/pr/easi/easi-frontend_configmap.yaml b/deploy/overlays/pr/easi/easi-frontend_configmap.yaml deleted file mode 100644 index 8bdf6e1cd3..0000000000 --- a/deploy/overlays/pr/easi/easi-frontend_configmap.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: easi-frontend-configmap - namespace: easi -data: - VITE_API_ADDRESS: http://${EASI_INGRESS}/api/v1 - VITE_GRAPHQL_ADDRESS: http://${EASI_INGRESS}/api/graph/query - VITE_OKTA_REDIRECT_URI: http://${EASI_INGRESS}/implicit/callback diff --git a/deploy/overlays/pr/easi/kustomization.yaml b/deploy/overlays/pr/easi/kustomization.yaml index 537f2818af..9378e285f1 100644 --- a/deploy/overlays/pr/easi/kustomization.yaml +++ b/deploy/overlays/pr/easi/kustomization.yaml @@ -13,9 +13,6 @@ patches: - path: easi-backend_configmap.yaml target: name: easi-backend-configmap -- path: easi-frontend_configmap.yaml - target: - name: easi-frontend-configmap # Patches for deployment changes - path: easi-backend_deployment.yaml diff --git a/deploy/overlays/pr/ingress/easi_ingress.yaml b/deploy/overlays/pr/ingress/easi_ingress.yaml index e554a86dc6..8ef2857f42 100644 --- a/deploy/overlays/pr/ingress/easi_ingress.yaml +++ b/deploy/overlays/pr/ingress/easi_ingress.yaml @@ -7,7 +7,6 @@ value: alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip - alb.ingress.kubernetes.io/healthcheck-path: /api/v1/healthcheck - op: replace path: /spec value: @@ -21,7 +20,7 @@ service: name: easi-frontend port: - number: 3000 + number: 80 - path: /api pathType: Prefix backend: @@ -29,17 +28,3 @@ name: easi-backend port: number: 8080 - - path: /mail - pathType: Prefix - backend: - service: - name: email - port: - number: 1080 - - path: /minio - pathType: Prefix - backend: - service: - name: minio - port: - number: 9001 diff --git a/scripts/k8s_dev.sh b/scripts/k8s_dev.sh index 197829571b..48d7a8152e 100755 --- a/scripts/k8s_dev.sh +++ b/scripts/k8s_dev.sh @@ -90,21 +90,30 @@ if kubectl get ns "$NAMESPACE" > /dev/null 2>&1; then } fi +APPLICATION_VERSION="$(git rev-parse @)" +APPLICATION_DATETIME="$(date --rfc-3339='seconds' --utc)" +APPLICATION_TS="$(date --date="$APPLICATION_DATETIME" '+%s')" + +export APPLICATION_VERSION +export APPLICATION_DATETIME +export APPLICATION_TS + # Build Docker images ( echo "🐋 Building easi-frontend:${NAMESPACE} image 🐋" - docker build -f ../Dockerfile.frontend_k8s -t easi-frontend:"$NAMESPACE" ../. - - # APPLICATION_VERSION=$(git rev-parse HEAD) - # APPLICATION_DATETIME="$(date --rfc-3339='seconds' --utc)" - # APPLICATION_TS="$(date --date="$APPLICATION_DATETIME" '+%s')" - # echo "APPLICATION_DATETIME=${APPLICATION_DATETIME}" - # echo "APPLICATION_TS=${APPLICATION_TS}" - # echo "APPLICATION_VERSION=${APPLICATION_VERSION}" + docker build -f ../Dockerfile.frontend_k8s -t easi-frontend:"$NAMESPACE" ../. \ + --build-arg VITE_LD_CLIENT_ID=63231d448bd05a111f06195b \ + --build-arg VITE_OKTA_CLIENT_ID=0oa2e913coDQeG19S297 \ + --build-arg VITE_OKTA_SERVER_ID=aus2e96etlbFPnBHt297 \ + --build-arg VITE_OKTA_ISSUER=https://test.idp.idm.cms.gov/oauth2/aus2e96etlbFPnBHt297 \ + --build-arg VITE_OKTA_DOMAIN=https://test.idp.idm.cms.gov \ + --build-arg VITE_OKTA_REDIRECT_URI=http://localhost:3000/implicit/callback echo "🐋 Building easi-backend:${NAMESPACE} image 🐋" - # docker build -f ../Dockerfile --build-arg APPLICATION_DATETIME="${APPLICATION_DATETIME}" --build-arg APPLICATION_TS="${APPLICATION_TS}" --build-arg APPLICATION_VERSION="${APPLICATION_VERSION}" -t easi-backend:latest ../. - docker build -f ../Dockerfile.backend_k8s --target build -t easi-backend:"$NAMESPACE" ../. + docker build -f ../Dockerfile -t easi-backend:"$NAMESPACE" ../. \ + --build-arg APPLICATION_DATETIME="${APPLICATION_DATETIME}" \ + --build-arg APPLICATION_TS="${APPLICATION_TS}" \ + --build-arg APPLICATION_VERSION="${APPLICATION_VERSION}" echo "🐋 Building db-migrate:${NAMESPACE} image 🐋" docker build -f ../Dockerfile.db_migrations --build-arg TAG=9.10-alpine -t db-migrate:"$NAMESPACE" ../. @@ -134,7 +143,6 @@ delete_temp_dir() { kustomize edit set namespace "$NAMESPACE" kustomize build > manifest.yaml - sed -i'' -E "s/easi-backend.localdev.me/${NAMESPACE}-backend.localdev.me/" manifest.yaml sed -i'' -E "s/easi.localdev.me/${NAMESPACE}.localdev.me/" manifest.yaml sed -i'' -E "s/email.localdev.me/${NAMESPACE}-email.localdev.me/" manifest.yaml sed -i'' -E "s/minio.localdev.me/${NAMESPACE}-minio.localdev.me/" manifest.yaml @@ -143,7 +151,7 @@ delete_temp_dir() { ) ( - TEMPDIR=$(mktemp -d ../tmp.ingress.XXXXX) + TEMPDIR=$(mktemp -d ../tmp.easi.XXXXX) cd "$TEMPDIR" || exit kustomize create --resources ../deploy/base/easi kustomize edit set namespace "$NAMESPACE" diff --git a/src/index.tsx b/src/index.tsx index e9f01f28c3..f1e6cc8298 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -20,7 +20,9 @@ import store from './store'; import './index.scss'; -const apiHost = new URL(import.meta.env.VITE_API_ADDRESS || '').host; +const apiHost = new URL( + import.meta.env.VITE_API_ADDRESS || window.location.origin +).host; // Initialize tracker for Google Analytics ReactGA.initialize([ @@ -61,12 +63,20 @@ function getAuthHeader(targetUrl: string) { /** * Setup client for GraphQL */ + +// Pull the graphql address from the vite environment variables +// However, if we don't have a VITE_GRAPHQL_ADDRESS, we should simply assume that the API is hosted on the same domain & port as the frontend +// We also assume a path of /api/graph/query should be tacked onto that +const graphqlAddress = + import.meta.env.VITE_GRAPHQL_ADDRESS || + `${window.location.origin}/api/graph/query`; + const uploadLink = createUploadLink({ - uri: import.meta.env.VITE_GRAPHQL_ADDRESS + uri: graphqlAddress }); const authLink = setContext((request, { headers }) => { - const header = getAuthHeader(import.meta.env.VITE_GRAPHQL_ADDRESS as string); + const header = getAuthHeader(graphqlAddress); return { headers: { ...headers, diff --git a/src/sagas/actionSaga.ts b/src/sagas/actionSaga.ts index d79800b38d..cc003dab3a 100644 --- a/src/sagas/actionSaga.ts +++ b/src/sagas/actionSaga.ts @@ -5,11 +5,15 @@ import { call, put, StrictEffect, takeLatest } from 'redux-saga/effects'; import { Action } from 'types/action'; import { postAction } from 'types/routines'; +// Pull the API address from the vite environment variables +// However, if we don't have a VITE_API_ADDRESS, we should simply assume that the API is hosted on the same domain & port as the frontend +// We also assume a path of /api/v1 should be tacked onto that +const apiAddress = + import.meta.env.VITE_API_ADDRESS || `${window.location.origin}/api/v1`; + export function postSystemIntakeActionRequest(formData: Action) { return axios.post( - `${import.meta.env.VITE_API_ADDRESS}/system_intake/${ - formData.intakeId - }/actions`, + `${apiAddress}/system_intake/${formData.intakeId}/actions`, formData ); } diff --git a/src/sagas/businessCaseSaga.ts b/src/sagas/businessCaseSaga.ts index 73be6b8503..e8bc1e1842 100644 --- a/src/sagas/businessCaseSaga.ts +++ b/src/sagas/businessCaseSaga.ts @@ -10,8 +10,14 @@ import { putBusinessCase } from 'types/routines'; +// Pull the API address from the vite environment variables +// However, if we don't have a VITE_API_ADDRESS, we should simply assume that the API is hosted on the same domain & port as the frontend +// We also assume a path of /api/v1 should be tacked onto that +const apiAddress = + import.meta.env.VITE_API_ADDRESS || `${window.location.origin}/api/v1`; + function getBusinessCaseRequest(id: string) { - return axios.get(`${import.meta.env.VITE_API_ADDRESS}/business_case/${id}`); + return axios.get(`${apiAddress}/business_case/${id}`); } function* getBusinessCase( @@ -30,7 +36,7 @@ function* getBusinessCase( function postBusinessCaseRequest(formData: BusinessCaseModel) { const data = prepareBusinessCaseForApi(formData); - return axios.post(`${import.meta.env.VITE_API_ADDRESS}/business_case`, data); + return axios.post(`${apiAddress}/business_case`, data); } function* createBusinessCase( @@ -49,10 +55,7 @@ function* createBusinessCase( function putBusinessCaseRequest(formData: BusinessCaseModel) { const data = prepareBusinessCaseForApi(formData); - return axios.put( - `${import.meta.env.VITE_API_ADDRESS}/business_case/${data.id}`, - data - ); + return axios.put(`${apiAddress}/business_case/${data.id}`, data); } function* updateBusinessCase(