diff --git a/.github/workflows/_deploy-loadtest-infra-aws.yaml b/.github/workflows/_deploy-loadtest-infra-aws.yaml new file mode 100644 index 0000000..80e29ee --- /dev/null +++ b/.github/workflows/_deploy-loadtest-infra-aws.yaml @@ -0,0 +1,49 @@ +name: _deploy-loadtest-infra-aws + +on: + workflow_call: + secrets: + AWS_ACCESS_KEY: + required: true + AWS_SECRET_KEY: + required: true + CLUSTER_PREFIX: + required: true + +env: + AWS_REGION: us-east-1 # Add your cluster zone here. + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_KEY }} + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + environment: production + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: "Configure Kubeconfig w/ EKS" + run: aws eks update-kubeconfig --name ${{ secrets.CLUSTER_PREFIX }}-prod --region ${{ env.AWS_REGION }} + + - name: Deploy InfluxDB and Grafana + run: | + helm repo add influxdata https://helm.influxdata.com/ + helm upgrade --install influxdb influxdata/influxdb --namespace monitoring --create-namespace --values ./deploy/influxdb/values.yaml + + helm repo add grafana https://grafana.github.io/helm-charts + helm upgrade --install grafana grafana/grafana --namespace monitoring --values ./deploy/grafana/values.yaml + + - name: Deploy K6 Operator + run: | + helm upgrade k6 \ + --atomic \ + --install \ + ./deploy/k6 \ + --values ./deploy/k6/values.yaml + + - name: Deploy load test scripts + run: | + helm upgrade --install tests ./deploy/tests diff --git a/.github/workflows/_deploy-loadtest-infra-gke.yaml b/.github/workflows/_deploy-loadtest-infra-gke.yaml new file mode 100644 index 0000000..489d416 --- /dev/null +++ b/.github/workflows/_deploy-loadtest-infra-gke.yaml @@ -0,0 +1,71 @@ +name: _deploy-loadtest-infra-gcp + +# Started from GH Docs +# https://docs.github.com/en/actions/deployment/deploying-to-your-cloud-provider/deploying-to-google-kubernetes-engine + +# REQUIRED REPO SECRETS +# - GCP_CREDENTIALS +# - CLUSTER_PREFIX + +on: + workflow_call: + secrets: + GCP_CREDENTIALS: + required: true + CLUSTER_PREFIX: + required: true +env: + GKE_ZONE: us-east1 # Add your cluster zone here. + CLUSTER_PREFIX: ${{ secrets.CLUSTER_PREFIX }} + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + environment: production + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - id: "auth" + uses: "google-github-actions/auth@v0" + with: + credentials_json: ${{ secrets.GCP_CREDENTIALS }} + + - name: "Set up Cloud SDK" + uses: google-github-actions/setup-gcloud@v0 + + - name: "Use gcloud CLI" + run: gcloud info + + # Configure Docker to use the gcloud command-line tool as a credential + # helper for authentication + - run: |- + gcloud --quiet auth configure-docker + + # Get the GKE credentials so we can deploy to the cluster + - uses: google-github-actions/get-gke-credentials@fb08709ba27618c31c09e014e1d8364b02e5042e + with: + cluster_name: ${{ env.CLUSTER_PREFIX }}-prod + location: ${{ env.GKE_ZONE }} + + - name: Deploy InfluxDB and Grafana + run: | + helm repo add influxdata https://helm.influxdata.com/ + helm upgrade --install influxdb influxdata/influxdb --namespace monitoring --create-namespace --values ./deploy/influxdb/values.yaml + + helm repo add grafana https://grafana.github.io/helm-charts + helm upgrade --install grafana grafana/grafana --namespace monitoring --values ./deploy/grafana/values.yaml + + - name: Deploy K6 Operator + run: | + helm upgrade k6 \ + --atomic \ + --install \ + ./deploy/k6 \ + --values ./deploy/k6/values.yaml + + - name: Deploy load test scripts + run: | + helm upgrade --install tests ./deploy/tests diff --git a/.github/workflows/_deploy-otel-collector-aws.yaml b/.github/workflows/_deploy-otel-collector-aws.yaml new file mode 100644 index 0000000..5286646 --- /dev/null +++ b/.github/workflows/_deploy-otel-collector-aws.yaml @@ -0,0 +1,63 @@ +name: _deploy-otel-collector-aws + +# Started from GH Docs +# https://docs.github.com/en/actions/deployment/deploying-to-your-cloud-provider/deploying-to-google-kubernetes-engine + +# REQUIRED REPO SECRETS +# - GCP_CREDENTIALS +# - CLUSTER_PREFIX + +on: + workflow_call: + secrets: + AWS_ACCESS_KEY: + required: true + AWS_SECRET_KEY: + required: true + CLUSTER_PREFIX: + required: true + +env: + AWS_REGION: us-east-1 # Add your cluster zone here. + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_KEY }} + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + environment: production + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: "Configure Kubeconfig w/ EKS" + run: aws eks update-kubeconfig --name ${{ secrets.CLUSTER_PREFIX }}-prod --region ${{ env.AWS_REGION }} + + - name: Deploy Otel + if: ${{ !inputs.dry-run }} + run: | + helm upgrade collector \ + --atomic \ + --install \ + ./deploy/collector \ + --namespace monitoring \ + --create-namespace \ + --set serviceAccount.create=true \ + --values ./deploy/collector/values.yaml + + - name: Helm dependency update + run: | + helm dependency update ./deploy/zipkin + + - name: Deploy Zipkin + if: ${{ !inputs.dry-run }} + run: | + helm upgrade zipkin \ + --atomic \ + --install \ + ./deploy/zipkin \ + --namespace zipkin \ + --create-namespace \ + --values ./deploy/zipkin/values.yaml \ No newline at end of file diff --git a/.github/workflows/_deploy-otel-collector-gke.yaml b/.github/workflows/_deploy-otel-collector-gke.yaml new file mode 100644 index 0000000..83a3be0 --- /dev/null +++ b/.github/workflows/_deploy-otel-collector-gke.yaml @@ -0,0 +1,77 @@ +name: _deploy-otel-collector-gcp + +# Started from GH Docs +# https://docs.github.com/en/actions/deployment/deploying-to-your-cloud-provider/deploying-to-google-kubernetes-engine + +# REQUIRED REPO SECRETS +# - GCP_CREDENTIALS +# - CLUSTER_PREFIX + +on: + workflow_call: + secrets: + GCP_CREDENTIALS: + required: true + CLUSTER_PREFIX: + required: true +env: + GKE_ZONE: us-east1 # Add your cluster zone here. + CLUSTER_PREFIX: ${{ secrets.CLUSTER_PREFIX }} + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + environment: production + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - id: "auth" + uses: "google-github-actions/auth@v0" + with: + credentials_json: ${{ secrets.GCP_CREDENTIALS }} + + - name: "Set up Cloud SDK" + uses: google-github-actions/setup-gcloud@v0 + + - name: "Use gcloud CLI" + run: gcloud info + + # Configure Docker to use the gcloud command-line tool as a credential + # helper for authentication + - run: |- + gcloud --quiet auth configure-docker + + # Get the GKE credentials so we can deploy to the cluster + - uses: google-github-actions/get-gke-credentials@fb08709ba27618c31c09e014e1d8364b02e5042e + with: + cluster_name: ${{ env.CLUSTER_PREFIX }}-prod + location: ${{ env.GKE_ZONE }} + + - name: Helm dependency update + run: | + helm dependency update ./deploy/zipkin + + - name: Deploy Otel + if: ${{ !inputs.dry-run }} + run: | + helm upgrade collector \ + --atomic \ + --install \ + ./deploy/collector \ + --namespace monitoring \ + --create-namespace \ + --values ./deploy/collector/values.yaml + + - name: Deploy Zipkin + if: ${{ !inputs.dry-run }} + run: | + helm upgrade zipkin \ + --atomic \ + --install \ + ./deploy/zipkin \ + --namespace zipkin \ + --create-namespace \ + --values ./deploy/zipkin/values.yaml \ No newline at end of file diff --git a/.github/workflows/_deploy-router-aws.yaml b/.github/workflows/_deploy-router-aws.yaml new file mode 100644 index 0000000..23a04fc --- /dev/null +++ b/.github/workflows/_deploy-router-aws.yaml @@ -0,0 +1,116 @@ +name: _deploy-router-aws + +on: + workflow_call: + inputs: + environment: + description: "Target variant" + type: string + required: true + default: dev + variant: + description: "Target variant" + type: string + required: true + default: dev + dry-run: + type: boolean + description: "Run a dry run with helm" + required: false + default: false + debug: + type: boolean + description: "Run helm in debug mode" + required: false + default: false + secrets: + AWS_ACCESS_KEY: + required: true + AWS_SECRET_KEY: + required: true + CLUSTER_PREFIX: + required: true + APOLLO_GRAPH_ID: + required: true + APOLLO_KEY: + required: true +env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_KEY }} + AWS_REGION: us-east-1 + CLUSTER_PREFIX: ${{ secrets.CLUSTER_PREFIX }} + APOLLO_GRAPH_ID: ${{ secrets.APOLLO_GRAPH_ID }} + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: "Configure Kubeconfig w/ EKS" + run: aws eks update-kubeconfig --name ${{ secrets.CLUSTER_PREFIX }}-${{ inputs.environment }} --region ${{ env.AWS_REGION }} + + - name: Helm dependency update + run: | + helm dependency update ./deploy/router + + # Deploy the Docker image to the GKE cluster with dry run + - name: Helm dry-run + if: ${{ inputs.dry-run }} + run: | + helm upgrade router \ + --create-namespace \ + --namespace router \ + --set router.managedFederation.graphRef=$APOLLO_GRAPH_ID@${{ inputs.variant }} \ + --set router.managedFederation.apiKey=${{ secrets.APOLLO_KEY }} \ + --set ingress.annotations."kubernetes\.io/ingress\.class"=alb \ + --set ingress.annotations."alb\.ingress\.kubernetes\.io/target-type"=ip \ + --set ingress.annotations."alb\.ingress\.kubernetes\.io/scheme"=internet-facing \ + --set ingress.gcp=false \ + --dry-run \ + --atomic \ + --install \ + ./deploy/router \ + --values ./deploy/router/values.yaml \ + -f ./deploy/router/environments/${{ inputs.environment }}.yaml + + # Deploy the Docker image to the GKE cluster for real with debug + - name: Deploy + if: ${{ !inputs.dry-run && inputs.debug }} + run: | + helm upgrade router \ + --create-namespace \ + --namespace router \ + --set router.managedFederation.graphRef=$APOLLO_GRAPH_ID@${{ inputs.variant }} \ + --set router.managedFederation.apiKey=${{ secrets.APOLLO_KEY }} \ + --set ingress.annotations."kubernetes\.io/ingress\.class"=alb \ + --set ingress.annotations."alb\.ingress\.kubernetes\.io/target-type"=ip \ + --set ingress.annotations."alb\.ingress\.kubernetes\.io/scheme"=internet-facing \ + --set ingress.gcp=false \ + --atomic \ + --install \ + --debug \ + ./deploy/router \ + --values ./deploy/router/values.yaml \ + -f ./deploy/router/environments/${{ inputs.environment }}.yaml + + # Deploy the Docker image to the GKE cluster for real + - name: Deploy + if: ${{ !inputs.dry-run }} + run: | + helm upgrade router \ + --create-namespace \ + --namespace router \ + --set router.managedFederation.graphRef=$APOLLO_GRAPH_ID@${{ inputs.variant }} \ + --set router.managedFederation.apiKey=${{ secrets.APOLLO_KEY }} \ + --set ingress.annotations."kubernetes\.io/ingress\.class"=alb \ + --set ingress.annotations."alb\.ingress\.kubernetes\.io/target-type"=ip \ + --set ingress.annotations."alb\.ingress\.kubernetes\.io/scheme"=internet-facing \ + --set ingress.gcp=false \ + --atomic \ + --install \ + ./deploy/router \ + --values ./deploy/router/values.yaml \ + -f ./deploy/router/environments/${{ inputs.environment }}.yaml diff --git a/.github/workflows/_deploy-router-gke.yaml b/.github/workflows/_deploy-router-gke.yaml new file mode 100644 index 0000000..68e6493 --- /dev/null +++ b/.github/workflows/_deploy-router-gke.yaml @@ -0,0 +1,129 @@ +name: _deploy-router-gcp + +# Started from GH Docs +# https://docs.github.com/en/actions/deployment/deploying-to-your-cloud-provider/deploying-to-google-kubernetes-engine + +# REQUIRED REPO SECRETS +# - GCP_CREDENTIALS +# - CLUSTER_PREFIX +# - APOLLO_GRAPH_ID +# - APOLLO_KEY + +on: + workflow_call: + inputs: + environment: + description: "Target variant" + type: string + required: true + default: dev + variant: + description: "Target variant" + type: string + required: true + default: dev + dry-run: + type: boolean + description: "Run a dry run with helm" + required: false + default: false + debug: + type: boolean + description: "Run helm in debug mode" + required: false + default: false + secrets: + GCP_CREDENTIALS: + required: true + CLUSTER_PREFIX: + required: true + APOLLO_GRAPH_ID: + required: true + APOLLO_KEY: + required: true + +env: + GKE_ZONE: us-east1 # Add your cluster zone here. + CLUSTER_PREFIX: ${{ secrets.CLUSTER_PREFIX }} + APOLLO_GRAPH_ID: ${{ secrets.APOLLO_GRAPH_ID }} + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - id: "auth" + uses: "google-github-actions/auth@v0" + with: + credentials_json: ${{ secrets.GCP_CREDENTIALS }} + + - name: "Set up Cloud SDK" + uses: google-github-actions/setup-gcloud@v0 + + - name: "Use gcloud CLI" + run: gcloud info + + # Configure Docker to use the gcloud command-line tool as a credential + # helper for authentication + - run: |- + gcloud --quiet auth configure-docker + + # Get the GKE credentials so we can deploy to the cluster + - uses: google-github-actions/get-gke-credentials@fb08709ba27618c31c09e014e1d8364b02e5042e + with: + cluster_name: ${{ env.CLUSTER_PREFIX }}-${{ inputs.environment }} + location: ${{ env.GKE_ZONE }} + + - name: Helm dependency update + run: | + helm dependency update ./deploy/router + + # Deploy the Docker image to the GKE cluster with dry run + - name: Helm dry-run + if: ${{ inputs.dry-run }} + run: | + helm upgrade router \ + --create-namespace \ + --namespace router \ + --set router.managedFederation.graphRef=$APOLLO_GRAPH_ID@${{ inputs.variant }} \ + --set router.managedFederation.apiKey=${{ secrets.APOLLO_KEY }} \ + --dry-run \ + --atomic \ + --install \ + ./deploy/router \ + --values ./deploy/router/values.yaml \ + -f ./deploy/router/environments/${{ inputs.environment }}.yaml + + # Deploy the Docker image to the GKE cluster for real with debug + - name: Deploy + if: ${{ !inputs.dry-run && inputs.debug }} + run: | + helm upgrade router \ + --create-namespace \ + --namespace router \ + --set router.managedFederation.graphRef=$APOLLO_GRAPH_ID@${{ inputs.variant }} \ + --set router.managedFederation.apiKey=${{ secrets.APOLLO_KEY }} \ + --atomic \ + --install \ + --debug \ + ./deploy/router \ + --values ./deploy/router/values.yaml \ + -f ./deploy/router/environments/${{ inputs.environment }}.yaml + + # Deploy the Docker image to the GKE cluster for real + - name: Deploy + if: ${{ !inputs.dry-run }} + run: | + helm upgrade router \ + --create-namespace \ + --namespace router \ + --set router.managedFederation.graphRef=$APOLLO_GRAPH_ID@${{ inputs.variant }} \ + --set router.managedFederation.apiKey=${{ secrets.APOLLO_KEY }} \ + --atomic \ + --install \ + ./deploy/router \ + --values ./deploy/router/values.yaml \ + -f ./deploy/router/environments/${{ inputs.environment }}.yaml diff --git a/.github/workflows/_determine-provider.yaml b/.github/workflows/_determine-provider.yaml new file mode 100644 index 0000000..de29f2f --- /dev/null +++ b/.github/workflows/_determine-provider.yaml @@ -0,0 +1,29 @@ +name: _determine-provider + +on: + workflow_call: + secrets: + AWS_ACCESS_KEY: + required: false + GCP_CREDENTIALS: + required: false + outputs: + gcp: + value: ${{ jobs.provider.outputs.gcp}} + aws: + value: ${{ jobs.provider.outputs.aws}} + +jobs: + provider: + env: + GCP: ${{ secrets.GCP_CREDENTIALS }} + AWS: ${{ secrets.AWS_ACCESS_KEY }} + runs-on: ubuntu-latest + outputs: + gcp: ${{ steps.gcp.outputs.gcp}} + aws: ${{ steps.aws.outputs.aws}} + steps: + - id: gcp + run: if [ -n "$GCP" ]; then echo "gcp=true" >> $GITHUB_OUTPUT ; else echo "gcp=false" >> $GITHUB_OUTPUT ; fi + - id: aws + run: if [ -n "$AWS" ]; then echo "aws=true" >> $GITHUB_OUTPUT ; else echo "aws=false" >> $GITHUB_OUTPUT ; fi diff --git a/.github/workflows/_run-loadtest-aws.yaml b/.github/workflows/_run-loadtest-aws.yaml new file mode 100644 index 0000000..23e3238 --- /dev/null +++ b/.github/workflows/_run-loadtest-aws.yaml @@ -0,0 +1,59 @@ +name: _run-loadtest-aws + +on: + workflow_call: + inputs: + test: + description: Test to Run + type: string + required: true + parallelism: + description: Number of workers + type: string + default: "1" + required: true + secrets: + AWS_ACCESS_KEY: + required: true + AWS_SECRET_KEY: + required: true + CLUSTER_PREFIX: + required: true +env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_KEY }} + AWS_REGION: us-east-1 + CLUSTER_PREFIX: ${{ secrets.CLUSTER_PREFIX }} + +jobs: + deploy: + name: test + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: "Configure Kubeconfig w/ EKS" + run: aws eks update-kubeconfig --name ${{ secrets.CLUSTER_PREFIX }}-prod --region ${{ env.AWS_REGION }} + + - name: "Apply K6" + env: + RUN_NAME: run-${{ inputs.test }}-${{ github.run_id }} + run: | + cat <=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 "collector.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/infra/collector/templates/service.yaml b/infra/collector/templates/service.yaml new file mode 100644 index 0000000..b519326 --- /dev/null +++ b/infra/collector/templates/service.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "collector.fullname" . }} + labels: {{- include "collector.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - targetPort: metrics-grpc + port: 4317 + protocol: TCP + name: metrics-grpc + - targetPort: metrics-http + port: 4318 + protocol: TCP + name: metrics-http + - targetPort: metrics-prom + port: 8888 + protocol: TCP + name: metrics-prom + selector: {{- include "collector.selectorLabels" . | nindent 4 }} diff --git a/infra/collector/templates/serviceaccount.yaml b/infra/collector/templates/serviceaccount.yaml new file mode 100644 index 0000000..c532270 --- /dev/null +++ b/infra/collector/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "collector.serviceAccountName" . }} + labels: + {{- include "collector.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/infra/collector/templates/tests/test-connection.yaml b/infra/collector/templates/tests/test-connection.yaml new file mode 100644 index 0000000..d84bdcd --- /dev/null +++ b/infra/collector/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "collector.fullname" . }}-test-connection" + labels: + {{- include "collector.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "collector.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/infra/collector/values.yaml b/infra/collector/values.yaml new file mode 100644 index 0000000..f9a3aef --- /dev/null +++ b/infra/collector/values.yaml @@ -0,0 +1,61 @@ +# Declare variables to be passed into your templates. +replicaCount: 1 + +image: + repository: otel/opentelemetry-collector-contrib + pullPolicy: IfNotPresent + tag: "0.59.0" + +imagePullSecrets: [] + +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: "metrics-writer" + +podAnnotations: {} + +podSecurityContext: {} + +securityContext: {} + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + className: "" + annotations: {} + hosts: + - host: collector.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + +resources: + # TODO: Update resource limits + 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/infra/grafana/values.yaml b/infra/grafana/values.yaml new file mode 100644 index 0000000..9e59ab2 --- /dev/null +++ b/infra/grafana/values.yaml @@ -0,0 +1,28 @@ +datasources: + datasources.yaml: + apiVersion: 1 + datasources: + - name: InfluxDB + type: influxdb + access: proxy + url: http://influxdb.monitoring:8086 + database: db + isDefault: true + version: 1 + editable: false +dashboardProviders: + dashboardproviders.yaml: + apiVersion: 1 + providers: + - name: default # this has to match the key in `dashboards` + orgId: 1 + type: file + disableDeletion: false + editable: true + options: + path: /var/lib/grafana/dashboards/default +dashboards: + default: # this has to match the name of a provider + k6-results: + url: https://grafana.com/api/dashboards/2587/revisions/3/download + datasource: InfluxDB diff --git a/infra/influxdb/values.yaml b/infra/influxdb/values.yaml new file mode 100644 index 0000000..6fd7c80 --- /dev/null +++ b/infra/influxdb/values.yaml @@ -0,0 +1,2 @@ +persistence: + enabled: false \ No newline at end of file diff --git a/infra/k6/Chart.yaml b/infra/k6/Chart.yaml new file mode 100644 index 0000000..63c564d --- /dev/null +++ b/infra/k6/Chart.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +appVersion: "0.0.6" +description: A Helm chart to install the k6 operator +name: k6-operator +version: 0.0.1 +kubeVersion: ">=1.16.0-0" +home: https://k6.io +sources: + - https://github.com/grafana/k6-operator +keywords: + - load-testing + - smoke-testing + - stress-testing + - soak-testing + - kubernetes +maintainers: + - name: yorugac + email: yorugac@gmail.com +icon: https://raw.githubusercontent.com/grafana/k6-docs/main/static/images/icon.png diff --git a/infra/k6/README.md b/infra/k6/README.md new file mode 100644 index 0000000..cb46533 --- /dev/null +++ b/infra/k6/README.md @@ -0,0 +1,53 @@ +# k6-operator + +![Version: 0.0.1](https://img.shields.io/badge/Version-0.0.1-informational?style=flat-square) ![AppVersion: 0.0.6](https://img.shields.io/badge/AppVersion-0.0.6-informational?style=flat-square) + +A Helm chart to install the k6 operator + +**Homepage:** + +## Maintainers + +| Name | Email | Url | +| ---- | ------ | --- | +| yorugac | yorugac@gmail.com | | + +## Source Code + +* + +## Requirements + +Kubernetes: `>=1.16.0-0` + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| authProxy.enabled | bool | `true` | enables the protection of /metrics endpoint. (https://github.com/brancz/kube-rbac-proxy) | +| authProxy.image.name | string | `"gcr.io/kubebuilder/kube-rbac-proxy"` | rbac-proxy image name | +| authProxy.image.pullPolicy | string | `"IfNotPresent"` | pull policy for the image can be Always, Never, IfNotPresent (default: IfNotPresent) | +| authProxy.image.tag | string | `"v0.5.0"` | rbac-proxy image tag | +| authProxy.livenessProbe | object | `{}` | Liveness probe in Probe format | +| authProxy.readinessProbe | object | `{}` | Readiness probe in Probe format | +| authProxy.resources | object | `{}` | rbac-proxy resource limitation/request | +| controlPlane | string | `"controller-manager"` | | +| customAnnotations | object | `{}` | Custom Annotations to be applied on all resources | +| customLabels | object | `{}` | Custom Label to be applied on all resources | +| manager.env | object | `{}` | Environment variable to be passet to the controller | +| manager.image.name | string | `"ghcr.io/grafana/operator"` | controller-manager image name | +| manager.image.pullPolicy | string | `"Always"` | pull policy for the image possible values Always, Never, IfNotPresent (default: Always) | +| manager.image.tag | string | `"latest"` | controller-manager image tag | +| manager.livenessProbe | object | `{}` | Liveness probe in Probe format | +| manager.readinessProbe | object | `{}` | Readiness probe in Probe format | +| manager.resources | object | `{"limits":{"cpu":"100m","memory":"100Mi"},"requests":{"cpu":"100m","memory":"50Mi"}}` | controller-manager Resources definition | +| manager.resources.limits.cpu | string | `"100m"` | controller-manager CPU limit (Max) | +| manager.resources.limits.memory | string | `"100Mi"` | controller-manager Memory limit (Max) | +| manager.resources.requests.cpu | string | `"100m"` | controller-manager CPU request (Min) | +| manager.resources.requests.memory | string | `"50Mi"` | controller-manager Memory request (Min) | +| manager.serviceAccount | string | `"k6-operator-controller"` | kubernetes service account for the manager | +| namespace.create | bool | `true` | create the namespace (default: true) | +| prometheus.enabled | bool | `false` | enables the prometheus metrics scraping (default: false) | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.5.0](https://github.com/norwoodj/helm-docs/releases/v1.5.0) \ No newline at end of file diff --git a/infra/k6/templates/NOTES.txt b/infra/k6/templates/NOTES.txt new file mode 100644 index 0000000..6f54f18 --- /dev/null +++ b/infra/k6/templates/NOTES.txt @@ -0,0 +1,8 @@ +Thank you for installing {{ .Chart.Name }}. + +Your release is named {{ .Release.Name }}. + +To learn more about the release, try: + + $ helm status {{ .Release.Name }} + $ helm get all {{ .Release.Name }} diff --git a/infra/k6/templates/_helpers.tpl b/infra/k6/templates/_helpers.tpl new file mode 100644 index 0000000..21594aa --- /dev/null +++ b/infra/k6/templates/_helpers.tpl @@ -0,0 +1,40 @@ +{{- define "operator.labels" -}} + {{- if .Values.customLabels }} + {{- with .Values.customLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- else}} + {{ print "{}" }} + {{- end }} +{{- end -}} + +{{- define "operator.annotations" -}} + {{- if .Values.customAnnotations }} + {{- with .Values.customAnnotations }} + {{- toYaml . }} + {{- end }} + {{- end }} +{{- end -}} + +{{- define "operator.namespace" -}} + {{- if eq .Release.Namespace "default" }} + {{- printf "%v-system" .Release.Name | indent 1 }} + {{- else }} + {{- .Release.Namespace | indent 1 }} + {{- end }} +{{- end -}} + + +{{- define "operator.livenessProbe" -}} + {{- if .Values.authProxy.livenessProbe }} + livenessProbe: + {{- toYaml .Values.authProxy.livenessProbe | nindent 12 }} + {{- end }} +{{- end -}} + +{{- define "operator.readinessProbe" -}} + {{- if .Values.authProxy.readinessProbe }} + readinessProbe: + {{- toYaml .Values.authProxy.readinessProbe | nindent 12 }} + {{- end }} +{{- end -}} diff --git a/infra/k6/templates/clusterRole.yaml b/infra/k6/templates/clusterRole.yaml new file mode 100644 index 0000000..72c96be --- /dev/null +++ b/infra/k6/templates/clusterRole.yaml @@ -0,0 +1,122 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: {{ .Release.Name }}-manager-role + labels: + {{- include "operator.labels" . }} + annotations: + {{- include "operator.annotations" . | default "{}" | nindent 4 }} +rules: +- apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - pods + - pods/log + verbs: + - get + - list + - watch +- apiGroups: + - k6.io + resources: + - k6s + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - k6.io + resources: + - k6s/status + - k6s/finalizers + verbs: + - get + - patch + - update +- apiGroups: + - "" + resources: + - secrets + verbs: + - list + - get + - watch +{{- if .Values.authProxy.enabled }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ .Release.Name }}-metrics-reader + labels: + {{- include "operator.labels" . }} + annotations: + {{- include "operator.annotations" . | default "" | nindent 4 }} +rules: +- nonResourceURLs: + - /metrics + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ .Release.Name }}-proxy-role + labels: + {{- include "operator.labels" . }} + annotations: + {{- include "operator.annotations" . | default "" | nindent 4 }} +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +{{- end }} diff --git a/infra/k6/templates/clusterRoleBinding.yaml b/infra/k6/templates/clusterRoleBinding.yaml new file mode 100644 index 0000000..350d166 --- /dev/null +++ b/infra/k6/templates/clusterRoleBinding.yaml @@ -0,0 +1,35 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ .Release.Name }}-manager-rolebinding + labels: + {{- include "operator.labels" . }} + annotations: + {{- include "operator.annotations" . | default "" | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ .Release.Name }}-manager-role +subjects: + - kind: ServiceAccount + name: {{ .Values.manager.serviceAccount }} + namespace: {{- include "operator.namespace" . -}} +{{- if .Values.authProxy.enabled }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ .Release.Name }}-proxy-rolebinding + labels: + {{- include "operator.labels" . }} + annotations: + {{- include "operator.annotations" . | default "" | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ .Release.Name }}-proxy-role +subjects: + - kind: ServiceAccount + name: {{ .Values.manager.serviceAccount }} + namespace: {{- include "operator.namespace" . -}} +{{- end }} diff --git a/infra/k6/templates/crds/k6.yaml b/infra/k6/templates/crds/k6.yaml new file mode 100644 index 0000000..608d0a6 --- /dev/null +++ b/infra/k6/templates/crds/k6.yaml @@ -0,0 +1,1977 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + labels: + {{- include "operator.labels" . }} + annotations: + {{- include "operator.annotations" . | nindent 4 }} + controller-gen.kubebuilder.io/version: v0.3.0 + creationTimestamp: null + name: k6s.k6.io +spec: + group: k6.io + names: + kind: K6 + listKind: K6List + plural: k6s + singular: k6 + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: K6 is the Schema for the k6s API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: K6Spec defines the desired state of K6 + properties: + arguments: + type: string + cleanup: + description: Cleanup allows for automatic cleanup of resources post + execution + enum: + - post + type: string + parallelism: + format: int32 + type: integer + paused: + type: string + ports: + items: + description: ContainerPort represents a network port in a single + container. + properties: + containerPort: + description: Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: Number of port to expose on the host. If specified, + this must be a valid port number, 0 < x < 65536. If HostNetwork + is specified, this must match ContainerPort. Most containers + do not need this. + format: int32 + type: integer + name: + description: If specified, this must be an IANA_SVC_NAME and + unique within the pod. Each named port in a pod must have + a unique name. Name for the port that can be referred to by + services. + type: string + protocol: + description: Protocol for port. Must be UDP, TCP, or SCTP. Defaults + to "TCP". + type: string + required: + - containerPort + type: object + type: array + quiet: + type: string + runner: + properties: + affinity: + description: Affinity is a group of affinity scheduling rules. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for + the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. The node that is most + preferred is the one with the greatest sum of weights, + i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node matches the corresponding matchExpressions; + the node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects + (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with + the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the + corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the affinity requirements + specified by this field cease to be met at some point + during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from + its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term + matches no objects. The requirements of them are + ANDed. The TopologySelectorTerm type implements + a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. The node that is most + preferred is the one with the greatest sum of weights, + i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum are + the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the affinity requirements + specified by this field cease to be met at some point + during pod execution (e.g. due to a pod label update), + the system may or may not try to eventually evict the + pod from its node. When there are multiple elements, + the lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-located + is defined as running on a node whose value of the + label with key matches that of any node + on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, etc. + as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the anti-affinity expressions + specified by this field, but it may choose a node that + violates one or more of the expressions. The node that + is most preferred is the one with the greatest sum of + weights, i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + anti-affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum are + the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified + by this field are not met at scheduling time, the pod + will not be scheduled onto the node. If the anti-affinity + requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod + label update), the system may or may not try to eventually + evict the pod from its node. When there are multiple + elements, the lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-located + is defined as running on a node whose value of the + label with key matches that of any node + on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + automountServiceAccountToken: + type: string + env: + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded + using the previous defined environment variables in the + container and any service environment variables. If a + variable cannot be resolved, the reference in the input + string will be unchanged. The $(VAR_NAME) syntax can be + escaped with a double $$, ie: $$(VAR_NAME). Escaped references + will never be expanded, regardless of whether the variable + exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, metadata.labels, metadata.annotations, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + image: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + nodeselector: + additionalProperties: + type: string + type: object + resources: + description: ResourceRequirements describes the compute resource + requirements. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + securityContext: + description: PodSecurityContext holds pod-level security attributes + and common container settings. Some fields are also present + in container.securityContext. Field values of container.securityContext + take precedence over field values of PodSecurityContext. + properties: + fsGroup: + description: "A special supplemental group that applies to + all containers in a pod. Some volume types allow the Kubelet + to change the ownership of that volume to be owned by the + pod: \n 1. The owning GID will be the FSGroup 2. The setgid + bit is set (new files created in the volume will be owned + by FSGroup) 3. The permission bits are OR'd with rw-rw---- + \n If unset, the Kubelet will not modify the ownership and + permissions of any volume." + format: int64 + type: integer + fsGroupChangePolicy: + description: 'fsGroupChangePolicy defines behavior of changing + ownership and permission of the volume before being exposed + inside Pod. This field will only apply to volume types which + support fsGroup based ownership(and permissions). It will + have no effect on ephemeral volume types such as: secret, + configmaps and emptydir. Valid values are "OnRootMismatch" + and "Always". If not specified defaults to "Always".' + type: string + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in SecurityContext. If set + in both SecurityContext and PodSecurityContext, the value + specified in SecurityContext takes precedence for that container. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + SecurityContext. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence + for that container. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + supplementalGroups: + description: A list of groups applied to the first process + run in each container, in addition to the container's primary + GID. If unspecified, no groups will be added to any container. + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced sysctls used + for the pod. Pods with unsupported sysctls (by the container + runtime) might fail to launch. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options within a container's + SecurityContext will be used. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + serviceAccountName: + type: string + type: object + script: + description: K6Script describes where the script to execute the tests + is found + properties: + configMap: + description: K6Configmap describes the config map script location + properties: + file: + type: string + name: + type: string + required: + - name + type: object + localFile: + type: string + volumeClaim: + description: K6VolumeClaim describes the volume claim script location + properties: + file: + type: string + name: + type: string + required: + - name + type: object + type: object + scuttle: + properties: + enabled: + type: string + envoyAdminApi: + type: string + genericQuitEndpoint: + type: string + istioQuitApi: + type: string + neverKillIstio: + type: boolean + neverKillIstioOnFailure: + type: boolean + quitWithoutEnvoyTimeout: + type: string + scuttleLogging: + type: boolean + startWithoutEnvoy: + type: boolean + waitForEnvoyTimeout: + type: string + type: object + separate: + type: boolean + starter: + properties: + affinity: + description: Affinity is a group of affinity scheduling rules. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for + the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. The node that is most + preferred is the one with the greatest sum of weights, + i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node matches the corresponding matchExpressions; + the node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects + (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with + the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the + corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the affinity requirements + specified by this field cease to be met at some point + during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from + its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term + matches no objects. The requirements of them are + ANDed. The TopologySelectorTerm type implements + a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. The node that is most + preferred is the one with the greatest sum of weights, + i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum are + the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the affinity requirements + specified by this field cease to be met at some point + during pod execution (e.g. due to a pod label update), + the system may or may not try to eventually evict the + pod from its node. When there are multiple elements, + the lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-located + is defined as running on a node whose value of the + label with key matches that of any node + on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, etc. + as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the anti-affinity expressions + specified by this field, but it may choose a node that + violates one or more of the expressions. The node that + is most preferred is the one with the greatest sum of + weights, i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + anti-affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum are + the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified + by this field are not met at scheduling time, the pod + will not be scheduled onto the node. If the anti-affinity + requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod + label update), the system may or may not try to eventually + evict the pod from its node. When there are multiple + elements, the lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-located + is defined as running on a node whose value of the + label with key matches that of any node + on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + automountServiceAccountToken: + type: string + env: + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded + using the previous defined environment variables in the + container and any service environment variables. If a + variable cannot be resolved, the reference in the input + string will be unchanged. The $(VAR_NAME) syntax can be + escaped with a double $$, ie: $$(VAR_NAME). Escaped references + will never be expanded, regardless of whether the variable + exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, metadata.labels, metadata.annotations, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + image: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + nodeselector: + additionalProperties: + type: string + type: object + resources: + description: ResourceRequirements describes the compute resource + requirements. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + securityContext: + description: PodSecurityContext holds pod-level security attributes + and common container settings. Some fields are also present + in container.securityContext. Field values of container.securityContext + take precedence over field values of PodSecurityContext. + properties: + fsGroup: + description: "A special supplemental group that applies to + all containers in a pod. Some volume types allow the Kubelet + to change the ownership of that volume to be owned by the + pod: \n 1. The owning GID will be the FSGroup 2. The setgid + bit is set (new files created in the volume will be owned + by FSGroup) 3. The permission bits are OR'd with rw-rw---- + \n If unset, the Kubelet will not modify the ownership and + permissions of any volume." + format: int64 + type: integer + fsGroupChangePolicy: + description: 'fsGroupChangePolicy defines behavior of changing + ownership and permission of the volume before being exposed + inside Pod. This field will only apply to volume types which + support fsGroup based ownership(and permissions). It will + have no effect on ephemeral volume types such as: secret, + configmaps and emptydir. Valid values are "OnRootMismatch" + and "Always". If not specified defaults to "Always".' + type: string + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in SecurityContext. If set + in both SecurityContext and PodSecurityContext, the value + specified in SecurityContext takes precedence for that container. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + SecurityContext. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence + for that container. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + supplementalGroups: + description: A list of groups applied to the first process + run in each container, in addition to the container's primary + GID. If unspecified, no groups will be added to any container. + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced sysctls used + for the pod. Pods with unsupported sysctls (by the container + runtime) might fail to launch. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options within a container's + SecurityContext will be used. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + serviceAccountName: + type: string + type: object + required: + - parallelism + - script + type: object + status: + description: K6Status defines the observed state of K6 + properties: + stage: + description: Stage describes which stage of the test execution lifecycle + our runners are in + enum: + - initialization + - initialized + - created + - started + - finished + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/infra/k6/templates/deployment.yaml b/infra/k6/templates/deployment.yaml new file mode 100644 index 0000000..ff1563c --- /dev/null +++ b/infra/k6/templates/deployment.yaml @@ -0,0 +1,73 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-controller-manager + namespace: {{- include "operator.namespace" . }} + labels: + control-plane: {{ .Values.controlPlane }} + {{- with .Values.customLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + annotations: + {{- include "operator.annotations" . | default "" | nindent 4 }} +spec: + replicas: 1 + selector: + matchLabels: + control-plane: {{ .Values.controlPlane }} + template: + metadata: + labels: + control-plane: {{ .Values.controlPlane }} + spec: + containers: + {{- if .Values.authProxy.enabled }} + - name: kube-rbac-proxy + image: "{{ .Values.authProxy.image.name }}:{{ .Values.authProxy.image.tag }}" + imagePullPolicy: {{ .Values.authProxy.image.pullPolicy }} + {{- if .Values.authProxy.resources }} + resources: + {{- toYaml .Values.authProxy.resources | nindent 12 }} + {{- end }} + {{- if .Values.authProxy.livenessProbe }} + livenessProbe: + {{- toYaml .Values.authProxy.livenessProbe | nindent 12 }} + {{- end }} + {{- if .Values.authProxy.readinessProbe }} + readinessProbe: + {{- toYaml .Values.authProxy.readinessProbe | nindent 12 }} + {{- end }} + args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=10 + {{- include "operator.readinessProbe" . }} + ports: + - containerPort: 8443 + name: https + {{- end }} + - name: manager + image: "{{ .Values.manager.image.name }}:{{ .Values.manager.image.tag }}" + imagePullPolicy: {{ .Values.manager.image.pullPolicy }} + + # removed live and ready checks because we're using a docker image + # built before the helm PR (https://github.com/grafana/k6-operator/pull/98) landed + + resources: + {{- toYaml .Values.manager.resources | nindent 12 }} + {{- if .Values.manager.env }} + env: + {{- with .Values.manager.env }} + {{- toYaml . | nindent 10 }} + {{- end }} + {{- end }} + command: + - /manager + args: + - --enable-leader-election + {{- if .Values.authProxy.enabled }} + - --metrics-addr=127.0.0.1:8080 + {{- end }} + serviceAccount: {{ .Values.manager.serviceAccount }} + terminationGracePeriodSeconds: 10 diff --git a/infra/k6/templates/namespace.yaml b/infra/k6/templates/namespace.yaml new file mode 100644 index 0000000..abc3bec --- /dev/null +++ b/infra/k6/templates/namespace.yaml @@ -0,0 +1,14 @@ +{{- if .Values.namespace.create }} +apiVersion: v1 +kind: Namespace +metadata: + name: {{ .Release.Name }}-system + labels: + app.kubernetes.io/name: {{ .Release.Name }} + control-plane: {{ .Values.controlPlane }} + {{- with .Values.customLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + annotations: + {{- include "operator.annotations" . | default "{}" | nindent 4 }} +{{- end }} diff --git a/infra/k6/templates/prometheus/serviceMonitor.yaml b/infra/k6/templates/prometheus/serviceMonitor.yaml new file mode 100644 index 0000000..f8bdd2d --- /dev/null +++ b/infra/k6/templates/prometheus/serviceMonitor.yaml @@ -0,0 +1,22 @@ +{{- if .Values.prometheus.enabled }} +# Prometheus Monitor Service (Metrics) +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + control-plane: {{ .Values.controlPlane }} + {{- with .Values.customLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + annotations: + {{- include "operator.annotations" . | default "{}" | nindent 4 }} + name: controller-manager-metrics-monitor + namespace: system +spec: + endpoints: + - path: /metrics + port: https + selector: + matchLabels: + control-plane: {{ .Values.controlPlane }} +{{- end }} diff --git a/infra/k6/templates/role.yaml b/infra/k6/templates/role.yaml new file mode 100644 index 0000000..a2fd0c8 --- /dev/null +++ b/infra/k6/templates/role.yaml @@ -0,0 +1,29 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ .Release.Name }}-leader-election-role + namespace: {{- include "operator.namespace" . }} + labels: + {{- include "operator.labels" . }} + annotations: + {{- include "operator.annotations" . | default "" | nindent 4 }} +rules: + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch diff --git a/infra/k6/templates/roleBinding.yaml b/infra/k6/templates/roleBinding.yaml new file mode 100644 index 0000000..5edf536 --- /dev/null +++ b/infra/k6/templates/roleBinding.yaml @@ -0,0 +1,17 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ .Release.Name }}-leader-election-rolebinding + namespace: {{- include "operator.namespace" . }} + labels: + {{- include "operator.labels" . }} + annotations: + {{- include "operator.annotations" . | default "" | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ .Release.Name }}-leader-election-role +subjects: +- kind: ServiceAccount + name: {{ .Values.manager.serviceAccount }} + namespace: {{- include "operator.namespace" . }} diff --git a/infra/k6/templates/service.yaml b/infra/k6/templates/service.yaml new file mode 100644 index 0000000..cd6aa54 --- /dev/null +++ b/infra/k6/templates/service.yaml @@ -0,0 +1,21 @@ +{{- if .Values.authProxy.enabled }} +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: {{ .Values.controlPlane }} + {{- with .Values.customLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + annotations: + {{- include "operator.annotations" . | default "{}" | nindent 4 }} + name: {{ .Release.Name }}-controller-manager-metrics-service + namespace: {{- include "operator.namespace" . }} +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: {{ .Values.controlPlane }} +{{- end }} diff --git a/infra/k6/templates/serviceAccount.yaml b/infra/k6/templates/serviceAccount.yaml new file mode 100644 index 0000000..bcc3ab3 --- /dev/null +++ b/infra/k6/templates/serviceAccount.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Values.manager.serviceAccount }} + namespace: {{- include "operator.namespace" . }} + labels: + {{- include "operator.labels" . }} + annotations: + {{- include "operator.annotations" . | default "" | nindent 4 }} diff --git a/infra/k6/values.yaml b/infra/k6/values.yaml new file mode 100644 index 0000000..c8f61c2 --- /dev/null +++ b/infra/k6/values.yaml @@ -0,0 +1,61 @@ +# customAnnotations -- Custom Annotations to be applied on all resources +customAnnotations: {} + +# customLabels -- Custom Label to be applied on all resources +customLabels: {} + +namespace: + # namespace.create -- create the namespace (default: true) + create: true + +prometheus: + # prometheus.enabled -- enables the prometheus metrics scraping (default: false) + enabled: false + +authProxy: + # authProxy.enabled -- enables the protection of /metrics endpoint. (https://github.com/brancz/kube-rbac-proxy) + enabled: false + image: + # authProxy.image.name -- rbac-proxy image name + name: gcr.io/kubebuilder/kube-rbac-proxy + # authProxy.image.tag -- rbac-proxy image tag + tag: v0.5.0 + # authProxy.image.pullPolicy -- pull policy for the image can be Always, Never, IfNotPresent (default: IfNotPresent) + pullPolicy: IfNotPresent + # authProxy.resources -- rbac-proxy resource limitation/request + resources: {} + # authProxy.livenessProbe -- Liveness probe in Probe format + livenessProbe: {} + # authProxy.readinessProbe -- Readiness probe in Probe format + readinessProbe: {} + +controlPlane: "controller-manager" + +manager: + # manager.serviceAccount -- kubernetes service account for the manager + serviceAccount: k6-operator-controller + image: + # manager.image.name -- controller-manager image name + name: ghcr.io/grafana/operator + # manager.image.tag -- controller-manager image tag + tag: latest + # manager.image.pullPolicy -- pull policy for the image possible values Always, Never, IfNotPresent (default: Always) + pullPolicy: Always + # manager.livenessProbe -- Liveness probe in Probe format + livenessProbe: {} + # manager.readinessProbe -- Readiness probe in Probe format + readinessProbe: {} + # manager.env -- Environment variable to be passet to the controller + env: {} + # manager.resources -- controller-manager Resources definition + resources: + limits: + # manager.resources.limits.cpu -- controller-manager CPU limit (Max) + cpu: 100m + # manager.resources.limits.memory -- controller-manager Memory limit (Max) + memory: 100Mi + requests: + # manager.resources.requests.cpu -- controller-manager CPU request (Min) + cpu: 100m + # manager.resources.requests.memory -- controller-manager Memory request (Min) + memory: 50Mi diff --git a/infra/router/.helmignore b/infra/router/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/infra/router/.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/infra/router/Chart.lock b/infra/router/Chart.lock new file mode 100644 index 0000000..24eccfa --- /dev/null +++ b/infra/router/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: router + repository: oci://ghcr.io/apollographql/helm-charts + version: 1.33.2 +digest: sha256:f484fb7f0528aa2b30b420c77c5e122e30315e560838a213e8b5b82fba536bf7 +generated: "2023-11-07T13:17:49.20246-05:00" diff --git a/infra/router/Chart.yaml b/infra/router/Chart.yaml new file mode 100644 index 0000000..ec2cd6c --- /dev/null +++ b/infra/router/Chart.yaml @@ -0,0 +1,29 @@ +apiVersion: v2 +name: router +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. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.1 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" + +dependencies: + - name: router + version: 1.33.2 + repository: oci://ghcr.io/apollographql/helm-charts diff --git a/infra/router/charts/router-1.33.2.tgz b/infra/router/charts/router-1.33.2.tgz new file mode 100644 index 0000000..074c730 Binary files /dev/null and b/infra/router/charts/router-1.33.2.tgz differ diff --git a/infra/router/environments/dev.yaml b/infra/router/environments/dev.yaml new file mode 100644 index 0000000..79519d8 --- /dev/null +++ b/infra/router/environments/dev.yaml @@ -0,0 +1,5 @@ +router: + resources: + requests: + cpu: 100m + memory: 256Mi diff --git a/infra/router/environments/prod.yaml b/infra/router/environments/prod.yaml new file mode 100644 index 0000000..7f40643 --- /dev/null +++ b/infra/router/environments/prod.yaml @@ -0,0 +1,36 @@ +router: + router: + configuration: + # duplicated from ../values.yaml + health_check: + listen: 0.0.0.0:8080 + sandbox: + enabled: true + homepage: + enabled: false + supergraph: + introspection: true + include_subgraph_errors: + all: true + plugins: + experimental.expose_query_plan: true + # end duplication + telemetry: + apollo: + field_level_instrumentation_sampler: 0.5 + tracing: + otlp: + endpoint: http://collector.monitoring:4317 + protocol: grpc + trace_config: + sampler: 0.5 + service_name: "router" + service_namespace: "router" + metrics: + otlp: + endpoint: http://collector.monitoring:4317 + protocol: grpc + resources: + requests: + cpu: 500m + memory: 512Mi diff --git a/infra/router/jwks.json b/infra/router/jwks.json new file mode 100644 index 0000000..013e156 --- /dev/null +++ b/infra/router/jwks.json @@ -0,0 +1,12 @@ +{ + "keys": [ + { + "kty": "EC", + "crv": "P-256", + "kid": "1", + "use": "sig", + "x": "Ku7NHl_Biej1FB0GMBrN2NRxrUBj2PbBeFPj_OnZPyo", + "y": "HZUWbmSA7PjVskGPGh7NAmKx3AOLLVpbCgg07jvTVV8" + } + ] +} \ No newline at end of file diff --git a/infra/router/rhai/client_id.rhai b/infra/router/rhai/client_id.rhai new file mode 100644 index 0000000..da625b3 --- /dev/null +++ b/infra/router/rhai/client_id.rhai @@ -0,0 +1,32 @@ +fn process_request(request) { + log_info("processing request"); + let valid_clients = ["1", "2"]; + let valid_client_names = ["apollo-client"]; + + if ("apollographql-client-version" in request.headers && "apollographql-client-name" in request.headers) { + let client_header = request.headers["apollographql-client-version"]; + let name_header = request.headers["apollographql-client-name"]; + + if !valid_clients.contains(client_header) { + log_error("Invalid client ID provided"); + throw #{ + status: 401, + message: "Invalid client ID provided" + }; + } + if !valid_client_names.contains(name_header) { + log_error("Invalid client name provided"); + throw #{ + status: 401, + message: "Invalid client name provided" + }; + } + } + else { + log_error("No client headers set. Please provide headers: apollographql-client-name and apollographql-client-version"); + throw #{ + status: 401, + message: "No client headers set" + }; + } +} \ No newline at end of file diff --git a/infra/router/rhai/main.rhai b/infra/router/rhai/main.rhai new file mode 100644 index 0000000..5489251 --- /dev/null +++ b/infra/router/rhai/main.rhai @@ -0,0 +1,12 @@ +import "client_id" as client_id; + +fn process_request(request) { + client_id::process_request(request) +} + +fn supergraph_service(service) { + // Rhai convention for creating a function pointer + const request_callback = Fn("process_request"); + + service.map_request(request_callback); +} \ No newline at end of file diff --git a/infra/router/templates/backendconfig.yaml b/infra/router/templates/backendconfig.yaml new file mode 100644 index 0000000..d4bc091 --- /dev/null +++ b/infra/router/templates/backendconfig.yaml @@ -0,0 +1,12 @@ +{{- if and .Values.ingress.enabled .Values.ingress.gcp -}} +apiVersion: cloud.google.com/v1 +kind: BackendConfig +metadata: + name: http-hc-config +spec: + healthCheck: + checkIntervalSec: 15 + port: {{ splitList ":" ((index .Values.router.router.configuration "health_check").listen | default ":8088") | last }} + type: HTTP + requestPath: /health +{{- end }} diff --git a/infra/router/templates/configmap.yaml b/infra/router/templates/configmap.yaml new file mode 100644 index 0000000..e9242c1 --- /dev/null +++ b/infra/router/templates/configmap.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: jwks-configmap + labels: + {{- include "router.labels" . | nindent 4 }} +data: + jwks.json: | + { + "keys": [ + { + "kty": "EC", + "crv": "P-256", + "kid": "1", + "use": "sig", + "x": "Ku7NHl_Biej1FB0GMBrN2NRxrUBj2PbBeFPj_OnZPyo", + "y": "HZUWbmSA7PjVskGPGh7NAmKx3AOLLVpbCgg07jvTVV8" + } + ] + } \ No newline at end of file diff --git a/infra/router/templates/ingress.yaml b/infra/router/templates/ingress.yaml new file mode 100644 index 0000000..7593a85 --- /dev/null +++ b/infra/router/templates/ingress.yaml @@ -0,0 +1,17 @@ +{{- if .Values.ingress.enabled -}} +{{- $svcPort := .Values.router.service.port -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ .Values.ingress.name }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + defaultBackend: + service: + name: {{ .Values.ingress.name }} + port: + number: {{ $svcPort }} +{{- end }} diff --git a/infra/router/templates/rhai-config.yaml b/infra/router/templates/rhai-config.yaml new file mode 100644 index 0000000..848adc3 --- /dev/null +++ b/infra/router/templates/rhai-config.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: rhai-config + labels: + {{- include "router.labels" . | nindent 4 }} +data: + {{- (.Files.Glob "rhai/*").AsConfig | nindent 2 }} \ No newline at end of file diff --git a/infra/router/values.yaml b/infra/router/values.yaml new file mode 100644 index 0000000..9ca3215 --- /dev/null +++ b/infra/router/values.yaml @@ -0,0 +1,58 @@ +# Default values for router. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +router: + router: + configuration: + # duplicated in environments/prod.yaml + health_check: + listen: 0.0.0.0:8080 + sandbox: + enabled: true + homepage: + enabled: false + supergraph: + introspection: true + include_subgraph_errors: + all: true + plugins: + experimental.expose_query_plan: true + rhai: + scripts: /dist/rhai + main: main.rhai + authentication: + router: + jwt: + jwks: + - url: file:///router/jwks.json + authorization: + preview_directives: + enabled: true + extraVolumeMounts: + - name: jwks-configmap-volume + mountPath: /router + readOnly: true + - name: rhai-volume + mountPath: /dist/rhai + readOnly: true + extraVolumes: + - name: jwks-configmap-volume + configMap: + # Provide the name of the ConfigMap containing the files you want + # to add to the container + name: jwks-configmap + - name: rhai-volume + configMap: + # Provide the name of the ConfigMap containing the files you want + # to add to the container + name: rhai-config + ingress: + enabled: false + service: + annotations: + cloud.google.com/backend-config: '{"default": "http-hc-config"}' +ingress: + enabled: true + gcp: true + name: router diff --git a/infra/tests/Chart.yaml b/infra/tests/Chart.yaml new file mode 100644 index 0000000..d1645a2 --- /dev/null +++ b/infra/tests/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +appVersion: "0.0.1" +description: Load tests as configmaps +name: tests +version: 0.0.1 +kubeVersion: ">=1.16.0-0" diff --git a/infra/tests/src/long.js b/infra/tests/src/long.js new file mode 100644 index 0000000..0547cec --- /dev/null +++ b/infra/tests/src/long.js @@ -0,0 +1,68 @@ +import http from "k6/http"; +import { check } from "k6"; + +export const options = { + stages: [ + { duration: "30s", target: 100 }, // simulate ramp-up of traffic from 1 to 100 users + { duration: "2m", target: 100 }, // stay at 100 users + { duration: "30s", target: 0 }, // ramp-down to 0 users + ], + thresholds: { + http_req_duration: ["p(99)<1500"], // 99% of requests must complete below 1.5s + }, +}; + +const BASE_URL = "http://router.router.svc.cluster.local/"; + +const loginMutation = ` +mutation Login { + login(username: "LOAD_TEST"){ + token + } +} +` + +const query = ` +query Locations { + locations { + name + reviewsForLocation { + rating + } + } +} +`; + +const headers = { + "Content-Type": "application/json", + "apollographql-client-name": "apollo-client", + "apollographql-client-version": "1" +}; + +export default () => { + const loginRes = http.post(BASE_URL, JSON.stringify({ query: loginMutation }), { + headers: headers, + }); + check(loginRes, { + "is login status 200": (r) => r.status === 200, + }); + const loginBody = JSON.parse(loginRes.body); + check(loginBody, { + "login without errors": (b) => b.errors == null, + }); + const jwt = loginBody.data.login.token; + + const requestHeaders = headers; + requestHeaders['Authorization'] = `Bearer ${jwt}` + const res = http.post(BASE_URL, JSON.stringify({ query: query }), { + headers: requestHeaders, + }); + check(res, { + "is status 200": (r) => r.status === 200, + }); + + const body = JSON.parse(res.body); + check(body, { + "without errors": (b) => b.errors == null, + }); +}; diff --git a/infra/tests/src/short.js b/infra/tests/src/short.js new file mode 100644 index 0000000..96d2df6 --- /dev/null +++ b/infra/tests/src/short.js @@ -0,0 +1,65 @@ +import http from "k6/http"; +import { check } from "k6"; + +export const options = { + stages: [ + { duration: "5s", target: 100 }, // simulate ramp-up of traffic from 1 to 100 users + { duration: "30s", target: 100 }, // stay at 100 users + { duration: "5", target: 0 }, // ramp-down to 0 users + ], + thresholds: { + http_req_duration: ["p(99)<1500"], // 99% of requests must complete below 1.5s + }, +}; + +const BASE_URL = "http://router.router.svc.cluster.local/"; + +const loginMutation = ` +mutation Login { + login(username: "LOAD_TEST"){ + token + } +} +` + +const query = ` +query Locations { + locations { + name + } +} +`; + +const headers = { + "Content-Type": "application/json", + "apollographql-client-name": "apollo-client", + "apollographql-client-version": "1" +}; + +export default () => { + const loginRes = http.post(BASE_URL, JSON.stringify({ query: loginMutation }), { + headers: headers, + }); + check(loginRes, { + "is login status 200": (r) => r.status === 200, + }); + const loginBody = JSON.parse(loginRes.body); + check(loginBody, { + "login without errors": (b) => b.errors == null, + }); + const jwt = loginBody.data.login.token; + + const requestHeaders = headers; + requestHeaders['Authorization'] = `Bearer ${jwt}` + const res = http.post(BASE_URL, JSON.stringify({ query: query }), { + headers: requestHeaders, + }); + check(res, { + "is status 200": (r) => r.status === 200, + }); + + const body = JSON.parse(res.body); + check(body, { + "without errors": (b) => b.errors == null, + }); +}; diff --git a/infra/tests/templates/configmap.yaml b/infra/tests/templates/configmap.yaml new file mode 100644 index 0000000..c91b150 --- /dev/null +++ b/infra/tests/templates/configmap.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: tests +data: +{{ (.Files.Glob "src/*").AsConfig | indent 2 }} diff --git a/infra/zipkin/.helmignore b/infra/zipkin/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/infra/zipkin/.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/infra/zipkin/Chart.lock b/infra/zipkin/Chart.lock new file mode 100644 index 0000000..acc16f0 --- /dev/null +++ b/infra/zipkin/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: zipkin + repository: https://openzipkin.github.io/zipkin + version: 0.3.0 +digest: sha256:680fbd54e0e6d16fa1f1ec16bce4dcde89e8d60a25888d37ff10036e6304c5f0 +generated: "2023-01-13T13:16:32.378945-05:00" diff --git a/infra/zipkin/Chart.yaml b/infra/zipkin/Chart.yaml new file mode 100644 index 0000000..f06f695 --- /dev/null +++ b/infra/zipkin/Chart.yaml @@ -0,0 +1,14 @@ +apiVersion: v2 +name: zipkin +description: A Helm chart for Kubernetes + +type: application + +version: 0.1.0 + +appVersion: "1.16.0" + +dependencies: + - name: zipkin + version: 0.3.0 + repository: https://openzipkin.github.io/zipkin \ No newline at end of file diff --git a/infra/zipkin/values.yaml b/infra/zipkin/values.yaml new file mode 100644 index 0000000..e8aa325 --- /dev/null +++ b/infra/zipkin/values.yaml @@ -0,0 +1,44 @@ +replicaCount: 1 + +image: + repository: openzipkin/zipkin-slim + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + + +serviceAccount: + # Specifies whether a service account should be created + create: true + # 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: "" + # Whether to use pod security policy + psp: false + +podAnnotations: + sidecar.istio.io/inject: "false" + +podSecurityContext: + {} + +securityContext: + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + +service: + type: ClusterIP + port: 9411 + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +zipkin: + storage: + type: mem \ No newline at end of file diff --git a/keys/certificate.pem b/keys/certificate.pem new file mode 100644 index 0000000..49d30e2 --- /dev/null +++ b/keys/certificate.pem @@ -0,0 +1,8 @@ +-----BEGIN CERTIFICATE----- +MIIBETCBuAIJANYdv6q6FctQMAoGCCqGSM49BAMCMBExDzANBgNVBAoMBkFwb2xs +bzAeFw0yMzExMDUxNzA3MjdaFw0zMzExMDIxNzA3MjdaMBExDzANBgNVBAoMBkFw +b2xsbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCruzR5fwYno9RQdBjAazdjU +ca1AY9j2wXhT4/zp2T8qHZUWbmSA7PjVskGPGh7NAmKx3AOLLVpbCgg07jvTVV8w +CgYIKoZIzj0EAwIDSAAwRQIhALr6A5+tBkBld6yJPLgrz3lqvdgVAvULymq7rnB8 +220bAiB5lXICOKavjtloIycve3F4PT8KB1n8CmQ+xgcHZXfI1Q== +-----END CERTIFICATE----- diff --git a/keys/jwks.json b/keys/jwks.json new file mode 100644 index 0000000..6eb9cb0 --- /dev/null +++ b/keys/jwks.json @@ -0,0 +1,12 @@ +{ + "keys": [ + { + "kty": "EC", + "crv": "P-256", + "kid": "1", + "use": "sig", + "x": "Ku7NHl_Biej1FB0GMBrN2NRxrUBj2PbBeFPj_OnZPyo", + "y": "HZUWbmSA7PjVskGPGh7NAmKx3AOLLVpbCgg07jvTVV8" + } + ] + } \ No newline at end of file diff --git a/keys/private_key.pem b/keys/private_key.pem new file mode 100644 index 0000000..d6c70a1 --- /dev/null +++ b/keys/private_key.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIOTGInb0Hca0vKMmZXsaYQ4fETaVnAQ2TCEVVfHKs8groAoGCCqGSM49 +AwEHoUQDQgAEKu7NHl/Biej1FB0GMBrN2NRxrUBj2PbBeFPj/OnZPyodlRZuZIDs ++NWyQY8aHs0CYrHcA4stWlsKCDTuO9NVXw== +-----END EC PRIVATE KEY----- diff --git a/keys/public_key.jwk b/keys/public_key.jwk new file mode 100644 index 0000000..f3e27e7 --- /dev/null +++ b/keys/public_key.jwk @@ -0,0 +1,6 @@ +{ + "kty": "EC", + "crv": "P-256", + "x": "Ku7NHl_Biej1FB0GMBrN2NRxrUBj2PbBeFPj_OnZPyo", + "y": "HZUWbmSA7PjVskGPGh7NAmKx3AOLLVpbCgg07jvTVV8" +} diff --git a/keys/public_key.pem b/keys/public_key.pem new file mode 100644 index 0000000..d759513 --- /dev/null +++ b/keys/public_key.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEKu7NHl/Biej1FB0GMBrN2NRxrUBj +2PbBeFPj/OnZPyodlRZuZIDs+NWyQY8aHs0CYrHcA4stWlsKCDTuO9NVXw== +-----END PUBLIC KEY-----