diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..fa51abb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +.git +README.md +*.tar.gz +*.tgz +node_modules/ +.DS_Store diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..0e621ec --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,70 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000..615cc2f --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,129 @@ +name: Rating API CI/CD + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +env: + DOCKLE_HOST: "unix:///var/run/docker.sock" + CONTAINER_IMAGE: "${{ secrets.ACR_LOGIN_SERVER }}/rating-api:gh-v1.0.${{ github.run_number }}" + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout source + uses: actions/checkout@v3 + + - name: Build the container image + run: docker build . --file Dockerfile --tag ${{ env.CONTAINER_IMAGE }} + + - name: Scan container image for vulnerabilities + uses: Azure/container-scan@v0 + id: container-scan + continue-on-error: true + with: + image-name: ${{ env.CONTAINER_IMAGE }} + severity-threshold: CRITICAL + run-quality-checks: true + + - name: Login to the Azure Container Registry + uses: azure/docker-login@v1 + with: + login-server: ${{ secrets.ACR_LOGIN_SERVER }} + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Push the container image to ACR + run: | + docker push ${{ env.CONTAINER_IMAGE }} + + - name: Post logs to ASC appinsights + uses: Azure/publish-security-assessments@v0 + with: + scan-results-path: ${{ steps.container-scan.outputs.scan-report-path }} + connection-string: ${{ secrets.ASC_APPINSIGHTS_CONNECTION_STRING }} + subscription-token: ${{ secrets.ASC_AUTH_TOKEN }} + + deploy-dev-containerapps: + + runs-on: ubuntu-latest + environment: 'dev-containerapps' + needs: build + + steps: + - name: Login to Azure Subscription with SP + uses: azure/login@v1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Create or update Container App deployment + uses: Azure/cli@v1 + with: + inlineScript: | + az config set extension.use_dynamic_install=yes_without_prompt + az containerapp show -n rating-api -g ${{ secrets.RESOURCE_GROUP }} -o table + + if [[ $? != 0 ]]; then + echo "Container app not found, creating a new one..." + + az containerapp create \ + --name rating-api \ + --resource-group ${{ secrets.RESOURCE_GROUP }} \ + --image ${{ env.CONTAINER_IMAGE }} \ + --environment ${{ secrets.CONTAINERAPPS_ENVIRONMENT }} \ + --registry-server ${{ secrets.ACR_LOGIN_SERVER }} \ + --registry-username ${{ secrets.REGISTRY_USERNAME }} \ + --registry-password ${{ secrets.REGISTRY_PASSWORD }} \ + --min-replicas 1 \ + --max-replicas 1 \ + --enable-dapr \ + --dapr-app-port 8080 \ + --dapr-app-id rating-api \ + --dapr-app-protocol http \ + --secrets "mongodb-uri=${{ secrets.CAPPS_MONGODB_URI }}" \ + --env-vars "MONGODB_URI=secretref:mongodb-uri" + else + echo "Container app exists, updating..." + + az containerapp update \ + --name rating-api \ + --resource-group ${{ secrets.RESOURCE_GROUP }} \ + --image ${{ env.CONTAINER_IMAGE }} + fi + + deploy-dev-aro: + + runs-on: ubuntu-latest + environment: 'dev-aro' + needs: build + + steps: + - name: OpenShift Login + uses: redhat-actions/oc-login@v1 + with: + openshift_server_url: ${{ secrets.OPENSHIFT_SERVER }} + openshift_token: ${{ secrets.OPENSHIFT_TOKEN }} + namespace: ${{ secrets.OPENSHIFT_NAMESPACE }} + + - name: Create or update ARO App deployment + run: | + #oc get deployment/rating-api + + #if [[ $? != 0 ]]; then + # echo "ARO app not found, creating a new one..." + + # oc import-image rating-api:v1 --from ${{ env.CONTAINER_IMAGE }} --reference-policy=local --confirm + # oc label imagestream/rating-api app=rating-api + # oc new-app --name rating-api --image-stream rating-api:v1 -e MONGODB_URI=${{ secrets.CAPPS_MONGODB_URI }} + #else + echo "ARO app exists, updating..." + oc project ${{ secrets.OPENSHIFT_NAMESPACE }} + oc tag ${{ env.CONTAINER_IMAGE }} rating-api:v1 --reference-policy local + #fi + diff --git a/Dockerfile.k8s b/Dockerfile.k8s new file mode 100644 index 0000000..06c59a3 --- /dev/null +++ b/Dockerfile.k8s @@ -0,0 +1,11 @@ +FROM node:lts-slim + +WORKDIR /opt/app-root +COPY package*.json ./ +RUN npm install + +COPY . . + +EXPOSE 8080 + +CMD [ "npm", "start" ] diff --git a/README.md b/README.md index a2172b5..3db0cb2 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,31 @@ Required configuration via environment variables: Upon startup, the application checks if it can connect to the database. If the database is empty, it populates it with data. +## (Optional) MongoDB install in Kubernetes + +```sh +# See Mongo DB Helm Chart (https://github.com/bitnami/charts/tree/master/bitnami/mongodb) + +helm repo add bitnami https://charts.bitnami.com/bitnami +helm repo update + +helm install mongodb bitnami/mongodb \ + --set persistence.enabled=true \ + --set mongodbUsername=ratingsuser \ + --set mongodbPassword=ratingspassword \ + --set mongodbDatabase=ratingsdb \ + --set mongodbRootPassword=ratingspassword + +MONGODB_URI="mongodb://ratingsuser:ratingspassword@mongodb:27017/ratingsdb" +``` + +## Helm install + +```sh +helm upgrade --install rating-api ./rating-api \ + --set env.database_uri=$MONGODB_URI \ +``` + # Contributing diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..00d0789 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,39 @@ +# Docker +# Build and push an image to Azure Container Registry +# https://docs.microsoft.com/azure/devops/pipelines/languages/docker + +trigger: +- master + +resources: +- repo: self + +variables: + dockerRegistryServiceConnection: 'ratingapp-acr-conn' + imageRepository: 'rating-api' + containerRegistry: '$(RegistryServer)' + dockerfilePath: '$(Build.SourcesDirectory)/Dockerfile' + tag: 'v1.0.$(Build.BuildId)' + + # Agent VM image name + vmImageName: 'ubuntu-latest' + +stages: +- stage: Build + displayName: Build and push stage + jobs: + - job: Build + displayName: Build + pool: + vmImage: $(vmImageName) + steps: + + - task: Docker@2 + displayName: Build and push an image to container registry + inputs: + command: buildAndPush + repository: $(imageRepository) + dockerfile: $(dockerfilePath) + containerRegistry: $(dockerRegistryServiceConnection) + tags: | + $(tag) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..13da6dd --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,26 @@ +# Use root/example as user/password credentials +version: '3.1' + +services: + + mongodb: + image: bitnami/mongodb:5.0 + restart: always + ports: + - 27017:27017 + environment: + MONGODB_USERNAME: ratingsuser + MONGODB_PASSWORD: ratingspassword + MONGODB_DATABASE: ratingsdb + MONGODB_ROOT_USER: root + MONGODB_ROOT_PASSWORD: ratingspassword + + rating-api: + image: rating-api:latest + restart: always + depends_on: + - mongodb + ports: + - 8080:8080 + environment: + MONGODB_URI: mongodb://ratingsuser:ratingspassword@mongodb:27017/ratingsdb diff --git a/rating-api/.helmignore b/rating-api/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/rating-api/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/rating-api/Chart.yaml b/rating-api/Chart.yaml new file mode 100644 index 0000000..1d7a4d7 --- /dev/null +++ b/rating-api/Chart.yaml @@ -0,0 +1,21 @@ +apiVersion: v2 +name: rating-api +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. +appVersion: v1 diff --git a/rating-api/templates/NOTES.txt b/rating-api/templates/NOTES.txt new file mode 100644 index 0000000..4c1b63a --- /dev/null +++ b/rating-api/templates/NOTES.txt @@ -0,0 +1,21 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "rating-api.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "rating-api.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "rating-api.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "rating-api.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80 +{{- end }} diff --git a/rating-api/templates/_helpers.tpl b/rating-api/templates/_helpers.tpl new file mode 100644 index 0000000..ac7fea0 --- /dev/null +++ b/rating-api/templates/_helpers.tpl @@ -0,0 +1,63 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "rating-api.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "rating-api.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "rating-api.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "rating-api.labels" -}} +helm.sh/chart: {{ include "rating-api.chart" . }} +{{ include "rating-api.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Selector labels +*/}} +{{- define "rating-api.selectorLabels" -}} +app.kubernetes.io/name: {{ include "rating-api.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "rating-api.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "rating-api.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} diff --git a/rating-api/templates/deployment.yaml b/rating-api/templates/deployment.yaml new file mode 100644 index 0000000..da9d709 --- /dev/null +++ b/rating-api/templates/deployment.yaml @@ -0,0 +1,58 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "rating-api.fullname" . }} + labels: + {{- include "rating-api.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "rating-api.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "rating-api.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "rating-api.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + - name: MONGODB_URI + value: {{ .Values.env.database_uri }} + ports: + - name: http + containerPort: 8080 + protocol: TCP + livenessProbe: + httpGet: + path: / + port: http + readinessProbe: + httpGet: + path: / + port: http + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/rating-api/templates/hpa.yaml b/rating-api/templates/hpa.yaml new file mode 100644 index 0000000..f17671a --- /dev/null +++ b/rating-api/templates/hpa.yaml @@ -0,0 +1,28 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "rating-api.fullname" . }} + labels: + {{- include "rating-api.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "rating-api.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/rating-api/templates/ingress.yaml b/rating-api/templates/ingress.yaml new file mode 100644 index 0000000..a8f7dda --- /dev/null +++ b/rating-api/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "rating-api.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "rating-api.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/rating-api/templates/service.yaml b/rating-api/templates/service.yaml new file mode 100644 index 0000000..6b60cab --- /dev/null +++ b/rating-api/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "rating-api.fullname" . }} + labels: + {{- include "rating-api.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "rating-api.selectorLabels" . | nindent 4 }} diff --git a/rating-api/templates/serviceaccount.yaml b/rating-api/templates/serviceaccount.yaml new file mode 100644 index 0000000..4e8b6ee --- /dev/null +++ b/rating-api/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "rating-api.serviceAccountName" . }} + labels: + {{- include "rating-api.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end -}} diff --git a/rating-api/templates/tests/test-connection.yaml b/rating-api/templates/tests/test-connection.yaml new file mode 100644 index 0000000..73bb76d --- /dev/null +++ b/rating-api/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "rating-api.fullname" . }}-test-connection" + labels: + {{- include "rating-api.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test-success +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "rating-api.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/rating-api/values.yaml b/rating-api/values.yaml new file mode 100644 index 0000000..891b493 --- /dev/null +++ b/rating-api/values.yaml @@ -0,0 +1,84 @@ +# Default values for rating-api. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: "[registry_server]/rating-api" + pullPolicy: IfNotPresent + + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +env: + database_uri: "mongodb://[username]:[password]@mongodb.[namespace].svc.cluster.local:27017/ratingsdb" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: false + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 8080 + +ingress: + enabled: false + className: "nginx" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: chart-example.local + paths: + - path: /api + pathType: Prefix + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..475ecda --- /dev/null +++ b/test/README.md @@ -0,0 +1,38 @@ +Run ratings-api integration tests +================================= + +Requires powershell or powershell core (pwsh). + +```sh +git clone https://github.com/clarenceb/rating-api +cd rating-api +docker build -t rating-api -f Dockerfile.k8s . +``` + +Run service and local db with docker-compose: + +```sh +docker-compose up -d +# Access the rating-api endpoint at http://localhost:8080/api +``` + +Run the tests with Pester: + +```sh +./run-tests.ps1 + +# Or with powershell: +powershell ./run-tests.ps1 +``` + +Teardown running containers: + +```sh +docker-compose down +``` + +Resources +--------- + +* https://pester.dev/docs/quick-start +* https://github.com/pester/Pester diff --git a/test/ratings-apis.tests.ps1 b/test/ratings-apis.tests.ps1 new file mode 100644 index 0000000..2d2049b --- /dev/null +++ b/test/ratings-apis.tests.ps1 @@ -0,0 +1,17 @@ +Import-Module Pester -PassThru + +Describe 'Get-Rating-Items' { + It 'Returns list of items' { + $result = Invoke-WebRequest -Uri 'http://localhost:8080/api/items' -Method GET + $result.StatusCode | Should -Be 200 + ($result.Content | ConvertFrom-Json).payload.count | Should -BeGreaterThan 0 + } +} + +Describe 'Get-Rating-Sites-Fruit-Smoothies' { + It 'Returns list of items' { + $result = Invoke-WebRequest -Uri 'http://localhost:8080/api/sites/FR' -Method GET + $result.StatusCode | Should -Be 200 + ($result.Content | ConvertFrom-Json).payload.name | Should -Be 'Fruit Smoothies' + } +} diff --git a/test/run-tests.ps1 b/test/run-tests.ps1 new file mode 100644 index 0000000..a37ee6e --- /dev/null +++ b/test/run-tests.ps1 @@ -0,0 +1,11 @@ +#!/bin/pwsh + +if (Get-Module -ListAvailable -Name Pester) { + Write-Host "Module Pester exists" +} +else { + Write-Host "Module Pester does not exist, installing"... + Install-Module -Name Pester -Force +} + +Invoke-Pester -Output Detailed .