diff --git a/.github/workflows/pr-open.yml b/.github/workflows/pr-open.yml index b933a78..c9e2f78 100644 --- a/.github/workflows/pr-open.yml +++ b/.github/workflows/pr-open.yml @@ -87,7 +87,39 @@ jobs: # Deploy Helm Chart cd charts/pubcode helm dependency update - helm upgrade --install --wait --atomic pubcode-${{ github.event.number }} --values values-pr.yaml --set-string global.repository=${{ github.repository }} --set-string api.containers[0].tag="${{ github.sha }}" --set-string frontend.containers[0].tag="${{ github.sha }}" --set-string global.secrets.emailRecipients="${{ secrets.EMAIL_RECIPIENTS }}" --set-string global.secrets.chesTokenURL="${{ secrets.CHES_TOKEN_URL }}" --set-string global.secrets.chesClientID="${{ secrets.CHES_CLIENT_ID }}" --set-string global.secrets.chesClientSecret="${{ secrets.CHES_CLIENT_SECRET }}" --set-string global.secrets.chesAPIURL="${{ secrets.CHES_API_URL }}" --set-string global.secrets.databaseAdminPassword="${{ secrets.DB_PWD }}" --set-string global.env.VITE_SCHEMA_BRANCH=${{ github.event.pull_request.head.ref }} --set-string namespace="${{ vars.oc_namespace }}" --timeout 5m . + helm upgrade --install --wait --atomic pubcode-${{ github.event.number }} \ + --set-string global.repository=${{ github.repository }} \ + --set-string api.containers[0].tag="${{ github.sha }}" \ + --set-string api.containers[0].resources.limits.cpu="250m" \ + --set-string api.containers[0].resources.limits.memory="250Mi" \ + --set-string api.containers[0].resources.requests.cpu="100m" \ + --set-string api.containers[0].resources.requests.memory="150Mi" \ + --set api.autoscaling.minReplicas=1 \ + --set api.autoscaling.maxReplicas=1 \ + --set frontend.autoscaling.minReplicas=1 \ + --set frontend.autoscaling.maxReplicas=1 \ + --set-string frontend.containers[0].tag="${{ github.sha }}" \ + --set-string frontend.containers[0].resources.limits.cpu="200m" \ + --set-string frontend.containers[0].resources.limits.memory="150Mi" \ + --set-string frontend.containers[0].resources.requests.cpu="100m" \ + --set-string frontend.containers[0].resources.requests.memory="50Mi" \ + --set-string database.containers[0].resources.limits.cpu="500m" \ + --set-string database.containers[0].resources.limits.memory="450Mi" \ + --set-string database.containers[0].resources.requests.cpu="100m" \ + --set-string database.containers[0].resources.requests.memory="150Mi" \ + --set-string database.initContainers[0].resources.limits.cpu="500m" \ + --set-string database.initContainers[0].resources.limits.memory="450Mi" \ + --set-string database.initContainers[0].resources.requests.cpu="100m" \ + --set-string database.initContainers[0].resources.requests.memory="150Mi" \ + --set-string database.pvc.size="350Mi" \ + --set-string global.secrets.emailRecipients="${{ secrets.EMAIL_RECIPIENTS }}" \ + --set-string global.secrets.chesTokenURL="${{ secrets.CHES_TOKEN_URL }}" \ + --set-string global.secrets.chesClientID="${{ secrets.CHES_CLIENT_ID }}" \ + --set-string global.secrets.chesClientSecret="${{ secrets.CHES_CLIENT_SECRET }}" \ + --set-string global.secrets.chesAPIURL="${{ secrets.CHES_API_URL }}" \ + --set-string global.secrets.databaseAdminPassword="${{ secrets.DB_PWD }}" \ + --set-string global.env.VITE_SCHEMA_BRANCH=${{ github.event.pull_request.head.ref }} \ + --set-string namespace="${{ vars.oc_namespace }}" -f values.yaml --timeout 5m . cypress-e2e: name: Cypress end to end test diff --git a/api/src/app.js b/api/src/app.js index 44c18ec..d9e3630 100644 --- a/api/src/app.js +++ b/api/src/app.js @@ -40,5 +40,9 @@ app.get("/", (req, res, next) => { }); app.use(/(\/api)?/, apiRouter); apiRouter.use("/pub-code", pubcodeRouter); +app.use((req, res, next) => { + res.status(404).send( + "

Not Found.

"); +}); module.exports = app; diff --git a/api/src/email/ches-service.js b/api/src/email/ches-service.js index d7661eb..adb717f 100644 --- a/api/src/email/ches-service.js +++ b/api/src/email/ches-service.js @@ -26,7 +26,7 @@ class ChesService { }); return { data, status }; } catch (e) { - console.log(SERVICE, e); + console.error(SERVICE, e); } } @@ -52,7 +52,7 @@ class ChesService { return { data, status }; } } catch (e) { - console.log(SERVICE, e); + console.error(SERVICE, e); } } @@ -81,7 +81,7 @@ class ChesService { return { data, status }; } } catch (e) { - console.log(SERVICE, e); + console.error(SERVICE, e); } } @@ -100,7 +100,8 @@ class ChesService { ); return { data, status }; } catch (e) { - console.log(SERVICE, e?.config?.data?.errors); + console.error(e); + console.error(SERVICE, e?.config?.data?.errors); } } @@ -119,7 +120,7 @@ class ChesService { ); return { data, status }; } catch (e) { - console.log(SERVICE, e); + console.error(SERVICE, e); } } @@ -138,7 +139,7 @@ class ChesService { ); return { data, status }; } catch (e) { - console.log(SERVICE, e); + console.error(SERVICE, e); } } @@ -167,7 +168,7 @@ class ChesService { return { data, status }; } } catch (e) { - console.log(SERVICE, e); + console.error(SERVICE, e); } } diff --git a/api/src/routes/pubcode-router.js b/api/src/routes/pubcode-router.js index c57b242..192160b 100644 --- a/api/src/routes/pubcode-router.js +++ b/api/src/routes/pubcode-router.js @@ -1,6 +1,6 @@ const express = require("express"); const router = express.Router(); -const { bulkLoad, readAll, findById, health } = require("../services/pub-code-service"); +const { bulkLoad, readAll, findById, health, softDeleteRepo } = require("../services/pub-code-service"); router.get("/ip/trace", (request, response) => response.send(request.ip)); router.get("/health", health); @@ -11,8 +11,16 @@ router.post("/bulk-load", (req, res, next) => { res.status(401).json({ message: "Unauthorized" }); } }, bulkLoad); - +/** + * This method allows for patching the objects with soft + */ +router.delete("/:repo_name", (req, res, next) => { + if (req.header("X-API-KEY") && req.header("X-API-KEY") === process.env.API_KEY) { + next(); + } else { + res.status(401).json({ message: "Unauthorized" }); + } +}, softDeleteRepo); router.get("/", readAll); -router.get("/:id", findById); module.exports = router; diff --git a/api/src/services/pub-code-service.js b/api/src/services/pub-code-service.js index 236b90a..fcae3e8 100644 --- a/api/src/services/pub-code-service.js +++ b/api/src/services/pub-code-service.js @@ -115,10 +115,26 @@ const health = async (req, res) => { res.status(500).json(error); } }; +const softDeleteRepo = async (req, res) => { + try { + let pubcodeEntityFromDB = await pubcodeEntity.findOne({ repo_name: req.params.repo_name }).exec(); + if (!pubcodeEntityFromDB) { + res.status(404).json({ message: "Repo Not Found" }); + } else { + await pubcodeEntity.updateOne({ _id: pubcodeEntityFromDB["_id"] }, { is_deleted: true }).exec(); + pubcodeEntityFromDB = await pubcodeEntity.findOne({ repo_name: req.params.repo_name }).exec(); + console.info(pubcodeEntityFromDB); + res.status(200).json({ message: "Repo Marked as soft deleted." }); + } + } catch (e) { + console.error(e); + } +}; module.exports = { bulkLoad, readAll, findById, - health + health, + softDeleteRepo }; diff --git a/charts/pubcode/Chart.yaml b/charts/pubcode/Chart.yaml index ecef98f..d499a99 100644 --- a/charts/pubcode/Chart.yaml +++ b/charts/pubcode/Chart.yaml @@ -27,18 +27,18 @@ dependencies: - name: component condition: api.enabled - version: 0.0.11 + version: 0.0.14 repository: https://bcgov.github.io/helm-service/ alias: api - name: component condition: frontend.enabled - version: 0.0.11 + version: 0.0.14 repository: https://bcgov.github.io/helm-service/ alias: frontend - name: component condition: database.enabled - version: 0.0.11 + version: 0.0.14 repository: https://bcgov.github.io/helm-service/ alias: database diff --git a/charts/pubcode/values-pr.yaml b/charts/pubcode/values-pr.yaml deleted file mode 100644 index ca939db..0000000 --- a/charts/pubcode/values-pr.yaml +++ /dev/null @@ -1,251 +0,0 @@ -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. -global: - repository: ~ # the repository where the images are stored. - registry: ghcr.io # the registry where the images are stored. override during runtime for other registry at global level or individual level. - env: - LOG_LEVEL: "info" - VITE_SCHEMA_BRANCH: "main" - secrets: - enabled: true - databaseHost: '{{ .Release.Name }}-database' - databaseAdminPassword: default - databaseAdminUser: admin - databaseName: pubcode - apiKey: default - emailRecipients: default - chesTokenURL: default - chesClientID: default - chesClientSecret: default - chesAPIURL: default - domain: "apps.silver.devops.gov.bc.ca" # it is required, apps.silver.devops.gov.bc.ca for silver cluster - openshiftImageRegistry: "image-registry.openshift-image-registry.svc:5000" - -api: - enabled: true - deployment: # can be either a statefulSet or a deployment not both - enabled: true - - containers: - - name: api - registry: '{{ .Values.global.registry }}' - repository: '{{ .Values.global.repository }}' # example, it includes registry and repository - image: api # the exact component name, be it backend, api-1 etc... - tag: prod # the tag of the image, it can be latest, 1.0.0 etc..., or the sha256 hash - envFrom: - secretRef: - name: '{{ .Release.Name }}' - ports: - - name: http - containerPort: 3000 - protocol: TCP - resources: # this is optional - limits: - cpu: 150m - memory: 250Mi - requests: - cpu: 100m - memory: 150Mi - readinessProbe: - httpGet: - path: /api/pub-code/health - port: 3000 - scheme: HTTP - initialDelaySeconds: 5 - periodSeconds: 2 - timeoutSeconds: 2 - successThreshold: 1 - failureThreshold: 30 - livenessProbe: - successThreshold: 1 - failureThreshold: 3 - httpGet: - path: /api/pub-code/health - port: 3000 - scheme: HTTP - initialDelaySeconds: 15 - periodSeconds: 30 - timeoutSeconds: 5 - - autoscaling: - enabled: true - minReplicas: 1 - maxReplicas: 1 - targetCPUUtilizationPercentage: 80 # this percentage from request cpu - vault: - enabled: false - service: - enabled: true - type: ClusterIP - ports: - - name: http - port: 80 - targetPort: 3000 # the container port where the application is listening on - protocol: TCP - nodeSelector: { } - tolerations: [ ] - affinity: { } - route: - enabled: true - host: "{{ .Release.Name }}-api.{{ .Values.global.domain }}" - targetPort: http # look at line#164 refer to the name. - -frontend: - enabled: true - deployment: # can be either a statefulSet or a deployment not both - enabled: true - containers: - - name: frontend - registry: '{{ .Values.global.registry }}' # example, it includes registry - repository: '{{ .Values.global.repository }}' # example, it includes repository - image: frontend # the exact component name, be it backend, api-1 etc... - tag: prod # the tag of the image, it can be latest, 1.0.0 etc..., or the sha256 hash - securityContext: - capabilities: - add: [ "NET_BIND_SERVICE" ] - env: - fromValues: - - name: VITE_SCHEMA_BRANCH - value: '{{ .Values.global.env.VITE_SCHEMA_BRANCH }}' - - name: LOG_LEVEL - value: '{{ .Values.global.env.LOG_LEVEL }}' - ports: - - name: http - containerPort: 3000 - protocol: TCP - - name: http2 - containerPort: 3001 - protocol: TCP - resources: # this is optional - limits: - cpu: 100m - memory: 150Mi - requests: - cpu: 50m - memory: 50Mi - readinessProbe: - httpGet: - path: /health - port: 3001 - scheme: HTTP - initialDelaySeconds: 5 - periodSeconds: 2 - timeoutSeconds: 2 - successThreshold: 1 - failureThreshold: 30 - livenessProbe: - successThreshold: 1 - failureThreshold: 3 - httpGet: - path: /health - port: 3001 - scheme: HTTP - initialDelaySeconds: 15 - periodSeconds: 30 - timeoutSeconds: 5 - autoscaling: - enabled: true - minReplicas: 1 - maxReplicas: 1 - targetCPUUtilizationPercentage: 80 # this percentage from request cpu - service: - enabled: true - type: ClusterIP - ports: - - name: http - port: 80 - targetPort: 3000 # the container port where the application is listening on - protocol: TCP - route: - enabled: true - host: "{{ .Release.Name }}.{{ .Values.global.domain }}" - targetPort: http # look at line#164 refer to the name. - -database: - enabled: true - deployment: # can be either a statefulSet or a deployment not both - enabled: true - deploymentStrategy: - type: Recreate - volumes: - - name: '{{ .Release.Name }}-database' - persistentVolumeClaim: - claimName: '{{ .Release.Name }}-database' - - name: '{{ include "component.fullname" . }}-config' - configMap: - name: '{{ include "component.fullname" . }}' - initContainers: - - name: database-init - registry: 'ghcr.io' # example, it includes registry - repository: 'bcgov/nr-containers' # example, it includes repository - image: mongo # the exact component name, be it backend, api-1 etc... - tag: 7.0.2 # the tag of the image, it can be latest, 1.0.0 etc..., or the sha256 hash - command: - - "sh" - - "-c" - - "mkdir -p /data/db" - resources: # this is optional - limits: - cpu: 500m - memory: 600Mi - requests: - cpu: 150m - memory: 250Mi - volumeMounts: - - name: '{{ .Release.Name }}-database' - mountPath: /data/db - containers: - - name: database - registry: 'ghcr.io' # example, it includes registry - repository: 'bcgov/nr-containers' # example, it includes repository - image: mongo # the exact component name, be it backend, api-1 etc... - tag: 7.0.2 # the tag of the image, it can be latest, 1.0.0 etc..., or the sha256 hash - envFrom: - secretRef: - name: '{{ .Release.Name }}' - ports: - - name: http - containerPort: 27017 - protocol: TCP - resources: # this is optional - limits: - cpu: 350m - memory: 600Mi - requests: - cpu: 150m - memory: 250Mi - readinessProbe: - tcpSocket: - port: 27017 - initialDelaySeconds: 10 - periodSeconds: 15 - timeoutSeconds: 5 - failureThreshold: 30 - livenessProbe: - failureThreshold: 20 - initialDelaySeconds: 60 - periodSeconds: 15 - tcpSocket: - port: 27017 - timeoutSeconds: 5 - volumeMounts: - - name: '{{ .Release.Name }}-database' - mountPath: /data/db - autoscaling: - enabled: false - minReplicas: 1 - maxReplicas: 1 - targetCPUUtilizationPercentage: 80 # this percentage from request cpu - service: - enabled: true - type: ClusterIP - ports: - - name: http - port: 27017 - targetPort: 27017 # the container port where the application is listening on - protocol: TCP - pvc: - enabled: true - size: 512Mi - storageClassName: netapp-file-standard - accessModes: ReadWriteMany diff --git a/utilities/remove-deleted-pubcode/index.js b/utilities/remove-deleted-pubcode/index.js index e0a6539..f56ad16 100644 --- a/utilities/remove-deleted-pubcode/index.js +++ b/utilities/remove-deleted-pubcode/index.js @@ -28,13 +28,13 @@ async function doProcess() { item.is_deleted = true; } } - results.push(item); + results.push(repoName); } catch (error) { console.error(`Failed to fetch data for ${item.repo_name}:`, error); process.exit(1); } } - await bulkLoadPubCodes(results); + await markSoftDeleted(results); } catch (error) { console.error('Failed to fetch data:', error); @@ -52,23 +52,25 @@ async function getYamlFromRepo(repoName, branchName) { return yamlResponse; } -async function bulkLoadPubCodes(yamlArrayAsJson) { - if (yamlArrayAsJson.length > 0) { - console.debug(`Found ${yamlArrayAsJson.length} yaml files to load into database.`); +async function markSoftDeleted(repoNames) { + if (repoNames.length > 0) { + console.info(`Found ${repoNames.length} yaml files to mark as soft delete.`); + console.info(repoNames); //send to backend api bulk load endpoint - try { - await axios.post(`${API_URL}/api/pub-code/bulk-load`, yamlArrayAsJson, { - headers: { - "X-API-KEY": API_KEY - } - }); - console.debug(`Successfully loaded ${yamlArrayAsJson.length} yaml files into database.`); - } catch (e) { - console.error(e.response?.status); - console.error(e.response?.config?.url); + for (const repoName of repoNames) { + try { + await axios.delete(`${API_URL}/api/pub-code/${repoName}`, { + headers: { + "X-API-KEY": API_KEY + } + }); + } catch (e) { + console.error(e.response?.status); + console.error(e.response?.config?.url); + } } } else { - console.debug(`No yaml files found at the root of repositories under bcgov.`); + console.info(`No yaml files to mark as soft delete.`); } } try{