From c9cef15297995df810109b7b54a5dde949fb19a0 Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Fri, 15 Nov 2024 09:44:13 -0500 Subject: [PATCH 01/22] Directory README --- charts/directory/README.md | 186 +++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 charts/directory/README.md diff --git a/charts/directory/README.md b/charts/directory/README.md new file mode 100644 index 0000000..4d85372 --- /dev/null +++ b/charts/directory/README.md @@ -0,0 +1,186 @@ +# Directory Helm Chart + +This chart installs the Aserto multi-tenant Directory service. It is backed by a Postgres database +and provides central management of authorization models and data. +Topaz instances can be configured to sync data from a specific tenant or to evaluate requests +in the central directory itself, without a local copy. + + +## Requirements + +### Helm + +[Helm](https://helm.sh) must be installed to use the charts. +Please refer to Helm's [documentation](https://helm.sh/docs/intro/install) to get started. + +Full OCI support is available starting from Helm v3.8.0. If you are using an older version, +follow Helm's [instructions](https://helm.sh/docs/topics/registries/) on how to enable OCI + + +### PostgreSQL + +The Aserto directory service requires a PostgreSQL database to store its data. +You can deploy a PostgresSQL instance using the +[Bitnami chart](https://bitnami.com/stack/postgresql/helm) or use a managed PostgreSQL +from your cloud provider. + + +#### Databases and Roles + +The directory service uses two database that can run on the same or different PostgreSQL +instances. The database are named `aserto-ds` and `aserto-root-ds` by default but the +names are configurable. + +When both databases are on the same PostgreSQL instance, the service can be configured to +connect to both using the same role or different ones. In either case, each role must +be the owner of the database it connects to and have the `CREATEROLE` option. +Additionally, if the role has the `CREATEDB` option, the service can create the databases +automatically at startup if they don't already exist. + +Without the `CREATEDB` option, you must create the databases manually before deploying the chart. +The following SQL commands can be used to create the roles and databases: + +```sql +CREATE ROLE aserto_root CREATEROLE LOGIN PASSWORD ''; +CREATE ROLE aserto_tenant CREATEROLE LOGIN PASSWORD ''; + +CREATE DATABASE "aserto-root-ds" OWNER = aserto_root TEMPLATE = template0; +CREATE DATABASE "aserto-ds" OWNER = aserto_tenant TEMPLATE = template0; +``` + +### Kubernetes Secrets + +The directory service require several secrets to be created in the kubernetes namespace to +which the service is deployed. The examples in the sections below use the `aserto` namespace. +To create the namespace, use: + +```shell +kubectl create namespace aserto +``` + +#### Database Credentials + +The database credentials must be stored in a Kubernetes secret in the same namespace as the +directory deployment. The secret must have two keys: `username` and `password`. + +For example, if deploying to the `aserto` namespace, a secret named `pg-ds-credentials`can be +created using: + +```shell +kubectl create secret generic pg-ds-credentials \ + --namespace aserto \ + --from-literal=username= \ + --from-literal=password= +``` + +Where `` and `` are database role credentials. + + +#### Image Pull Secret + +The Aserto images are stored in a private registry and require an access token to be stored in +a kubernetes secret for the cluster to be able to pull them. +To create a token, log into your GitHub account that was granted acccess to the Aserto registry, +follow [these instructions](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) +and include the `read:packages` scope. + +The token must then be stored in a Kubernetes secret in the same namespace as the Aserto chart: + +```shell +kubectl create secret docker-registry ghcr-creds \ + --namespace aserto \ + --docker-server=https://ghcr.io \ + --docker-username= \ + --docker-password= +``` + +### SSH Keys + +The directory service exposes a management endpoint over SSH. The management endpoint is used, +among other things, to initialize the root directory database. +The directory chart can be configured with one or more SSH public keys to be granted access to +the management endpoint. + +If you don't already have an SSH key, you can follow [these instructions](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent#generating-a-new-ssh-key) +to create one. + + +## Configuration + +Configuring a deployment is done using a `values.yaml` file that can be passed as an argument to +`helm install`, or embedded in your own chart's `values.yaml` if you are using the Aserto chart +as a dependency. + +The main configuration options are discussed below. Take a look at [values.yaml](https://github.com/aserto-dev/helm/blob/main/charts/directory/values.yaml) +for a full view of available options. + + +### Image + +The directory service is a part of Aserto's commercial offering and the service container image +is not available publicly. +If your GitHub account has been granted access to the image you can proceed in one of two ways. + +If you have a private OCI registry that your Kubernetes cluster can access, you can pull the +directory image using your GitHub credentials, push it your registry, and use it from there. +In your `values.yaml` file, point the `image.repository` to your local registry. For example: +```yaml +image: + repository: my.registry.com/my-repo/directory + # Optionally override the image tag. The default is the chart appVersion. + # tag: x.y.z +``` + +If you'd prefer to pull the Aserto image directly from your Kubernetes cluster you can create an +[Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic) +with the `read:packages` scope. Store the token in a Kubernetes secret of type `docker-registry` +in the same namespace you plan to deploy the directory into. For example, if you are deploying to +the `aserto` namespace, you can create a secret named `ghcr-creds` by replacing `` and +`` below with your GitHub user and access token. + +```sh +kubectl create secret docker-registry ghcr-creds \ + --docker-server=https://ghcr.io --docker-username= --docker-password= -n directory +``` + +In your `values.yaml` file add the secret name to `imagePullSecrets`: +```yaml +imagePullSecrets: + - name: ghcr-creds +``` + +### SSH Keys + +At least one admin key must be configured under `sshAdminKeys`: +```yaml +sshAdminKeys: | + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABg... admin@acme.com + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... another_admin@acme.com +``` + +### Database + +The directory service uses two Postgres databases, an internal "root" directory used to authorize access to the +directory itself and other Aserto services, and a "tenant" directory where data owned by directory tenants +are stored. The two database can run on the same Postgres instance or on separate ones. + +[See above](#databases-and-roles) on how to set up the databases and create Kubernetes secrets with +credentials. + +Then configure the databases in `rootDirectory.database` and `tenantDirectory.database`: + +```yaml +rootDirectory: + database: + host: # hostname of the Postgres instance to be used for the root directory. + sslMode: require # set to 'disable' if the database doesn't use TLS. + admin: + credentialsSecret: # name of k8s secret with the db credentials + +tenantDirectory: + database: + host: # hostname of the Postgres instance to be used for the tenant directory. + sslMode: require # set to 'disable' if the database doesn't use TLS. + admin: + credentialsSecret: # name of k8s secret with the db credentials +``` From 70b3eec36270ff03270f9a9b7577df5a2922011d Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Fri, 15 Nov 2024 09:44:31 -0500 Subject: [PATCH 02/22] Increase default directory cache size to 128M --- charts/directory/templates/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/directory/templates/config.yaml b/charts/directory/templates/config.yaml index fa10bc1..7daf1f8 100644 --- a/charts/directory/templates/config.yaml +++ b/charts/directory/templates/config.yaml @@ -89,7 +89,7 @@ stringData: cache: {{- $cache := .Values.cache | default dict }} - cache_size_mb: {{ $cache.sizeMB | default 25 }} + cache_size_mb: {{ $cache.sizeMB | default 128 }} cache_invalidation_time_seconds: {{ $cache.ttlSeconds | default 1800 }} clean_window_seconds: {{ $cache.cleanWindowSeconds | default 10 }} From abb28dae7838cee4057156d2c0a75a2cb78bf91f Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Wed, 20 Nov 2024 14:37:47 -0500 Subject: [PATCH 03/22] Support running without TLS and initial test harness --- charts/aserto-lib/templates/_client.tpl | 19 +- charts/aserto-lib/templates/_clusteraddr.tpl | 2 +- charts/aserto-lib/templates/_config.tpl | 20 +- charts/aserto-lib/templates/_golangsvc.tpl | 26 +- charts/aserto/values.yaml | 22 + charts/authorizer/templates/deployment.yaml | 55 +- charts/console/templates/deployment.yaml | 2 +- charts/console/values.yaml | 2 +- charts/directory/.helmignore | 1 + charts/directory/Chart.lock | 6 +- charts/directory/Chart.yaml | 3 +- charts/directory/templates/_helpers.tpl | 33 + charts/directory/templates/api_keys.yaml | 18 +- charts/directory/templates/config.yaml | 2 +- charts/directory/templates/deployment.yaml | 45 +- charts/directory/templates/root_key.yaml | 22 + charts/directory/values.yaml | 37 +- charts/discovery/templates/deployment.yaml | 10 +- charts/scim/templates/deployment.yaml | 10 +- charts/scim/values.yaml | 4 +- charts/topaz/.helmignore | 1 + test/directory/directory.values.yaml | 38 ++ test/directory/tests.yaml | 23 + tools/k3test/.python-version | 1 + tools/k3test/README.md | 0 tools/k3test/k3test.py | 211 ++++++ tools/k3test/pylintrc | 651 +++++++++++++++++++ tools/k3test/pyproject.toml | 17 + tools/k3test/uv.lock | 487 ++++++++++++++ 29 files changed, 1646 insertions(+), 122 deletions(-) create mode 100644 charts/directory/templates/root_key.yaml create mode 100644 test/directory/directory.values.yaml create mode 100644 test/directory/tests.yaml create mode 100644 tools/k3test/.python-version create mode 100644 tools/k3test/README.md create mode 100755 tools/k3test/k3test.py create mode 100644 tools/k3test/pylintrc create mode 100644 tools/k3test/pyproject.toml create mode 100644 tools/k3test/uv.lock diff --git a/charts/aserto-lib/templates/_client.tpl b/charts/aserto-lib/templates/_client.tpl index 30cdb85..4eb9919 100644 --- a/charts/aserto-lib/templates/_client.tpl +++ b/charts/aserto-lib/templates/_client.tpl @@ -1,22 +1,25 @@ -{{- define "aserto-lib.clientCA" }} -{{- if .disableTLSVerification }} +{{- define "aserto-lib.clientTLS" }} +{{- if .noVerify | and .noTLS -}} + {{- fail "'noVerify' and 'noTLS' are mutually exclusive." }} +{{- end }} +{{- if .noTLS }} +no_tls: true +{{- else if .skipVerify }} insecure : true -{{- else if .grpcCertSecret }} +{{- else if .caCertSecret }} ca_cert_path: /{{ .certVolume }}/ca.crt -{{- else }} -ca_cert_path: /grpc-certs/ca.crt {{- end }} {{- end }} {{- define "aserto-lib.rootDirectoryClient" -}} address: {{ include "aserto-lib.rootDirectoryAddress" . }} tenant_id: {{ include "aserto-lib.rootDirectoryTenantID" . }} -{{- $cfg := include "aserto-lib.rootDirectoryCfg" . | fromYaml }} -{{- include "aserto-lib.clientCA" (mergeOverwrite $cfg (dict "certVolume" "root-ds-grpc-certs")) -}} +{{- $cfg := include "aserto-lib.rootClientCfg" . | fromYaml }} +{{- include "aserto-lib.clientTLS" (mergeOverwrite $cfg (dict "certVolume" "root-ds-grpc-certs")) -}} {{- end }} {{- define "aserto-lib.directoryClient" -}} address: {{ include "aserto-lib.directoryAddress" . }} {{- $cfg := include "aserto-lib.mergeGlobal" (list . "directory") | fromYaml }} -{{- include "aserto-lib.clientCA" (mergeOverwrite $cfg (dict "certVolume" "ds-grpc-certs")) -}} +{{- include "aserto-lib.clientTLS" (mergeOverwrite $cfg (dict "certVolume" "ds-grpc-certs")) -}} {{- end }} diff --git a/charts/aserto-lib/templates/_clusteraddr.tpl b/charts/aserto-lib/templates/_clusteraddr.tpl index d21d00e..193d872 100644 --- a/charts/aserto-lib/templates/_clusteraddr.tpl +++ b/charts/aserto-lib/templates/_clusteraddr.tpl @@ -29,7 +29,7 @@ Args: [scope, config, service] Cluster address of the root directory service */}} {{- define "aserto-lib.rootDirectoryAddress" }} -{{- include "aserto-lib.svcClusterAddress" (list . "grpc" "rootDirectory" "directory")}} +{{- include "aserto-lib.svcClusterAddress" (list . "grpc" "rootDS" "directory")}} {{- end }} {{/* diff --git a/charts/aserto-lib/templates/_config.tpl b/charts/aserto-lib/templates/_config.tpl index 0223cf6..23ae9c3 100644 --- a/charts/aserto-lib/templates/_config.tpl +++ b/charts/aserto-lib/templates/_config.tpl @@ -1,5 +1,5 @@ -{{- define "aserto-lib.rootDirectoryCfg" }} -{{- include "aserto-lib.mergeGlobal" (list . "rootDirectory") }} +{{- define "aserto-lib.rootClientCfg" }} +{{- include "aserto-lib.mergeGlobal" (list . "rootDS") }} {{- end }} {{- define "aserto-lib.directoryCfg" }} @@ -10,9 +10,17 @@ {{- include "aserto-lib.mergeGlobal" (list . "discovery") }} {{- end }} -{{- define "aserto-lib.rootDirectoryApiKey" }} -{{- (include "aserto-lib.rootDirectoryCfg" . | fromYaml).apiKey | - default (dict "secretName" "root-ds-keys" "secretKey" "api-key") | toYaml -}} +{{- define "aserto-lib.rootApiKeyEnv" }} +{{- with include "aserto-lib.rootClientCfg" . | fromYaml -}} +{{- if .apiKey -}} +value: {{ .apiKey }} +{{- else -}} +valueFrom: + secretKeyRef: + name: {{ (.apiKeySecret).name | default "root-ds-keys" }} + key: {{ (.apiKeySecret).key | default "api-key" }} +{{- end }} +{{- end }} {{- end }} {{- define "aserto-lib.directoryApiKeys" }} @@ -30,6 +38,6 @@ Root directory tenant ID */}} {{- define "aserto-lib.rootDirectoryTenantID" -}} -{{- (include "aserto-lib.rootDirectoryCfg" . | fromYaml).tenantID | +{{- (include "aserto-lib.rootClientCfg" . | fromYaml).tenantID | default "00000000-0000-11ef-0000-000000000000" -}} {{- end }} diff --git a/charts/aserto-lib/templates/_golangsvc.tpl b/charts/aserto-lib/templates/_golangsvc.tpl index 7358cfd..236fee1 100644 --- a/charts/aserto-lib/templates/_golangsvc.tpl +++ b/charts/aserto-lib/templates/_golangsvc.tpl @@ -17,32 +17,38 @@ Renders gRPC service configuration. */}} {{- define "aserto-lib.grpcService" -}} listen_address: 0.0.0.0:{{ include "aserto-lib.grpcPort" . }} -connection_timeout_seconds: {{ (include "aserto-lib.grpcConfig" . | fromYaml).connectionTimeoutSec | default "2" }} +{{- with include "aserto-lib.grpcConfig" . | fromYaml }} +connection_timeout_seconds: {{ .connectionTimeoutSec }} +{{- if .certSecret }} certs: tls_key_path: '/grpc-certs/tls.key' tls_cert_path: '/grpc-certs/tls.crt' tls_ca_cert_path: '/grpc-certs/ca.crt' {{- end }} +{{- end }} +{{- end }} {{/* Renders HTTPS service configuration. */}} {{- define "aserto-lib.httpsService" -}} listen_address: 0.0.0.0:{{ include "aserto-lib.httpsPort" . }} +{{- with include "aserto-lib.httpsConfig" . | fromYaml }} +{{- with .allowed_origins }} +allowed_origins: +{{- . | toYaml | nindent 2 }} +{{- end }} +read_timeout: {{ .read_timeout | default "2s"}} +read_header_timeout: {{ .read_header_timeout | default "2s" }} +write_timeout: {{ .write_timeout | default "2s" }} +idle_timeout: {{ .idle_timeout | default "30s" }} +{{- with .cerSecret }} certs: tls_key_path: '/https-certs/tls.key' tls_cert_path: '/https-certs/tls.crt' tls_ca_cert_path: '/https-certs/ca.crt' - -{{- $cfg := include "aserto-lib.httpsConfig" . | fromYaml }} -{{- if $cfg.allowed_origins }} -allowed_origins: -{{- $cfg.allowed_origins | toYaml | nindent 2 }} {{- end }} -read_timeout: {{ $cfg.read_timeout | default "2s"}} -read_header_timeout: {{ $cfg.read_header_timeout | default "2s" }} -write_timeout: {{ $cfg.write_timeout | default "2s" }} -idle_timeout: {{ $cfg.idle_timeout | default "30s" }} +{{- end }} {{- end }} {{/* diff --git a/charts/aserto/values.yaml b/charts/aserto/values.yaml index 0413e6d..4c82c01 100644 --- a/charts/aserto/values.yaml +++ b/charts/aserto/values.yaml @@ -43,6 +43,28 @@ global: # durations: false # gateway: false + rootDS: + # Address and port of the root directory's gRPC service. + # Default: directory..svc.cluster.local:8282 + address: "" + # [Optiona] API key for the remote directory + apiKey: "" + # [Optional] Kubernetes secret containing the API key for the remote directory + apiKeySecret: + # Secret name + name: "" + # Secret key + key: "api-key" + # [Optional] Kubernetes secret containing the CA certificate of the root directory. + caCertSecret: + name: "" + key: "" + # Skip verification of remote TLS certificate + noVerify: false + # Connect over a plain-text connection. + # INSECURE: credentials are sent unencrypted within the cluster. + noTLS: false + rootDirectory: # Disable TLS verification on disableTLSVerification: true diff --git a/charts/authorizer/templates/deployment.yaml b/charts/authorizer/templates/deployment.yaml index b2024ab..685ad47 100644 --- a/charts/authorizer/templates/deployment.yaml +++ b/charts/authorizer/templates/deployment.yaml @@ -50,11 +50,11 @@ spec: {{- end }} {{- end }} - {{- with (include "aserto-lib.rootDirectoryCfg" . | fromYaml) }} - {{- if .grpcCertSecret }} + {{- with (include "aserto-lib.rootClientCfg" . | fromYaml) }} + {{- if .caCertSecret }} - name: root-ds-grpc-certs secret: - secretName: {{ .grpcCertSecret }} + secretName: {{ .caCertSecret }} items: - key: ca.crt path: ca.crt @@ -80,14 +80,14 @@ spec: args: ["run", "--config-file", "/config/config.yaml"] imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - {{- with (include "aserto-lib.selfPorts" . | fromYaml )}} + {{- with (include "aserto-lib.selfPorts" . | fromYaml )}} - name: https containerPort: {{ .https }} - name: grpc containerPort: {{ .grpc }} - name: metrics containerPort: {{ .metrics }} - {{- end }} + {{- end }} volumeMounts: - name: config mountPath: /config @@ -103,66 +103,61 @@ spec: readOnly: true {{- end }} - {{- if (include "aserto-lib.rootDirectoryCfg" . | fromYaml).grpcCertSecret }} + {{- if (include "aserto-lib.rootClientCfg" . | fromYaml).caCertSecret }} - name: root-ds-grpc-certs mountPath: /root-ds-grpc-certs readOnly: true - {{- end }} + {{- end }} - {{- if (include "aserto-lib.discoveryCfg" . | fromYaml).httpsCertSecret }} + {{- if (include "aserto-lib.discoveryCfg" . | fromYaml).httpsCertSecret }} - name: discovery-https-certs mountPath: /discovery-https-certs readOnly: true - {{- end }} + {{- end }} env: - {{- with .Values.apiKey }} + - name: AUTHORIZER_DS0_API_KEY + {{ include "aserto-lib.rootApiKeyEnv" . | nindent 14 }} + {{- with .Values.apiKey }} - name: AUTHORIZER_ROOT_KEY valueFrom: secretKeyRef: name: {{ .secretName }} key: {{ .secretKey }} - {{- end }} + {{- end }} - {{- with (include "aserto-lib.rootDirectoryApiKey" . | fromYaml) }} - - name: AUTHORIZER_DS0_API_KEY - valueFrom: - secretKeyRef: - name: {{ .secretName }} - key: {{ .secretKey }} - {{- end }} - {{- with (include "aserto-lib.directoryApiKeys" . | fromYaml) }} + {{- with (include "aserto-lib.directoryApiKeys" . | fromYaml) }} - name: AUTHORIZER_REMOTE_DIRECTORY_API_KEY valueFrom: secretKeyRef: name: {{ .secretName }} key: {{ .readerSecretKey }} - {{- end }} - {{- with (include "aserto-lib.discoveryApiKey" . | fromYaml) }} + {{- end }} + {{- with (include "aserto-lib.discoveryApiKey" . | fromYaml) }} - name: AUTHORIZER_DISCOVERY_ROOT_KEY valueFrom: secretKeyRef: name: {{ .secretName }} key: {{ .secretKey }} - {{- end }} - {{- with (include "aserto-lib.selfPorts" . | fromYaml )}} + {{- end }} + {{- with (include "aserto-lib.selfPorts" . | fromYaml )}} livenessProbe: grpc: port: {{ .health }} readinessProbe: grpc: port: {{ .health }} - {{- end }} + {{- end }} resources: {{- toYaml .Values.resources | nindent 12 }} - {{- with .Values.nodeSelector }} + {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} + {{- end }} + {{- with .Values.affinity }} affinity: {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} + {{- end }} + {{- with .Values.tolerations }} tolerations: {{- toYaml . | nindent 8 }} - {{- end }} + {{- end }} diff --git a/charts/console/templates/deployment.yaml b/charts/console/templates/deployment.yaml index 06af902..50730f5 100644 --- a/charts/console/templates/deployment.yaml +++ b/charts/console/templates/deployment.yaml @@ -55,7 +55,7 @@ spec: - name: DS0_TENANT_ID value: {{ .tenant_id }} {{- end }} - {{- with (include "aserto-lib.rootDirectoryApiKey" . | fromYaml) }} + {{- with (include "aserto-lib.rootApiKeyEnv" . | fromYaml) }} - name: DS0_ROOT_KEY valueFrom: secretKeyRef: diff --git a/charts/console/values.yaml b/charts/console/values.yaml index 119b4ba..3678ee2 100644 --- a/charts/console/values.yaml +++ b/charts/console/values.yaml @@ -20,7 +20,7 @@ oidc: rootDirectory: disableTLSVerification: false - grpcCertSecret: "" + caCertSecret: "" # address: "{{ .Release.Name }}-aserto-directory.aserto.svc.cluster.local:8282" # Set the service log level (trace/debug/info/warn/error) diff --git a/charts/directory/.helmignore b/charts/directory/.helmignore index 18607ed..c1c4640 100644 --- a/charts/directory/.helmignore +++ b/charts/directory/.helmignore @@ -23,3 +23,4 @@ .vscode/ build/ +ci/ diff --git a/charts/directory/Chart.lock b/charts/directory/Chart.lock index fc5be72..8baca77 100644 --- a/charts/directory/Chart.lock +++ b/charts/directory/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: aserto-lib - repository: oci://ghcr.io/aserto-dev/helm + repository: file:///Users/ronenhilewicz/aserto-dev/helm/charts/aserto-lib version: 0.1.5 -digest: sha256:d4b6f4909c81802d39c520b76bbcd5a1f7f9897d0b20cee02a9978f3a5b14447 -generated: "2024-11-12T16:34:28.799109-05:00" +digest: sha256:d4b6380105553ea5153be1973c56dcf386900d68679a53c987a163760ca8bacd +generated: "2024-11-15T16:05:15.959277-05:00" diff --git a/charts/directory/Chart.yaml b/charts/directory/Chart.yaml index 8469892..e6fcc31 100644 --- a/charts/directory/Chart.yaml +++ b/charts/directory/Chart.yaml @@ -32,4 +32,5 @@ appVersion: "0.33.1" dependencies: - name: aserto-lib version: ~0.1.5 - repository: oci://ghcr.io/aserto-dev/helm + # repository: oci://ghcr.io/aserto-dev/helm + repository: file:///Users/ronenhilewicz/aserto-dev/helm/charts/aserto-lib diff --git a/charts/directory/templates/_helpers.tpl b/charts/directory/templates/_helpers.tpl index 925eece..e8899b0 100644 --- a/charts/directory/templates/_helpers.tpl +++ b/charts/directory/templates/_helpers.tpl @@ -79,3 +79,36 @@ Create the name of the service account to use {{ fail "all tenants must include either 'keys' or 'keysSecret'" }} {{- end }} {{- end}} + + +{{- define "directory.rootClient" -}} +{{- if not (.Values.rootDirectory).runService | and (not .Values.rootDS) -}} + {{- fail "roodDS configuration is required when running a standalone directory with the root in another deployment."}} +{{- end }} +{{- if .Values.rootDS }} +{{ include "aserto-lib.rootDirectoryClient" . }} +{{- else -}} +address: localhost:{{ include "aserto-lib.grpcPort" . }} +tenant_id: {{ include "aserto-lib.rootDirectoryTenantID" . }} +{{- if (include "aserto-lib.grpcConfig" . | fromYaml).certSecret }} +ca_cert_path: /grpc-certs/ca.crt +{{- else }} +no_tls: true +{{- end }} +{{- end }} +{{- end }} + + +{{- define "directory.rootApiKeyEnv" -}} +{{- if not (.Values.rootDirectory).runService | and (not .Values.rootDS) -}} + {{- fail "roodDS configuration is required when running a standalone directory with the root in another deployment." -}} +{{- end -}} +{{- if .Values.rootDS -}} +{{ include "aserto-lib.rootApiKeyEnv" . }} +{{- else -}} +valueFrom: + secretKeyRef: + name: root-ds-keys + key: api-key +{{- end }} +{{- end }} diff --git a/charts/directory/templates/api_keys.yaml b/charts/directory/templates/api_keys.yaml index c09f975..f7cd365 100644 --- a/charts/directory/templates/api_keys.yaml +++ b/charts/directory/templates/api_keys.yaml @@ -1,20 +1,7 @@ ---- -{{- $cfg := include "aserto-lib.rootDirectoryApiKey" . | fromYaml -}} -{{- $data := (lookup "v1" "Secret" .Release.Namespace $cfg.secretName).data }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ $cfg.secretName }} -data: - {{- if $data }} - {{ $cfg.secretKey }}: {{ get $data $cfg.secretKey }} - {{- else }} - {{ $cfg.secretKey }}: {{ randAlphaNum 20 | b64enc }} - {{- end }} - ---- +{{- if .Values.rootDirectory.runService }} {{- $cfg := include "aserto-lib.directoryApiKeys" . | fromYaml -}} {{- $data := (lookup "v1" "Secret" .Release.Namespace $cfg.secretName).data }} +--- apiVersion: v1 kind: Secret metadata: @@ -27,4 +14,5 @@ data: {{ $cfg.writerSecretKey }}: {{ randAlphaNum 20 | b64enc }} {{ $cfg.readerSecretKey }}: {{ randAlphaNum 20 | b64enc }} {{- end }} +{{- end }} diff --git a/charts/directory/templates/config.yaml b/charts/directory/templates/config.yaml index 7daf1f8..ca6ae5c 100644 --- a/charts/directory/templates/config.yaml +++ b/charts/directory/templates/config.yaml @@ -74,7 +74,7 @@ stringData: root_ds: client: - {{- include "aserto-lib.rootDirectoryClient" . | nindent 8 }} + {{- include "directory.rootClient" . | nindent 8 }} {{- if and .Values.rootDirectory.runService .Values.rootDirectory.manifest }} root_manifest: {{ .Values.rootDirectory.manifest }} diff --git a/charts/directory/templates/deployment.yaml b/charts/directory/templates/deployment.yaml index 0b0affc..1c55d5f 100644 --- a/charts/directory/templates/deployment.yaml +++ b/charts/directory/templates/deployment.yaml @@ -36,31 +36,23 @@ spec: - name: admin-keys configMap: name: {{ include "directory.fullname" . }}-admin-keys + {{- with (include "aserto-lib.grpcConfig" . | fromYaml).certSecret }} - name: grpc-certs - {{- with (include "aserto-lib.grpcConfig" . | fromYaml) }} - {{- if .certSecret }} secret: - secretName: {{ .certSecret }} - {{- else }} - emptyDir: {} - {{- end }} + secretName: {{ . }} {{- end }} + {{- with (include "aserto-lib.httpsConfig" . | fromYaml).certSecret }} - name: https-certs - {{- with (include "aserto-lib.httpsConfig" . | fromYaml) }} - {{- if .certSecret }} secret: - secretName: {{ .certSecret }} - {{- else }} - emptyDir: {} - {{- end }} + secretName: {{ . }} {{- end }} - {{- if and .Values.tenantDirectory.runService .Values.tenantDirectory.rootDS.grpcCertSecret }} + {{- if (.Values.tenantDirectory).runService | and ((.Values.rootDS).caCertSecret).name }} - name: root-ds-grpc-certs secret: - secretName: {{ .Values.tenantDirectory.rootDS.grpcCertSecret }} + secretName: {{ .Values.rootDS.caCertSecret.name }} items: - - key: ca.crt + - key: {{ .Values.rootDS.caCertSecret.name | default "ca.crt" }} path: ca.crt {{- end }} @@ -72,14 +64,14 @@ spec: args: ["--config", "/config/config.yaml", "run"] imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - {{- with (include "aserto-lib.selfPorts" . | fromYaml )}} + {{- with (include "aserto-lib.selfPorts" . | fromYaml )}} - name: https containerPort: {{ .https }} - name: grpc containerPort: {{ .grpc }} - name: metrics containerPort: {{ .metrics }} - {{- end }} + {{- end }} - containerPort: 2222 name: ssh-admin volumeMounts: @@ -89,29 +81,26 @@ spec: - name: admin-keys mountPath: "/admin-keys" readOnly: true + {{- with (include "aserto-lib.grpcConfig" . | fromYaml).certSecret }} - name: grpc-certs mountPath: /grpc-certs - {{- if (include "aserto-lib.grpcConfig" . | fromYaml).certSecret }} readOnly: true - {{- end }} + {{- end }} + {{- with (include "aserto-lib.httpsConfig" . | fromYaml).certSecret }} - name: https-certs mountPath: /https-certs - {{- if (include "aserto-lib.httpsConfig" . | fromYaml).certSecret }} readOnly: true - {{- end }} + {{- end }} - {{- if and .Values.tenantDirectory.runService .Values.tenantDirectory.rootDS.grpcCertSecret }} + {{- if and .Values.tenantDirectory.runService (.Values.rootDS).caCertSecret }} - name: root-ds-grpc-certs mountPath: /root-ds-grpc-certs readOnly: true - {{- end }} + {{- end }} env: - {{- with (include "aserto-lib.rootDirectoryApiKey" . | fromYaml) }} + {{- with include "directory.rootApiKeyEnv" . }} - name: DIRECTORY_ROOT_DS_CLIENT_API_KEY - valueFrom: - secretKeyRef: - name: {{ .secretName }} - key: {{ .secretKey }} + {{- . | nindent 14 }} {{- end }} {{- if .Values.tenantDirectory.runService }} diff --git a/charts/directory/templates/root_key.yaml b/charts/directory/templates/root_key.yaml new file mode 100644 index 0000000..2032552 --- /dev/null +++ b/charts/directory/templates/root_key.yaml @@ -0,0 +1,22 @@ +{{- if .Values.rootDirectory.runService }} +{{- $cfg := include "aserto-lib.rootClientCfg" . | fromYaml -}} +{{- $secretName := ($cfg.apiKeySecret).name | default "root-ds-keys" -}} +{{- $secretKey := ($cfg.apiKeySecret).key | default "api-key" -}} + +{{- $apiKey := $cfg.apiKey -}} +{{- if empty $apiKey -}} + {{- $current := (lookup "v1" "Secret" $.Release.Namespace $secretName).data }} + {{- if $current }} + {{- $apiKey = get $current $secretKey }} + {{- else -}} + {{- $apiKey = randAlphaNum 32 | b64enc }} + {{- end }} +{{- end -}} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} +data: + {{ $secretKey }}: {{ $apiKey }} +{{- end }} diff --git a/charts/directory/values.yaml b/charts/directory/values.yaml index d5aa6de..3836d0d 100644 --- a/charts/directory/values.yaml +++ b/charts/directory/values.yaml @@ -54,9 +54,6 @@ rootDirectory: tenantDirectory: runService: true - rootDS: - grpcCertSecret: "" - database: host: "" port: 5432 @@ -79,6 +76,28 @@ tenantDirectory: conn_max_lifetime: 5 query_timeout_seconds: 5 +# rootDS: +# # Address and port of the root directory's gRPC service. +# # Default: directory..svc.cluster.local:8282 +# address: "" +# # [Optional] API key for the remote directory +# apiKey: "" +# # [Optional] Kubernetes secret containing the API key for the remote directory +# apiKeySecret: +# # Secret name +# name: "" +# # Secret key +# key: "api-key" +# # [Optional] Kubernetes secret containing the CA certificate of the root directory. +# caCertSecret: +# name: "" +# key: "" +# # Skip verification of remote TLS certificate +# noVerify: false +# # Connect over a plain-text connection. +# # INSECURE: credentials are sent unencrypted within the cluster. +# noTLS: false + # Optional: list of predefined tenants. tenants: # - name: my-tenant @@ -95,12 +114,20 @@ tenants: # readerKey: reader -# Set the service log level (trace/debug/info/warn/error) +# Set the service log level (trace/debug/info/warn/error). +# The default is 'info'. # logLevel: info -# Set the service's gRPC connection timeout (in seconds) +# gRPC configuration. grpc: + # Set the service's gRPC connection timeout (in seconds) connectionTimeoutSec: 2 + # Optional: name of a Kubernetes secret of type kubernetes.io/tls. + # If specified, the gRPC server uses TLS with the provided certificate. + # Otherwise, ther server runs without TLS. + # Note: clients will not send credentials without TLS. If the service + # runs without it, you must configure TLS at the ingress or gateway. + certSecret: # https: # allowed_origins: diff --git a/charts/discovery/templates/deployment.yaml b/charts/discovery/templates/deployment.yaml index 6fbadeb..8b43179 100644 --- a/charts/discovery/templates/deployment.yaml +++ b/charts/discovery/templates/deployment.yaml @@ -50,11 +50,11 @@ spec: {{- end }} {{- end }} - {{- with (include "aserto-lib.rootDirectoryCfg" . | fromYaml) }} - {{- if .grpcCertSecret }} + {{- with (include "aserto-lib.rootClientCfg" . | fromYaml) }} + {{- if .caCertSecret }} - name: root-ds-grpc-certs secret: - secretName: {{ .grpcCertSecret }} + secretName: {{ .caCertSecret }} items: - key: ca.crt path: ca.crt @@ -90,7 +90,7 @@ spec: readOnly: true {{- end }} - {{- if (include "aserto-lib.rootDirectoryCfg" . | fromYaml).grpcCertSecret }} + {{- if (include "aserto-lib.rootClientCfg" . | fromYaml).caCertSecret }} - name: root-ds-grpc-certs mountPath: /root-ds-grpc-certs readOnly: true @@ -104,7 +104,7 @@ spec: key: {{ .secretKey }} {{- end }} - {{- with (include "aserto-lib.rootDirectoryApiKey" . | fromYaml) }} + {{- with (include "aserto-lib.rootApiKeyEnv" . | fromYaml) }} - name: DISCOVERY_DS0_API_KEY valueFrom: secretKeyRef: diff --git a/charts/scim/templates/deployment.yaml b/charts/scim/templates/deployment.yaml index 1d78b5c..40b0789 100644 --- a/charts/scim/templates/deployment.yaml +++ b/charts/scim/templates/deployment.yaml @@ -41,11 +41,11 @@ spec: {{- end }} {{- end }} - {{- with (include "aserto-lib.rootDirectoryCfg" . | fromYaml) }} - {{- if .grpcCertSecret }} + {{- with (include "aserto-lib.rootClientCfg" . | fromYaml) }} + {{- if .caCertSecret }} - name: root-ds-grpc-certs secret: - secretName: {{ .grpcCertSecret }} + secretName: {{ .caCertSecret }} items: - key: ca.crt path: ca.crt @@ -72,7 +72,7 @@ spec: readOnly: true {{- end }} - {{- if (include "aserto-lib.rootDirectoryCfg" . | fromYaml).grpcCertSecret }} + {{- if (include "aserto-lib.rootClientCfg" . | fromYaml).caCertSecret }} - name: root-ds-grpc-certs mountPath: /root-ds-grpc-certs readOnly: true @@ -88,7 +88,7 @@ spec: secretKeyRef: name: {{ include "scim.auth.secretName" . }} key: {{ include "scim.auth.secretKey" . }} - {{- with (include "aserto-lib.rootDirectoryApiKey" . | fromYaml) }} + {{- with (include "aserto-lib.rootApiKeyEnv" . | fromYaml) }} - name: ASERTO_SCIM_DIRECTORY_API_KEY valueFrom: secretKeyRef: diff --git a/charts/scim/values.yaml b/charts/scim/values.yaml index fa41122..9429da7 100644 --- a/charts/scim/values.yaml +++ b/charts/scim/values.yaml @@ -19,7 +19,7 @@ global: rootDirectory: address: "" - grpcCertSecret: "" + caCertSecret: "" port: 8080 @@ -44,7 +44,7 @@ port: 8080 rootDirectory: disableTLSVerification: false - grpcCertSecret: "" + caCertSecret: "" # address: "{{ .Release.Name }}-aserto-directory.aserto.svc.cluster.local:8282" diff --git a/charts/topaz/.helmignore b/charts/topaz/.helmignore index 18607ed..c1c4640 100644 --- a/charts/topaz/.helmignore +++ b/charts/topaz/.helmignore @@ -23,3 +23,4 @@ .vscode/ build/ +ci/ diff --git a/test/directory/directory.values.yaml b/test/directory/directory.values.yaml new file mode 100644 index 0000000..f2c03c7 --- /dev/null +++ b/test/directory/directory.values.yaml @@ -0,0 +1,38 @@ +--- +image: + tag: 0.33.2-cac7c06f-arm64 + +imagePullSecrets: + - name: ghcr-creds + +rootDirectory: + database: + host: host.k3d.internal + dbName: test-root-ds + sslMode: disable + admin: + credentialsSecret: pg-credentials + reader: + credentialsSecret: pg-root-reader-credentials + +tenantDirectory: + database: + host: host.k3d.internal + dbName: test-ds + sslMode: disable + admin: + credentialsSecret: pg-credentials + reader: + credentialsSecret: pg-tenant-reader-credentials + +cache: + sizeMB: 100 + +sshAdminKeys: | + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMEAOAWPotrmDRxyLRSswrectb8NJ4pH/i09N91Hd4Lj ronen@hilewi.cz + +tenants: + - name: test + id: 3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1 + keysSecret: + name: test-tenant-keys diff --git a/test/directory/tests.yaml b/test/directory/tests.yaml new file mode 100644 index 0000000..5fc89ce --- /dev/null +++ b/test/directory/tests.yaml @@ -0,0 +1,23 @@ +--- +tests: + - name: directory-no-tls + pull_secret: $GITHUB_TOKEN + secrets: + - name: pg-credentials + values: + username: postgres + password: $POSTGRES_PASSWORD + - name: test-tenant-keys + values: + reader: apikey_tenant_reader + writer: apikey_tenant_writer + deployments: + - chart: directory + values: directory.values.yaml + ports: + - 2222 + - 8282 + run: + - "ssh -p 2222 localhost provision root-keys" + - "ssh -p 2222 localhost provision tenant test --id 3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1" + - "topaz ds get manifest -H localhost:8282 --tenant-id 3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1 -k {read_key} --stdout --plaintext" diff --git a/tools/k3test/.python-version b/tools/k3test/.python-version new file mode 100644 index 0000000..24ee5b1 --- /dev/null +++ b/tools/k3test/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/tools/k3test/README.md b/tools/k3test/README.md new file mode 100644 index 0000000..e69de29 diff --git a/tools/k3test/k3test.py b/tools/k3test/k3test.py new file mode 100755 index 0000000..d101c4d --- /dev/null +++ b/tools/k3test/k3test.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +"""Test Helm Charts in k3s""" + +import logging +import random +import signal +import string +import subprocess +import time +from contextlib import contextmanager +from functools import lru_cache +from typing import Iterator, Mapping + +from kubernetes import client, config +from kubernetes.client.rest import ApiException + +logger = logging.getLogger("k3stest") + +TOPAZ = "~/aserto-dev/topaz/dist/topaz_darwin_arm64/topaz" +TENANT_ID = "3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1" +TENANT_NAME = "test" + + +def main(): + init_logger(logging.DEBUG) + config.load_kube_config() + + read_key, write_key = keygen(), keygen() + + with new_namespace("directory") as ns: + ns.run( + "create", + "secret", + "docker-registry", + "ghcr-creds", + "--docker-server=https://ghcr.io", + "--docker-username=gh_user", + "--docker-password=$GITHUB_TOKEN", + ) + + ns.run( + "create", + "secret", + "generic", + "pg-credentials", + "--from-literal=password=", + "--from-literal=username=postgres", + ) + + ns.run( + "create", + "secret", + "generic", + f"{TENANT_NAME}-tenant-keys", + f"--from-literal=reader={read_key}", + f"--from-literal=writer={write_key}", + ) + + ns.helm( + "install", + "directory", + "charts/directory", + "-f", + "test/directory/directory.values.yaml", + ) + + ns.wait(ns.svc_pod("directory")) + + with ns.forward("directory", {8282: 8282, 2222: 2222}): + subprocess.run( + args="ssh -p 2222 localhost provision root-keys", + shell=True, + check=True, + ) + + subprocess.run( + args=f"ssh -p 2222 localhost provision tenant {TENANT_NAME} --id {TENANT_ID}", + shell=True, + check=True, + ) + + subprocess.run( + args=f"{TOPAZ} ds get manifest -H localhost:8282 --tenant-id {TENANT_ID} " + f"--api-key {read_key} --stdout --plaintext", + shell=True, + check=True, + ) + + +@contextmanager +def new_namespace(name: str) -> Iterator["Namespace"]: + ns = Namespace(name) + if ns.exists(): + logger.info("namespace '%s' already exists. deleting it...", name) + ns.delete() + + ns.create() + + yield ns + + +class Namespace: + def __init__(self, namespace: str): + self.namespace = namespace + self.cluster = client.CoreV1Api() + + def create(self): + self.kubectl("create", "namespace", self.namespace) + + def delete(self): + self.kubectl("delete", "namespace", self.namespace, "--wait=true") + + def exists(self): + try: + self.cluster.read_namespace(self.namespace) + return True + except ApiException as e: + print("read ns error:", e) + + return False + + def run(self, *args): + self.kubectl(*args, "-n", self.namespace) + + def wait(self, pod: str): + self.run( + "wait", + "--for=condition=ready", + "--timeout=30s", + "pod", + pod, + ) + + @contextmanager + def forward(self, svc: str, ports: Mapping[int, int]): + pod = self.svc_pod(svc) + port_mapping = tuple(f"{k}:{v}" for k, v in ports.items()) + logger.debug("port-mapping: %s", port_mapping) + args = " ".join( + ("kubectl", "-n", self.namespace, "port-forward", pod) + port_mapping + ) + logger.debug("port-forward: %s", args) + proc = subprocess.Popen( + args=args, + shell=True, + ) + + time.sleep(1.5) + + try: + yield proc + finally: + logger.debug("terminating port-forward") + proc.send_signal(signal.SIGINT) + proc.wait() + logger.debug("port-forward terminated") + + @lru_cache(maxsize=32) + def svc_pod(self, svc: str) -> str: + proc = self.kubectl( + "get", + "pods", + "--namespace", + "directory", + "-l", + f"app.kubernetes.io/name={svc},app.kubernetes.io/instance={svc}", + "-o", + "jsonpath='{.items[0].metadata.name}'", + capture_output=True, + ) + + if proc.returncode != 0: + proc.check_returncode() + + return proc.stdout.decode() + + def helm(self, *args, check=True): + args = ("helm", "-n", self.namespace) + args + return subprocess.run(args=" ".join(args), shell=True, check=check) + + @staticmethod + def kubectl(*args, check: bool = True, capture_output: bool = False): + args = ("kubectl",) + args + return subprocess.run( + args=" ".join(args), check=check, shell=True, capture_output=capture_output + ) + + +def keygen() -> str: + return "".join(random.choices(string.ascii_letters + string.digits, k=32)) + + +def init_logger(level=logging.INFO): + logger.setLevel(level) + + # create console handler and set level to debug + ch = logging.StreamHandler() + ch.setLevel(logging.DEBUG) + + # create formatter + formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") + + # add formatter to ch + ch.setFormatter(formatter) + + # add ch to logger + logger.addHandler(ch) + + +if __name__ == "__main__": + main() diff --git a/tools/k3test/pylintrc b/tools/k3test/pylintrc new file mode 100644 index 0000000..acafbb5 --- /dev/null +++ b/tools/k3test/pylintrc @@ -0,0 +1,651 @@ +[MAIN] + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Clear in-memory caches upon conclusion of linting. Useful if running pylint +# in a server-like mode. +clear-cache-post-run=no + +# Load and enable all available extensions. Use --list-extensions to see a list +# all available extensions. +#enable-all-extensions= + +# In error mode, messages with a category besides ERROR or FATAL are +# suppressed, and no reports are done by default. Error mode is compatible with +# disabling specific errors. +#errors-only= + +# Always return a 0 (non-error) status code, even if lint errors are found. +# This is primarily useful in continuous integration scripts. +#exit-zero= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. +extension-pkg-allow-list= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. (This is an alternative name to extension-pkg-allow-list +# for backward compatibility.) +extension-pkg-whitelist= + +# Return non-zero exit code if any of these messages/categories are detected, +# even if score is above --fail-under value. Syntax same as enable. Messages +# specified are enabled, while categories only check already-enabled messages. +fail-on= + +# Specify a score threshold under which the program will exit with error. +fail-under=10 + +# Interpret the stdin as a python script, whose filename needs to be passed as +# the module_or_package argument. +#from-stdin= + +# Files or directories to be skipped. They should be base names, not paths. +ignore=CVS + +# Add files or directories matching the regular expressions patterns to the +# ignore-list. The regex matches against paths and can be in Posix or Windows +# format. Because '\\' represents the directory delimiter on Windows systems, +# it can't be used as an escape character. +ignore-paths= + +# Files or directories matching the regular expression patterns are skipped. +# The regex matches against base names, not paths. The default value ignores +# Emacs file locks +ignore-patterns=^\.# + +# List of module names for which member attributes should not be checked and +# will not be imported (useful for modules/projects where namespaces are +# manipulated during runtime and thus existing member attributes cannot be +# deduced by static analysis). It supports qualified module names, as well as +# Unix pattern matching. +ignored-modules= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use, and will cap the count on Windows to +# avoid hangs. +jobs=1 + +# Control the amount of potential inferred values when inferring a single +# object. This can help the performance when dealing with large functions or +# complex, nested conditions. +limit-inference-results=100 + +# List of plugins (as comma separated values of python module names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Resolve imports to .pyi stubs if available. May reduce no-member messages and +# increase not-an-iterable messages. +prefer-stubs=no + +# Minimum Python version to use for version dependent checks. Will default to +# the version used to run pylint. +py-version=3.13 + +# Discover python modules and packages in the file system subtree. +recursive=no + +# Add paths to the list of the source roots. Supports globbing patterns. The +# source root is an absolute path or a path relative to the current working +# directory used to determine a package namespace for modules located under the +# source root. +source-roots= + +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages. +suggestion-mode=yes + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# In verbose mode, extra non-checker-related info will be displayed. +#verbose= + + +[BASIC] + +# Naming style matching correct argument names. +argument-naming-style=snake_case + +# Regular expression matching correct argument names. Overrides argument- +# naming-style. If left empty, argument names will be checked with the set +# naming style. +#argument-rgx= + +# Naming style matching correct attribute names. +attr-naming-style=snake_case + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style. If left empty, attribute names will be checked with the set naming +# style. +#attr-rgx= + +# Bad variable names which should always be refused, separated by a comma. +bad-names=foo, + bar, + baz, + toto, + tutu, + tata + +# Bad variable names regexes, separated by a comma. If names match any regex, +# they will always be refused +bad-names-rgxs= + +# Naming style matching correct class attribute names. +class-attribute-naming-style=any + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style. If left empty, class attribute names will be checked +# with the set naming style. +#class-attribute-rgx= + +# Naming style matching correct class constant names. +class-const-naming-style=UPPER_CASE + +# Regular expression matching correct class constant names. Overrides class- +# const-naming-style. If left empty, class constant names will be checked with +# the set naming style. +#class-const-rgx= + +# Naming style matching correct class names. +class-naming-style=PascalCase + +# Regular expression matching correct class names. Overrides class-naming- +# style. If left empty, class names will be checked with the set naming style. +#class-rgx= + +# Naming style matching correct constant names. +const-naming-style=UPPER_CASE + +# Regular expression matching correct constant names. Overrides const-naming- +# style. If left empty, constant names will be checked with the set naming +# style. +#const-rgx= + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming style matching correct function names. +function-naming-style=snake_case + +# Regular expression matching correct function names. Overrides function- +# naming-style. If left empty, function names will be checked with the set +# naming style. +#function-rgx= + +# Good variable names which should always be accepted, separated by a comma. +good-names=i, + j, + k, + ex, + Run, + _ + +# Good variable names regexes, separated by a comma. If names match any regex, +# they will always be accepted +good-names-rgxs= + +# Include a hint for the correct naming format with invalid-name. +include-naming-hint=no + +# Naming style matching correct inline iteration names. +inlinevar-naming-style=any + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style. If left empty, inline iteration names will be checked +# with the set naming style. +#inlinevar-rgx= + +# Naming style matching correct method names. +method-naming-style=snake_case + +# Regular expression matching correct method names. Overrides method-naming- +# style. If left empty, method names will be checked with the set naming style. +#method-rgx= + +# Naming style matching correct module names. +module-naming-style=snake_case + +# Regular expression matching correct module names. Overrides module-naming- +# style. If left empty, module names will be checked with the set naming style. +#module-rgx= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +# These decorators are taken in consideration only for invalid-name. +property-classes=abc.abstractproperty + +# Regular expression matching correct type alias names. If left empty, type +# alias names will be checked with the set naming style. +#typealias-rgx= + +# Regular expression matching correct type variable names. If left empty, type +# variable names will be checked with the set naming style. +#typevar-rgx= + +# Naming style matching correct variable names. +variable-naming-style=snake_case + +# Regular expression matching correct variable names. Overrides variable- +# naming-style. If left empty, variable names will be checked with the set +# naming style. +#variable-rgx= + + +[CLASSES] + +# Warn about protected attribute access inside special methods +check-protected-access-in-special-methods=no + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp, + asyncSetUp, + __post_init__ + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make,os._exit + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + + +[DESIGN] + +# List of regular expressions of class ancestor names to ignore when counting +# public methods (see R0903) +exclude-too-few-public-methods= + +# List of qualified class names to ignore when counting class parents (see +# R0901) +ignored-parents= + +# Maximum number of arguments for function / method. +max-args=5 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Maximum number of boolean expressions in an if statement (see R0916). +max-bool-expr=5 + +# Maximum number of branch for function / method body. +max-branches=12 + +# Maximum number of locals for function / method body. +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of positional arguments for function / method. +max-positional-arguments=5 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body. +max-returns=6 + +# Maximum number of statements in function / method body. +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when caught. +overgeneral-exceptions=builtins.BaseException,builtins.Exception + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module. +max-module-lines=1000 + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[IMPORTS] + +# List of modules that can be imported at any level, not just the top level +# one. +allow-any-import-level= + +# Allow explicit reexports by alias from a package __init__. +allow-reexport-from-package=no + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules= + +# Output a graph (.gv or any supported image format) of external dependencies +# to the given file (report RP0402 must not be disabled). +ext-import-graph= + +# Output a graph (.gv or any supported image format) of all (i.e. internal and +# external) dependencies to the given file (report RP0402 must not be +# disabled). +import-graph= + +# Output a graph (.gv or any supported image format) of internal dependencies +# to the given file (report RP0402 must not be disabled). +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + +# Couples of modules and preferred modules, separated by a comma. +preferred-modules= + + +[LOGGING] + +# The type of string formatting that logging methods do. `old` means using % +# formatting, `new` is for `{}` formatting. +logging-format-style=old + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, +# UNDEFINED. +confidence=HIGH, + CONTROL_FLOW, + INFERENCE, + INFERENCE_FAILURE, + UNDEFINED + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once). You can also use "--disable=all" to +# disable everything first and then re-enable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +disable=raw-checker-failed, + bad-inline-option, + locally-disabled, + file-ignored, + suppressed-message, + useless-suppression, + deprecated-pragma, + use-symbolic-message-instead, + use-implicit-booleaness-not-comparison-to-string, + use-implicit-booleaness-not-comparison-to-zero, + missing-module-docstring, + missing-function-docstring, + missing-class-docstring + + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable= + + +[METHOD_ARGS] + +# List of qualified names (i.e., library.method) which require a timeout +# parameter e.g. 'requests.api.get,requests.api.post' +timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME, + XXX, + TODO + +# Regular expression of note tags to take in consideration. +notes-rgx= + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +# Complete name of functions that never returns. When checking for +# inconsistent-return-statements if a never returning function is called then +# it will be considered as an explicit return statement and no message will be +# printed. +never-returning-functions=sys.exit,argparse.parse_error + +# Let 'consider-using-join' be raised when the separator to join on would be +# non-empty (resulting in expected fixes of the type: ``"- " + " - +# ".join(items)``) +suggest-join-with-non-empty-separator=yes + + +[REPORTS] + +# Python expression which should return a score less than or equal to 10. You +# have access to the variables 'fatal', 'error', 'warning', 'refactor', +# 'convention', and 'info' which contain the number of messages in each +# category, as well as 'statement' which is the total number of statements +# analyzed. This score is used by the global evaluation report (RP0004). +evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details. +msg-template= + +# Set the output format. Available formats are: text, parseable, colorized, +# json2 (improved json format), json (old json format) and msvs (visual +# studio). You can also give a reporter class, e.g. +# mypackage.mymodule.MyReporterClass. +#output-format= + +# Tells whether to display a full report or only the messages. +reports=no + +# Activate the evaluation score. +score=yes + + +[SIMILARITIES] + +# Comments are removed from the similarity computation +ignore-comments=yes + +# Docstrings are removed from the similarity computation +ignore-docstrings=yes + +# Imports are removed from the similarity computation +ignore-imports=yes + +# Signatures are removed from the similarity computation +ignore-signatures=yes + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 + +# Spelling dictionary name. No available dictionaries : You need to install +# both the python package and the system dependency for enchant to work. +spelling-dict= + +# List of comma separated words that should be considered directives if they +# appear at the beginning of a comment and should not be checked. +spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains the private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to the private dictionary (see the +# --spelling-private-dict-file option) instead of raising a message. +spelling-store-unknown-words=no + + +[STRING] + +# This flag controls whether inconsistent-quotes generates a warning when the +# character used as a quote delimiter is used inconsistently within a module. +check-quote-consistency=no + +# This flag controls whether the implicit-str-concat should generate a warning +# on implicit string concatenation in sequences defined over several lines. +check-str-concat-over-line-jumps=no + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of symbolic message names to ignore for Mixin members. +ignored-checks-for-mixins=no-member, + not-async-context-manager, + not-context-manager, + attribute-defined-outside-init + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + +# Regex pattern to define which classes are considered mixins. +mixin-class-rgx=.*[Mm]ixin + +# List of decorators that change the signature of a decorated function. +signature-mutators= + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid defining new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of names allowed to shadow builtins +allowed-redefined-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_, + _cb + +# A regular expression matching the name of dummy variables (i.e. expected to +# not be used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io diff --git a/tools/k3test/pyproject.toml b/tools/k3test/pyproject.toml new file mode 100644 index 0000000..ccc13bf --- /dev/null +++ b/tools/k3test/pyproject.toml @@ -0,0 +1,17 @@ +[project] +name = "k3test" +version = "0.1.0" +description = "Runs chart tests in k3s" +readme = "README.md" +requires-python = ">=3.13" +dependencies = [ + "kubernetes>=31.0.0", + "python-retry>=0.0.1", +] + +[dependency-groups] +dev = [ + "black>=24.10.0", + "kubernetes-typed>=18.20.2", + "pylint>=3.3.1", +] diff --git a/tools/k3test/uv.lock b/tools/k3test/uv.lock new file mode 100644 index 0000000..2702887 --- /dev/null +++ b/tools/k3test/uv.lock @@ -0,0 +1,487 @@ +version = 1 +requires-python = ">=3.13" + +[[package]] +name = "astroid" +version = "3.3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/1e/326fb1d3d83a3bb77c9f9be29d31f2901e35acb94b0605c3f2e5085047f9/astroid-3.3.5.tar.gz", hash = "sha256:5cfc40ae9f68311075d27ef68a4841bdc5cc7f6cf86671b49f00607d30188e2d", size = 397229 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/30/624365383fa4a40329c0f0bbbc151abc4a64e30dfc110fc8f6e2afcd02bb/astroid-3.3.5-py3-none-any.whl", hash = "sha256:a9d1c946ada25098d790e079ba2a1b112157278f3fb7e718ae6a9252f5835dc8", size = 274586 }, +] + +[[package]] +name = "attrs" +version = "24.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/0f/aafca9af9315aee06a89ffde799a10a582fe8de76c563ee80bbcdc08b3fb/attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", size = 792678 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/21/5b6702a7f963e95456c0de2d495f67bf5fd62840ac655dc451586d23d39a/attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2", size = 63001 }, +] + +[[package]] +name = "black" +version = "24.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d8/0d/cc2fb42b8c50d80143221515dd7e4766995bd07c56c9a3ed30baf080b6dc/black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875", size = 645813 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/a0/a993f58d4ecfba035e61fca4e9f64a2ecae838fc9f33ab798c62173ed75c/black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981", size = 1643986 }, + { url = "https://files.pythonhosted.org/packages/37/d5/602d0ef5dfcace3fb4f79c436762f130abd9ee8d950fa2abdbf8bbc555e0/black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b", size = 1448085 }, + { url = "https://files.pythonhosted.org/packages/47/6d/a3a239e938960df1a662b93d6230d4f3e9b4a22982d060fc38c42f45a56b/black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2", size = 1760928 }, + { url = "https://files.pythonhosted.org/packages/dd/cf/af018e13b0eddfb434df4d9cd1b2b7892bab119f7a20123e93f6910982e8/black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b", size = 1436875 }, + { url = "https://files.pythonhosted.org/packages/8d/a7/4b27c50537ebca8bec139b872861f9d2bf501c5ec51fcf897cb924d9e264/black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d", size = 206898 }, +] + +[[package]] +name = "cachetools" +version = "5.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/38/a0f315319737ecf45b4319a8cd1f3a908e29d9277b46942263292115eee7/cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a", size = 27661 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/07/14f8ad37f2d12a5ce41206c21820d8cb6561b728e51fad4530dff0552a67/cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292", size = 9524 }, +] + +[[package]] +name = "certifi" +version = "2024.8.30" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/4f/e1808dc01273379acc506d18f1504eb2d299bd4131743b9fc54d7be4df1e/charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", size = 106620 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/89/68a4c86f1a0002810a27f12e9a7b22feb198c59b2f05231349fbce5c06f4/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", size = 194617 }, + { url = "https://files.pythonhosted.org/packages/4f/cd/8947fe425e2ab0aa57aceb7807af13a0e4162cd21eee42ef5b053447edf5/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", size = 125310 }, + { url = "https://files.pythonhosted.org/packages/5b/f0/b5263e8668a4ee9becc2b451ed909e9c27058337fda5b8c49588183c267a/charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", size = 119126 }, + { url = "https://files.pythonhosted.org/packages/ff/6e/e445afe4f7fda27a533f3234b627b3e515a1b9429bc981c9a5e2aa5d97b6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", size = 139342 }, + { url = "https://files.pythonhosted.org/packages/a1/b2/4af9993b532d93270538ad4926c8e37dc29f2111c36f9c629840c57cd9b3/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", size = 149383 }, + { url = "https://files.pythonhosted.org/packages/fb/6f/4e78c3b97686b871db9be6f31d64e9264e889f8c9d7ab33c771f847f79b7/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", size = 142214 }, + { url = "https://files.pythonhosted.org/packages/2b/c9/1c8fe3ce05d30c87eff498592c89015b19fade13df42850aafae09e94f35/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", size = 144104 }, + { url = "https://files.pythonhosted.org/packages/ee/68/efad5dcb306bf37db7db338338e7bb8ebd8cf38ee5bbd5ceaaaa46f257e6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", size = 146255 }, + { url = "https://files.pythonhosted.org/packages/0c/75/1ed813c3ffd200b1f3e71121c95da3f79e6d2a96120163443b3ad1057505/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", size = 140251 }, + { url = "https://files.pythonhosted.org/packages/7d/0d/6f32255c1979653b448d3c709583557a4d24ff97ac4f3a5be156b2e6a210/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", size = 148474 }, + { url = "https://files.pythonhosted.org/packages/ac/a0/c1b5298de4670d997101fef95b97ac440e8c8d8b4efa5a4d1ef44af82f0d/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", size = 151849 }, + { url = "https://files.pythonhosted.org/packages/04/4f/b3961ba0c664989ba63e30595a3ed0875d6790ff26671e2aae2fdc28a399/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", size = 149781 }, + { url = "https://files.pythonhosted.org/packages/d8/90/6af4cd042066a4adad58ae25648a12c09c879efa4849c705719ba1b23d8c/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482", size = 144970 }, + { url = "https://files.pythonhosted.org/packages/cc/67/e5e7e0cbfefc4ca79025238b43cdf8a2037854195b37d6417f3d0895c4c2/charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", size = 94973 }, + { url = "https://files.pythonhosted.org/packages/65/97/fc9bbc54ee13d33dc54a7fcf17b26368b18505500fc01e228c27b5222d80/charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", size = 102308 }, + { url = "https://files.pythonhosted.org/packages/bf/9b/08c0432272d77b04803958a4598a51e2a4b51c06640af8b8f0f908c18bf2/charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", size = 49446 }, +] + +[[package]] +name = "click" +version = "8.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "platform_system == 'Windows'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "dill" +version = "0.3.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/70/43/86fe3f9e130c4137b0f1b50784dd70a5087b911fe07fa81e53e0c4c47fea/dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c", size = 187000 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/d1/e73b6ad76f0b1fb7f23c35c6d95dbc506a9c8804f43dda8cb5b0fa6331fd/dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a", size = 119418 }, +] + +[[package]] +name = "durationpy" +version = "0.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/31/e9/f49c4e7fccb77fa5c43c2480e09a857a78b41e7331a75e128ed5df45c56b/durationpy-0.9.tar.gz", hash = "sha256:fd3feb0a69a0057d582ef643c355c40d2fa1c942191f914d12203b1a01ac722a", size = 3186 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/a3/ac312faeceffd2d8f86bc6dcb5c401188ba5a01bc88e69bed97578a0dfcd/durationpy-0.9-py3-none-any.whl", hash = "sha256:e65359a7af5cedad07fb77a2dd3f390f8eb0b74cb845589fa6c057086834dd38", size = 3461 }, +] + +[[package]] +name = "google-auth" +version = "2.36.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "pyasn1-modules" }, + { name = "rsa" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/71/4c5387d8a3e46e3526a8190ae396659484377a73b33030614dd3b28e7ded/google_auth-2.36.0.tar.gz", hash = "sha256:545e9618f2df0bcbb7dcbc45a546485b1212624716975a1ea5ae8149ce769ab1", size = 268336 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/9a/3d5087d27865c2f0431b942b5c4500b7d1b744dd3262fdc973a4c39d099e/google_auth-2.36.0-py2.py3-none-any.whl", hash = "sha256:51a15d47028b66fd36e5c64a82d2d57480075bccc7da37cde257fc94177a61fb", size = 209519 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "isort" +version = "5.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/f9/c1eb8635a24e87ade2efce21e3ce8cd6b8630bb685ddc9cdaca1349b2eb5/isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", size = 175303 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/b3/8def84f539e7d2289a02f0524b944b15d7c75dab7628bedf1c4f0992029c/isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6", size = 92310 }, +] + +[[package]] +name = "jsonschema" +version = "4.17.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "pyrsistent" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/3d/ca032d5ac064dff543aa13c984737795ac81abc9fb130cd2fcff17cfabc7/jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d", size = 297785 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/97/c698bd9350f307daad79dd740806e1a59becd693bd11443a0f531e3229b3/jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6", size = 90379 }, +] + +[[package]] +name = "jsonschema-typed-v2" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonschema" }, + { name = "mypy" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/7a/2957ce3853329c6cfee06bf32cf98763d93c7a3b73a0c145a9eae5f7485a/jsonschema_typed_v2-0.8.0-py3-none-any.whl", hash = "sha256:f1461a31e8c4a36f22a2384032492b51b3079af1995b289cc81efbdcbce3e5dc", size = 11982 }, +] + +[[package]] +name = "k3test" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "kubernetes" }, + { name = "python-retry" }, +] + +[package.dev-dependencies] +dev = [ + { name = "black" }, + { name = "kubernetes-typed" }, + { name = "pylint" }, +] + +[package.metadata] +requires-dist = [ + { name = "kubernetes", specifier = ">=31.0.0" }, + { name = "python-retry", specifier = ">=0.0.1" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "black", specifier = ">=24.10.0" }, + { name = "kubernetes-typed", specifier = ">=18.20.2" }, + { name = "pylint", specifier = ">=3.3.1" }, +] + +[[package]] +name = "kubernetes" +version = "31.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "durationpy" }, + { name = "google-auth" }, + { name = "oauthlib" }, + { name = "python-dateutil" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "requests-oauthlib" }, + { name = "six" }, + { name = "urllib3" }, + { name = "websocket-client" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7e/bd/ffcd3104155b467347cd9b3a64eb24182e459579845196b3a200569c8912/kubernetes-31.0.0.tar.gz", hash = "sha256:28945de906c8c259c1ebe62703b56a03b714049372196f854105afe4e6d014c0", size = 916096 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/a8/17f5e28cecdbd6d48127c22abdb794740803491f422a11905c4569d8e139/kubernetes-31.0.0-py2.py3-none-any.whl", hash = "sha256:bf141e2d380c8520eada8b351f4e319ffee9636328c137aa432bc486ca1200e1", size = 1857013 }, +] + +[[package]] +name = "kubernetes-typed" +version = "18.20.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonschema" }, + { name = "jsonschema-typed-v2" }, + { name = "mypy" }, + { name = "mypy-extensions" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/37/4c/0f49075f2ca5f4bd2c5ec05f0ca37c1649076a3bd93ae13f593f95fb31ba/kubernetes_typed-18.20.2.tar.gz", hash = "sha256:5c2fcf5de2b3192133a4cba9d0d8f7187e4ccc7f7ad6d3a17dfe67ea5b90b101", size = 199120 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/d5/d13cf0211d950b19b89683e65829b4da9c8b2eed0b71afec564f240e3bcb/kubernetes_typed-18.20.2-py3-none-any.whl", hash = "sha256:dff59feb21c092a83ac5b0e65b9cc5f9d00caa95833e0c486692dedfc1276a13", size = 757856 }, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350 }, +] + +[[package]] +name = "mypy" +version = "1.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e8/21/7e9e523537991d145ab8a0a2fd98548d67646dc2aaaf6091c31ad883e7c1/mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e", size = 3152532 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/bb/ab4cfdc562cad80418f077d8be9b4491ee4fb257440da951b85cbb0a639e/mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7", size = 11069721 }, + { url = "https://files.pythonhosted.org/packages/59/3b/a393b1607cb749ea2c621def5ba8c58308ff05e30d9dbdc7c15028bca111/mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62", size = 10063996 }, + { url = "https://files.pythonhosted.org/packages/d1/1f/6b76be289a5a521bb1caedc1f08e76ff17ab59061007f201a8a18cc514d1/mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8", size = 12584043 }, + { url = "https://files.pythonhosted.org/packages/a6/83/5a85c9a5976c6f96e3a5a7591aa28b4a6ca3a07e9e5ba0cec090c8b596d6/mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7", size = 13036996 }, + { url = "https://files.pythonhosted.org/packages/b4/59/c39a6f752f1f893fccbcf1bdd2aca67c79c842402b5283563d006a67cf76/mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc", size = 9737709 }, + { url = "https://files.pythonhosted.org/packages/3b/86/72ce7f57431d87a7ff17d442f521146a6585019eb8f4f31b7c02801f78ad/mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a", size = 2647043 }, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, +] + +[[package]] +name = "oauthlib" +version = "3.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/fa/fbf4001037904031639e6bfbfc02badfc7e12f137a8afa254df6c4c8a670/oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918", size = 177352 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/80/cab10959dc1faead58dc8384a781dfbf93cb4d33d50988f7a69f1b7c9bbe/oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", size = 151688 }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, +] + +[[package]] +name = "pyasn1" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135 }, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/67/6afbf0d507f73c32d21084a79946bfcfca5fbc62a72057e9c23797a737c9/pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c", size = 310028 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/89/bc88a6711935ba795a679ea6ebee07e128050d6382eaa35a0a47c8032bdc/pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd", size = 181537 }, +] + +[[package]] +name = "pylint" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astroid" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "dill" }, + { name = "isort" }, + { name = "mccabe" }, + { name = "platformdirs" }, + { name = "tomlkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/3a/13e90e29777e695d90f422cf4fadb81c999e4755a9089838561bd0590cac/pylint-3.3.1.tar.gz", hash = "sha256:9f3dcc87b1203e612b78d91a896407787e708b3f189b5fa0b307712d49ff0c6e", size = 1516703 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/11/4a3f814eee14593f3cfcf7046bc765bf1646d5c88132c08c45310fc7d85f/pylint-3.3.1-py3-none-any.whl", hash = "sha256:2f846a466dd023513240bc140ad2dd73bfc080a5d85a710afdb728c420a5a2b9", size = 521768 }, +] + +[[package]] +name = "pyrsistent" +version = "0.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/3a/5031723c09068e9c8c2f0bc25c3a9245f2b1d1aea8396c787a408f2b95ca/pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4", size = 103642 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/88/0acd180010aaed4987c85700b7cc17f9505f3edb4e5873e4dc67f613e338/pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b", size = 58106 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "python-retry" +version = "0.0.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/16/442a6ef19877c8f344cdf42219abc735b41187b1904c9f7a2b9a3d823ce1/python_retry-0.0.1-py3-none-any.whl", hash = "sha256:4e1678a05721a7708639adf0b10d4a07fb719906a310514b393a2c83c4b9964d", size = 4229 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "requests-oauthlib" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "oauthlib" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179 }, +] + +[[package]] +name = "rsa" +version = "4.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/65/7d973b89c4d2351d7fb232c2e452547ddfa243e93131e7cfa766da627b52/rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21", size = 29711 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/97/fa78e3d2f65c02c8e1268b9aba606569fe97f6c8f7c2d74394553347c145/rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", size = 34315 }, +] + +[[package]] +name = "six" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, +] + +[[package]] +name = "tomlkit" +version = "0.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/09/a439bec5888f00a54b8b9f05fa94d7f901d6735ef4e55dcec9bc37b5d8fa/tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79", size = 192885 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/b6/a447b5e4ec71e13871be01ba81f5dfc9d0af7e473da256ff46bc0e24026f/tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", size = 37955 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + +[[package]] +name = "urllib3" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", size = 300677 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338 }, +] + +[[package]] +name = "websocket-client" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da", size = 54648 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826 }, +] From da94ca1611e7265c26aeda68258ea671fdaebda4 Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Thu, 21 Nov 2024 10:40:19 -0500 Subject: [PATCH 04/22] Test harness --- .gitignore | 3 + test/directory/directory.values.yaml | 2 +- test/directory/tests.yaml | 20 +- tools/k3test/k3test.py | 282 +++++++++++---------------- tools/k3test/model.py | 29 +++ tools/k3test/namespace.py | 117 +++++++++++ tools/k3test/pyproject.toml | 6 +- tools/k3test/uv.lock | 108 +++++++++- 8 files changed, 384 insertions(+), 183 deletions(-) create mode 100644 tools/k3test/model.py create mode 100644 tools/k3test/namespace.py diff --git a/.gitignore b/.gitignore index 6e36ffb..fe60ce6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ .ext/ /charts/*/build/ /charts/*/charts/ + +# python +**/__pycache__/ diff --git a/test/directory/directory.values.yaml b/test/directory/directory.values.yaml index f2c03c7..1a971e8 100644 --- a/test/directory/directory.values.yaml +++ b/test/directory/directory.values.yaml @@ -1,6 +1,6 @@ --- image: - tag: 0.33.2-cac7c06f-arm64 + tag: 0.33.2-3e32438c-arm64 imagePullSecrets: - name: ghcr-creds diff --git a/test/directory/tests.yaml b/test/directory/tests.yaml index 5fc89ce..deb083d 100644 --- a/test/directory/tests.yaml +++ b/test/directory/tests.yaml @@ -15,9 +15,19 @@ tests: - chart: directory values: directory.values.yaml ports: - - 2222 - - 8282 + 2222: 2222 + 8282: 8282 run: - - "ssh -p 2222 localhost provision root-keys" - - "ssh -p 2222 localhost provision tenant test --id 3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1" - - "topaz ds get manifest -H localhost:8282 --tenant-id 3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1 -k {read_key} --stdout --plaintext" + - > + ssh -p 2222 -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR + localhost provision root-keys + - > + ssh -p 2222 -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR + localhost provision tenant test --id 3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1 + - > + ${TOPAZ:-topaz} ds get manifest -H localhost:8282 --tenant-id 3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1 + -k apikey_tenant_reader --stdout --plaintext + cleanup: + - > + ssh -p 2222 -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR + localhost provision tenant test --id 3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1 --delete diff --git a/tools/k3test/k3test.py b/tools/k3test/k3test.py index d101c4d..960aea9 100755 --- a/tools/k3test/k3test.py +++ b/tools/k3test/k3test.py @@ -2,192 +2,142 @@ """Test Helm Charts in k3s""" import logging -import random -import signal -import string import subprocess -import time -from contextlib import contextmanager -from functools import lru_cache -from typing import Iterator, Mapping +from contextlib import contextmanager, ExitStack +from os import path +from typing import Iterator -from kubernetes import client, config -from kubernetes.client.rest import ApiException +import click +import git +import yaml -logger = logging.getLogger("k3stest") - -TOPAZ = "~/aserto-dev/topaz/dist/topaz_darwin_arm64/topaz" -TENANT_ID = "3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1" -TENANT_NAME = "test" - - -def main(): - init_logger(logging.DEBUG) - config.load_kube_config() +from kubernetes import config - read_key, write_key = keygen(), keygen() - - with new_namespace("directory") as ns: - ns.run( - "create", - "secret", - "docker-registry", - "ghcr-creds", - "--docker-server=https://ghcr.io", - "--docker-username=gh_user", - "--docker-password=$GITHUB_TOKEN", - ) - - ns.run( - "create", - "secret", - "generic", - "pg-credentials", - "--from-literal=password=", - "--from-literal=username=postgres", - ) - - ns.run( - "create", - "secret", - "generic", - f"{TENANT_NAME}-tenant-keys", - f"--from-literal=reader={read_key}", - f"--from-literal=writer={write_key}", - ) +from model import Spec, Test +from namespace import Namespace - ns.helm( - "install", - "directory", - "charts/directory", - "-f", - "test/directory/directory.values.yaml", - ) - - ns.wait(ns.svc_pod("directory")) - - with ns.forward("directory", {8282: 8282, 2222: 2222}): - subprocess.run( - args="ssh -p 2222 localhost provision root-keys", - shell=True, - check=True, - ) - - subprocess.run( - args=f"ssh -p 2222 localhost provision tenant {TENANT_NAME} --id {TENANT_ID}", - shell=True, - check=True, - ) +logger = logging.getLogger("k3stest") - subprocess.run( - args=f"{TOPAZ} ds get manifest -H localhost:8282 --tenant-id {TENANT_ID} " - f"--api-key {read_key} --stdout --plaintext", - shell=True, - check=True, +COLOR_HARNESS = "blue" +COLOR_STEP = "magenta" +COLOR_CLEANUP = "cyan" + + +class Runner: + def __init__(self, test: Test, spec_path: str): + self.test = test + self.spec_path = spec_path + self.git_root = git_root(__file__) + + def run(self): + with self.new_namespace(self.test.name) as ns: + self.set_image_pull_secret(ns) + + for secret in self.test.secrets: + click.echo( + f"πŸ”’ {click.style("Creating secret:", fg=COLOR_HARNESS)} {secret.name}" + ) + ns.create_secret(secret) + + for deployment in self.test.deployments: + click.echo( + f"πŸ—ΊοΈ {click.style("Installing chart:", fg=COLOR_HARNESS)} {deployment.chart}" + ) + ns.helm( + "install", + deployment.chart, + path.join(self.git_root, "charts", deployment.chart), + "-f", + path.join(self.spec_path, deployment.values), + ) + + for deployment in self.test.deployments: + ns.wait(ns.svc_pod(deployment.chart)) + + with ExitStack() as stack: + for deployment in self.test.deployments: + click.echo( + f"πŸ”€ {click.style("Forwarding port(s):", fg=COLOR_HARNESS)} {deployment.chart} - {deployment.ports}" + ) + stack.enter_context(ns.forward(deployment.chart, deployment.ports)) + + click.echo(f"\nβœ… Deployment complete.\n") + try: + self.execute_steps() + finally: + self.execute_cleanup() + + def execute_steps(self): + click.echo(f"πŸƒ {click.style("Running tests", fg=COLOR_HARNESS)}\n") + for step in self.test.run: + click.echo(f"πŸ§ͺ {click.style(step, fg=COLOR_STEP)}") + self.subprocess(step) + + def execute_cleanup(self): + click.echo(f"\n🧹 {click.style("Running cleanup", fg=COLOR_HARNESS)}\n") + for step in self.test.cleanup: + click.echo(f"🧽 {click.style(step, fg=COLOR_STEP)}") + self.subprocess(step, check=False) + + def set_image_pull_secret(self, ns: Namespace): + if self.test.pull_secret: + ns.kubectl( + "create", + "secret", + "docker-registry", + "ghcr-creds", + "--docker-server=https://ghcr.io", + "--docker-username=gh_user", + f"--docker-password={path.expandvars(self.test.pull_secret)}", ) + @staticmethod + def subprocess(args: str, check=True): + subprocess.run( + args=args, + shell=True, + check=check, + ) -@contextmanager -def new_namespace(name: str) -> Iterator["Namespace"]: - ns = Namespace(name) - if ns.exists(): - logger.info("namespace '%s' already exists. deleting it...", name) - ns.delete() - - ns.create() - - yield ns - - -class Namespace: - def __init__(self, namespace: str): - self.namespace = namespace - self.cluster = client.CoreV1Api() + @staticmethod + @contextmanager + def new_namespace(name: str) -> Iterator["Namespace"]: + ns = Namespace(name) + if ns.ns_exists(): + logger.info("namespace '%s' already exists. deleting it...", name) + ns.delete_ns() - def create(self): - self.kubectl("create", "namespace", self.namespace) + click.echo(f"🐳 {click.style("Creating namespace:", fg=COLOR_HARNESS)} {name}") + ns.create_ns() - def delete(self): - self.kubectl("delete", "namespace", self.namespace, "--wait=true") + yield ns - def exists(self): - try: - self.cluster.read_namespace(self.namespace) - return True - except ApiException as e: - print("read ns error:", e) + click.echo(f"\n🐳 {click.style("Deleting namespace:", fg=COLOR_HARNESS)} {name}") + ns.delete_ns() - return False - def run(self, *args): - self.kubectl(*args, "-n", self.namespace) +@click.command() +@click.argument("specfile", type=click.File()) +def main(specfile): + """Run tests in a kubernetes cluster. - def wait(self, pod: str): - self.run( - "wait", - "--for=condition=ready", - "--timeout=30s", - "pod", - pod, - ) + SPECFILE: path to a YAML file with test definitions. + """ - @contextmanager - def forward(self, svc: str, ports: Mapping[int, int]): - pod = self.svc_pod(svc) - port_mapping = tuple(f"{k}:{v}" for k, v in ports.items()) - logger.debug("port-mapping: %s", port_mapping) - args = " ".join( - ("kubectl", "-n", self.namespace, "port-forward", pod) + port_mapping - ) - logger.debug("port-forward: %s", args) - proc = subprocess.Popen( - args=args, - shell=True, - ) - - time.sleep(1.5) - - try: - yield proc - finally: - logger.debug("terminating port-forward") - proc.send_signal(signal.SIGINT) - proc.wait() - logger.debug("port-forward terminated") - - @lru_cache(maxsize=32) - def svc_pod(self, svc: str) -> str: - proc = self.kubectl( - "get", - "pods", - "--namespace", - "directory", - "-l", - f"app.kubernetes.io/name={svc},app.kubernetes.io/instance={svc}", - "-o", - "jsonpath='{.items[0].metadata.name}'", - capture_output=True, - ) - - if proc.returncode != 0: - proc.check_returncode() - - return proc.stdout.decode() + init_logger(logging.DEBUG) + config.load_kube_config() - def helm(self, *args, check=True): - args = ("helm", "-n", self.namespace) + args - return subprocess.run(args=" ".join(args), shell=True, check=check) + spec = Spec(**yaml.safe_load(specfile)) + spec_path = path.dirname(specfile.name) - @staticmethod - def kubectl(*args, check: bool = True, capture_output: bool = False): - args = ("kubectl",) + args - return subprocess.run( - args=" ".join(args), check=check, shell=True, capture_output=capture_output - ) + for test in spec.tests: + click.echo(f"🏁 {click.style('Starting test:', fg=COLOR_HARNESS)} {test.name}") + Runner(test, spec_path).run() -def keygen() -> str: - return "".join(random.choices(string.ascii_letters + string.digits, k=32)) +def git_root(from_path: str) -> str: + repo = git.Repo(from_path, search_parent_directories=True) + return repo.git.rev_parse("--show-toplevel") def init_logger(level=logging.INFO): diff --git a/tools/k3test/model.py b/tools/k3test/model.py new file mode 100644 index 0000000..096a13b --- /dev/null +++ b/tools/k3test/model.py @@ -0,0 +1,29 @@ +from dataclasses import dataclass +from pydantic import BaseModel + + +@dataclass +class Secret: + name: str + values: dict[str, str] + + +@dataclass +class Deployment: + chart: str + values: str + ports: dict[int, int] + + +@dataclass +class Test: + name: str + pull_secret: str + secrets: list[Secret] + deployments: list[Deployment] + run: list[str] + cleanup: list[str] + + +class Spec(BaseModel): + tests: list[Test] diff --git a/tools/k3test/namespace.py b/tools/k3test/namespace.py new file mode 100644 index 0000000..dc7ad09 --- /dev/null +++ b/tools/k3test/namespace.py @@ -0,0 +1,117 @@ +import logging +import signal +import subprocess +import time + +from contextlib import contextmanager +from functools import lru_cache +from os import path +from typing import Mapping + +from kubernetes import client +from kubernetes.client.rest import ApiException + +from model import Secret + +logger = logging.getLogger("namespace") + + +class Namespace: + def __init__(self, namespace: str): + self.namespace = namespace + self.cluster = client.CoreV1Api() + + def create_ns(self): + kubectl("create", "namespace", self.namespace) + + def delete_ns(self): + kubectl("delete", "namespace", self.namespace, "--wait=true") + + def ns_exists(self): + try: + self.cluster.read_namespace(self.namespace) + return True + except ApiException as e: + if e.status != 404: + raise + + return False + + def create_secret(self, secret: Secret): + keys = ( + f"--from-literal={k}={path.expandvars(v)}" for k, v in secret.values.items() + ) + self.kubectl( + "create", + "secret", + "generic", + secret.name, + *keys, + ) + + def kubectl(self, *args): + kubectl(*args, "-n", self.namespace) + + def wait(self, pod: str): + self.kubectl( + "wait", + "--for=condition=ready", + "--timeout=30s", + "pod", + pod, + ) + + @contextmanager + def forward(self, svc: str, ports: Mapping[int, int]): + pod = self.svc_pod(svc) + port_mapping = tuple(f"{k}:{v}" for k, v in ports.items()) + logger.debug("port-mapping: %s", port_mapping) + args = " ".join( + ("kubectl", "-n", self.namespace, "port-forward", pod) + port_mapping + ) + logger.debug("port-forward: %s", args) + proc = subprocess.Popen( + args=args, + shell=True, + stdout=subprocess.DEVNULL, + ) + + time.sleep(1.5) + + try: + yield proc + finally: + logger.debug("terminating port-forward") + proc.send_signal(signal.SIGINT) + proc.wait() + logger.debug("port-forward terminated") + + @lru_cache(maxsize=32) + def svc_pod(self, svc: str) -> str: + proc = kubectl( + "get", + "pods", + "--namespace", + self.namespace, + "-l", + f"app.kubernetes.io/name={svc},app.kubernetes.io/instance={svc}", + "-o", + "jsonpath='{.items[0].metadata.name}'", + capture_output=True, + ) + + if proc.returncode != 0: + proc.check_returncode() + + return proc.stdout.decode() + + def helm(self, *args, check=True): + args = ("helm", "-n", self.namespace) + args + return subprocess.run(args=" ".join(args), shell=True, check=check) + + +def kubectl(*args, check: bool = True, capture_output: bool = False): + args = ("kubectl",) + args + return subprocess.run( + args=" ".join(args), check=check, shell=True, capture_output=capture_output + ) diff --git a/tools/k3test/pyproject.toml b/tools/k3test/pyproject.toml index ccc13bf..28c25b2 100644 --- a/tools/k3test/pyproject.toml +++ b/tools/k3test/pyproject.toml @@ -5,8 +5,11 @@ description = "Runs chart tests in k3s" readme = "README.md" requires-python = ">=3.13" dependencies = [ + "click>=8.1.7", + "gitpython>=3.1.43", "kubernetes>=31.0.0", - "python-retry>=0.0.1", + "pydantic>=2.9.2", + "pyyaml>=6.0.2", ] [dependency-groups] @@ -14,4 +17,5 @@ dev = [ "black>=24.10.0", "kubernetes-typed>=18.20.2", "pylint>=3.3.1", + "types-pyyaml>=6.0.12.20240917", ] diff --git a/tools/k3test/uv.lock b/tools/k3test/uv.lock index 2702887..aef8b4a 100644 --- a/tools/k3test/uv.lock +++ b/tools/k3test/uv.lock @@ -1,6 +1,15 @@ version = 1 requires-python = ">=3.13" +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + [[package]] name = "astroid" version = "3.3.5" @@ -120,6 +129,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4c/a3/ac312faeceffd2d8f86bc6dcb5c401188ba5a01bc88e69bed97578a0dfcd/durationpy-0.9-py3-none-any.whl", hash = "sha256:e65359a7af5cedad07fb77a2dd3f390f8eb0b74cb845589fa6c057086834dd38", size = 3461 }, ] +[[package]] +name = "gitdb" +version = "4.0.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/0d/bbb5b5ee188dec84647a4664f3e11b06ade2bde568dbd489d9d64adef8ed/gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b", size = 394469 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/5b/8f0c4a5bb9fd491c277c21eff7ccae71b47d43c4446c9d0c6cff2fe8c2c4/gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4", size = 62721 }, +] + +[[package]] +name = "gitpython" +version = "3.1.43" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b6/a1/106fd9fa2dd989b6fb36e5893961f82992cf676381707253e0bf93eb1662/GitPython-3.1.43.tar.gz", hash = "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c", size = 214149 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/bd/cc3a402a6439c15c3d4294333e13042b915bbeab54edc457c723931fed3f/GitPython-3.1.43-py3-none-any.whl", hash = "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff", size = 207337 }, +] + [[package]] name = "google-auth" version = "2.36.0" @@ -182,8 +215,11 @@ name = "k3test" version = "0.1.0" source = { virtual = "." } dependencies = [ + { name = "click" }, + { name = "gitpython" }, { name = "kubernetes" }, - { name = "python-retry" }, + { name = "pydantic" }, + { name = "pyyaml" }, ] [package.dev-dependencies] @@ -191,12 +227,16 @@ dev = [ { name = "black" }, { name = "kubernetes-typed" }, { name = "pylint" }, + { name = "types-pyyaml" }, ] [package.metadata] requires-dist = [ + { name = "click", specifier = ">=8.1.7" }, + { name = "gitpython", specifier = ">=3.1.43" }, { name = "kubernetes", specifier = ">=31.0.0" }, - { name = "python-retry", specifier = ">=0.0.1" }, + { name = "pydantic", specifier = ">=2.9.2" }, + { name = "pyyaml", specifier = ">=6.0.2" }, ] [package.metadata.requires-dev] @@ -204,6 +244,7 @@ dev = [ { name = "black", specifier = ">=24.10.0" }, { name = "kubernetes-typed", specifier = ">=18.20.2" }, { name = "pylint", specifier = ">=3.3.1" }, + { name = "types-pyyaml", specifier = ">=6.0.12.20240917" }, ] [[package]] @@ -337,6 +378,43 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/77/89/bc88a6711935ba795a679ea6ebee07e128050d6382eaa35a0a47c8032bdc/pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd", size = 181537 }, ] +[[package]] +name = "pydantic" +version = "2.9.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/b7/d9e3f12af310e1120c21603644a1cd86f59060e040ec5c3a80b8f05fae30/pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f", size = 769917 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/e4/ba44652d562cbf0bf320e0f3810206149c8a4e99cdbf66da82e97ab53a15/pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12", size = 434928 }, +] + +[[package]] +name = "pydantic-core" +version = "2.23.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e2/aa/6b6a9b9f8537b872f552ddd46dd3da230367754b6f707b8e1e963f515ea3/pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863", size = 402156 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/ef/16ee2df472bf0e419b6bc68c05bf0145c49247a1095e85cee1463c6a44a1/pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc", size = 1856143 }, + { url = "https://files.pythonhosted.org/packages/da/fa/bc3dbb83605669a34a93308e297ab22be82dfb9dcf88c6cf4b4f264e0a42/pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd", size = 1770063 }, + { url = "https://files.pythonhosted.org/packages/4e/48/e813f3bbd257a712303ebdf55c8dc46f9589ec74b384c9f652597df3288d/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05", size = 1790013 }, + { url = "https://files.pythonhosted.org/packages/b4/e0/56eda3a37929a1d297fcab1966db8c339023bcca0b64c5a84896db3fcc5c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d", size = 1801077 }, + { url = "https://files.pythonhosted.org/packages/04/be/5e49376769bfbf82486da6c5c1683b891809365c20d7c7e52792ce4c71f3/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510", size = 1996782 }, + { url = "https://files.pythonhosted.org/packages/bc/24/e3ee6c04f1d58cc15f37bcc62f32c7478ff55142b7b3e6d42ea374ea427c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6", size = 2661375 }, + { url = "https://files.pythonhosted.org/packages/c1/f8/11a9006de4e89d016b8de74ebb1db727dc100608bb1e6bbe9d56a3cbbcce/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b", size = 2071635 }, + { url = "https://files.pythonhosted.org/packages/7c/45/bdce5779b59f468bdf262a5bc9eecbae87f271c51aef628d8c073b4b4b4c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327", size = 1916994 }, + { url = "https://files.pythonhosted.org/packages/d8/fa/c648308fe711ee1f88192cad6026ab4f925396d1293e8356de7e55be89b5/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6", size = 1968877 }, + { url = "https://files.pythonhosted.org/packages/16/16/b805c74b35607d24d37103007f899abc4880923b04929547ae68d478b7f4/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f", size = 2116814 }, + { url = "https://files.pythonhosted.org/packages/d1/58/5305e723d9fcdf1c5a655e6a4cc2a07128bf644ff4b1d98daf7a9dbf57da/pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769", size = 1738360 }, + { url = "https://files.pythonhosted.org/packages/a5/ae/e14b0ff8b3f48e02394d8acd911376b7b66e164535687ef7dc24ea03072f/pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5", size = 1919411 }, +] + [[package]] name = "pylint" version = "3.3.1" @@ -376,14 +454,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, ] -[[package]] -name = "python-retry" -version = "0.0.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/00/16/442a6ef19877c8f344cdf42219abc735b41187b1904c9f7a2b9a3d823ce1/python_retry-0.0.1-py3-none-any.whl", hash = "sha256:4e1678a05721a7708639adf0b10d4a07fb719906a310514b393a2c83c4b9964d", size = 4229 }, -] - [[package]] name = "pyyaml" version = "6.0.2" @@ -450,6 +520,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, ] +[[package]] +name = "smmap" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/04/b5bf6d21dc4041000ccba7eb17dd3055feb237e7ffc2c20d3fae3af62baa/smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62", size = 22291 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/a5/10f97f73544edcdef54409f1d839f6049a0d79df68adbc1ceb24d1aaca42/smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da", size = 24282 }, +] + [[package]] name = "tomlkit" version = "0.13.2" @@ -459,6 +538,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f9/b6/a447b5e4ec71e13871be01ba81f5dfc9d0af7e473da256ff46bc0e24026f/tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", size = 37955 }, ] +[[package]] +name = "types-pyyaml" +version = "6.0.12.20240917" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/92/7d/a95df0a11f95c8f48d7683f03e4aed1a2c0fc73e9de15cca4d38034bea1a/types-PyYAML-6.0.12.20240917.tar.gz", hash = "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587", size = 12381 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/2c/c1d81d680997d24b0542aa336f0a65bd7835e5224b7670f33a7d617da379/types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash = "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570", size = 15264 }, +] + [[package]] name = "typing-extensions" version = "4.12.2" From 8ab29895207895041fc33885c684eaed3d597a2b Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Thu, 21 Nov 2024 12:16:55 -0500 Subject: [PATCH 05/22] Rename test harness to ktest.py --- tools/{k3test => ktest}/.python-version | 0 tools/{k3test => ktest}/README.md | 0 tools/{k3test/k3test.py => ktest/ktest.py} | 11 ++++-- tools/{k3test => ktest}/model.py | 0 tools/{k3test => ktest}/namespace.py | 0 tools/{k3test => ktest}/pylintrc | 0 tools/{k3test => ktest}/pyproject.toml | 4 +-- tools/{k3test => ktest}/uv.lock | 40 ++++++++++++---------- 8 files changed, 31 insertions(+), 24 deletions(-) rename tools/{k3test => ktest}/.python-version (100%) rename tools/{k3test => ktest}/README.md (100%) rename tools/{k3test/k3test.py => ktest/ktest.py} (93%) rename tools/{k3test => ktest}/model.py (100%) rename tools/{k3test => ktest}/namespace.py (100%) rename tools/{k3test => ktest}/pylintrc (100%) rename tools/{k3test => ktest}/pyproject.toml (85%) rename tools/{k3test => ktest}/uv.lock (88%) diff --git a/tools/k3test/.python-version b/tools/ktest/.python-version similarity index 100% rename from tools/k3test/.python-version rename to tools/ktest/.python-version diff --git a/tools/k3test/README.md b/tools/ktest/README.md similarity index 100% rename from tools/k3test/README.md rename to tools/ktest/README.md diff --git a/tools/k3test/k3test.py b/tools/ktest/ktest.py similarity index 93% rename from tools/k3test/k3test.py rename to tools/ktest/ktest.py index 960aea9..9b15fd5 100755 --- a/tools/k3test/k3test.py +++ b/tools/ktest/ktest.py @@ -57,13 +57,16 @@ def run(self): with ExitStack() as stack: for deployment in self.test.deployments: click.echo( - f"πŸ”€ {click.style("Forwarding port(s):", fg=COLOR_HARNESS)} {deployment.chart} - {deployment.ports}" + f"πŸ”€ {click.style("Forwarding port(s):", fg=COLOR_HARNESS)} " + f"{deployment.chart} - {deployment.ports}" ) stack.enter_context(ns.forward(deployment.chart, deployment.ports)) - click.echo(f"\nβœ… Deployment complete.\n") + click.echo("\nβœ… Deployment complete.\n") try: self.execute_steps() + + click.echo("\nβœ… Tests complete.\n") finally: self.execute_cleanup() @@ -112,7 +115,9 @@ def new_namespace(name: str) -> Iterator["Namespace"]: yield ns - click.echo(f"\n🐳 {click.style("Deleting namespace:", fg=COLOR_HARNESS)} {name}") + click.echo( + f"\n🐳 {click.style("Deleting namespace:", fg=COLOR_HARNESS)} {name}" + ) ns.delete_ns() diff --git a/tools/k3test/model.py b/tools/ktest/model.py similarity index 100% rename from tools/k3test/model.py rename to tools/ktest/model.py diff --git a/tools/k3test/namespace.py b/tools/ktest/namespace.py similarity index 100% rename from tools/k3test/namespace.py rename to tools/ktest/namespace.py diff --git a/tools/k3test/pylintrc b/tools/ktest/pylintrc similarity index 100% rename from tools/k3test/pylintrc rename to tools/ktest/pylintrc diff --git a/tools/k3test/pyproject.toml b/tools/ktest/pyproject.toml similarity index 85% rename from tools/k3test/pyproject.toml rename to tools/ktest/pyproject.toml index 28c25b2..71c3618 100644 --- a/tools/k3test/pyproject.toml +++ b/tools/ktest/pyproject.toml @@ -1,7 +1,7 @@ [project] -name = "k3test" +name = "ktest" version = "0.1.0" -description = "Runs chart tests in k3s" +description = "Runs chart tests in kubernetes" readme = "README.md" requires-python = ">=3.13" dependencies = [ diff --git a/tools/k3test/uv.lock b/tools/ktest/uv.lock similarity index 88% rename from tools/k3test/uv.lock rename to tools/ktest/uv.lock index aef8b4a..3e7502e 100644 --- a/tools/k3test/uv.lock +++ b/tools/ktest/uv.lock @@ -211,7 +211,7 @@ wheels = [ ] [[package]] -name = "k3test" +name = "ktest" version = "0.1.0" source = { virtual = "." } dependencies = [ @@ -380,39 +380,41 @@ wheels = [ [[package]] name = "pydantic" -version = "2.9.2" +version = "2.10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/b7/d9e3f12af310e1120c21603644a1cd86f59060e040ec5c3a80b8f05fae30/pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f", size = 769917 } +sdist = { url = "https://files.pythonhosted.org/packages/e9/78/58c36d0cf331b659d0ccd99175e3523c457b4f8e67cb92a8fdc22ec1667c/pydantic-2.10.0.tar.gz", hash = "sha256:0aca0f045ff6e2f097f1fe89521115335f15049eeb8a7bef3dafe4b19a74e289", size = 781980 } wheels = [ - { url = "https://files.pythonhosted.org/packages/df/e4/ba44652d562cbf0bf320e0f3810206149c8a4e99cdbf66da82e97ab53a15/pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12", size = 434928 }, + { url = "https://files.pythonhosted.org/packages/9e/ee/255cbfdbf5c47650de70ac8a5425107511f505ed0366c29d537f7f1842e1/pydantic-2.10.0-py3-none-any.whl", hash = "sha256:5e7807ba9201bdf61b1b58aa6eb690916c40a47acfb114b1b4fef3e7fd5b30fc", size = 454346 }, ] [[package]] name = "pydantic-core" -version = "2.23.4" +version = "2.27.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e2/aa/6b6a9b9f8537b872f552ddd46dd3da230367754b6f707b8e1e963f515ea3/pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863", size = 402156 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/ef/16ee2df472bf0e419b6bc68c05bf0145c49247a1095e85cee1463c6a44a1/pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc", size = 1856143 }, - { url = "https://files.pythonhosted.org/packages/da/fa/bc3dbb83605669a34a93308e297ab22be82dfb9dcf88c6cf4b4f264e0a42/pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd", size = 1770063 }, - { url = "https://files.pythonhosted.org/packages/4e/48/e813f3bbd257a712303ebdf55c8dc46f9589ec74b384c9f652597df3288d/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05", size = 1790013 }, - { url = "https://files.pythonhosted.org/packages/b4/e0/56eda3a37929a1d297fcab1966db8c339023bcca0b64c5a84896db3fcc5c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d", size = 1801077 }, - { url = "https://files.pythonhosted.org/packages/04/be/5e49376769bfbf82486da6c5c1683b891809365c20d7c7e52792ce4c71f3/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510", size = 1996782 }, - { url = "https://files.pythonhosted.org/packages/bc/24/e3ee6c04f1d58cc15f37bcc62f32c7478ff55142b7b3e6d42ea374ea427c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6", size = 2661375 }, - { url = "https://files.pythonhosted.org/packages/c1/f8/11a9006de4e89d016b8de74ebb1db727dc100608bb1e6bbe9d56a3cbbcce/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b", size = 2071635 }, - { url = "https://files.pythonhosted.org/packages/7c/45/bdce5779b59f468bdf262a5bc9eecbae87f271c51aef628d8c073b4b4b4c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327", size = 1916994 }, - { url = "https://files.pythonhosted.org/packages/d8/fa/c648308fe711ee1f88192cad6026ab4f925396d1293e8356de7e55be89b5/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6", size = 1968877 }, - { url = "https://files.pythonhosted.org/packages/16/16/b805c74b35607d24d37103007f899abc4880923b04929547ae68d478b7f4/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f", size = 2116814 }, - { url = "https://files.pythonhosted.org/packages/d1/58/5305e723d9fcdf1c5a655e6a4cc2a07128bf644ff4b1d98daf7a9dbf57da/pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769", size = 1738360 }, - { url = "https://files.pythonhosted.org/packages/a5/ae/e14b0ff8b3f48e02394d8acd911376b7b66e164535687ef7dc24ea03072f/pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5", size = 1919411 }, +sdist = { url = "https://files.pythonhosted.org/packages/d1/cd/8331ae216bcc5a3f2d4c6b941c9f63de647e2700d38133f4f7e0132a00c4/pydantic_core-2.27.0.tar.gz", hash = "sha256:f57783fbaf648205ac50ae7d646f27582fc706be3977e87c3c124e7a92407b10", size = 412675 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/b2/740159bdfe532d856e340510246aa1fd723b97cadf1a38153bdfb52efa28/pydantic_core-2.27.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5f2b19b8d6fca432cb3acf48cf5243a7bf512988029b6e6fd27e9e8c0a204d85", size = 1886935 }, + { url = "https://files.pythonhosted.org/packages/ca/2a/2f435d9fd591c912ca227f29c652a93775d35d54677b57c3157bbad823b5/pydantic_core-2.27.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c86679f443e7085ea55a7376462553996c688395d18ef3f0d3dbad7838f857a2", size = 1805318 }, + { url = "https://files.pythonhosted.org/packages/ba/f2/755b628009530b19464bb95c60f829b47a6ef7930f8ca1d87dac90fd2848/pydantic_core-2.27.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:510b11e9c3b1a852876d1ccd8d5903684336d635214148637ceb27366c75a467", size = 1822284 }, + { url = "https://files.pythonhosted.org/packages/3d/c2/a12744628b1b55c5384bd77657afa0780868484a92c37a189fb460d1cfe7/pydantic_core-2.27.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb704155e73b833801c247f39d562229c0303f54770ca14fb1c053acb376cf10", size = 1848522 }, + { url = "https://files.pythonhosted.org/packages/60/1d/dfcb8ab94a4637d4cf682550a2bf94695863988e7bcbd6f4d83c04178e17/pydantic_core-2.27.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ce048deb1e033e7a865ca384770bccc11d44179cf09e5193a535c4c2f497bdc", size = 2031678 }, + { url = "https://files.pythonhosted.org/packages/ee/c8/f9cbcab0275e031c4312223c75d999b61fba60995003cd89dc4866300059/pydantic_core-2.27.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58560828ee0951bb125c6f2862fbc37f039996d19ceb6d8ff1905abf7da0bf3d", size = 2672948 }, + { url = "https://files.pythonhosted.org/packages/41/f9/c613546237cf58ed7a7fa9158410c14d0e7e0cbbf95f83a905c9424bb074/pydantic_core-2.27.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abb4785894936d7682635726613c44578c420a096729f1978cd061a7e72d5275", size = 2152419 }, + { url = "https://files.pythonhosted.org/packages/49/71/b951b03a271678b1d1b79481dac38cf8bce8a4e178f36ada0e9aff65a679/pydantic_core-2.27.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2883b260f7a93235488699d39cbbd94fa7b175d3a8063fbfddd3e81ad9988cb2", size = 1986408 }, + { url = "https://files.pythonhosted.org/packages/9a/2c/07b0d5b5e1cdaa07b7c23e758354377d294ff0395116d39c9fa734e5d89e/pydantic_core-2.27.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c6fcb3fa3855d583aa57b94cf146f7781d5d5bc06cb95cb3afece33d31aac39b", size = 1995895 }, + { url = "https://files.pythonhosted.org/packages/63/09/c21e0d7438c7e742209cc8603607c8d389df96018396c8a2577f6e24c5c5/pydantic_core-2.27.0-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:e851a051f7260e6d688267eb039c81f05f23a19431bd7dfa4bf5e3cb34c108cd", size = 2085914 }, + { url = "https://files.pythonhosted.org/packages/68/e4/5ed8f09d92655dcd0a86ee547e509adb3e396cef0a48f5c31e3b060bb9d0/pydantic_core-2.27.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:edb1bfd45227dec8d50bc7c7d86463cd8728bcc574f9b07de7369880de4626a3", size = 2150217 }, + { url = "https://files.pythonhosted.org/packages/cd/e6/a202f0e1b81c729130404e82d9de90dc4418ec01df35000d48d027c38501/pydantic_core-2.27.0-cp313-none-win32.whl", hash = "sha256:678f66462058dd978702db17eb6a3633d634f7aa0deaea61e0a674152766d3fc", size = 1830973 }, + { url = "https://files.pythonhosted.org/packages/06/3d/21ed0f308e6618ce6c5c6bfb9e71734a9a3256d5474a53c8e5aaaba498ca/pydantic_core-2.27.0-cp313-none-win_amd64.whl", hash = "sha256:d28ca7066d6cdd347a50d8b725dc10d9a1d6a1cce09836cf071ea6a2d4908be0", size = 1974853 }, + { url = "https://files.pythonhosted.org/packages/d7/18/e5744a132b81f98b9f92e15f33f03229a1d254ce7af942b1422ec2ac656f/pydantic_core-2.27.0-cp313-none-win_arm64.whl", hash = "sha256:6f4a53af9e81d757756508b57cae1cf28293f0f31b9fa2bfcb416cc7fb230f9d", size = 1877469 }, ] [[package]] From d89189030156be62bec2f4b06d82fc3d37efe9d0 Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Fri, 22 Nov 2024 08:50:54 -0500 Subject: [PATCH 06/22] ktest handles config-maps + CI tests --- .github/workflows/ci.yaml | 32 ++++++++++++++++++++-- .gitignore | 1 + charts/directory/ci/minimal-values.yaml | 5 ++-- charts/directory/templates/_helpers.tpl | 10 +++++++ charts/directory/templates/admin_keys.yaml | 12 +++++--- charts/directory/templates/config.yaml | 2 +- charts/directory/templates/deployment.yaml | 2 +- charts/directory/values.yaml | 14 +++++++--- test/directory/directory.values.yaml | 6 ++-- test/directory/tests.yaml | 11 ++++++-- tools/ktest/ktest.py | 11 ++++++-- tools/ktest/model.py | 13 +++++++++ tools/ktest/namespace.py | 9 +++++- 13 files changed, 105 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6862d03..4c7c0c5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -7,10 +7,11 @@ on: env: HELM_VERSION: v3.14.4 - PYTHON_VERSION: 3.x + PYTHON_VERSION: 3.13 + UV_VERSION: 0.5.3 jobs: - lint: + test: runs-on: ubuntu-latest steps: - @@ -21,7 +22,6 @@ jobs: url: https://vault.eng.aserto.com/ token: ${{ secrets.VAULT_TOKEN }} secrets: | - kv/data/github "USERNAME" | DOCKER_USERNAME; kv/data/github "READ_WRITE_TOKEN" | READ_WRITE_TOKEN; - uses: actions/checkout@v4 @@ -47,3 +47,29 @@ jobs: name: Lint run: | ct lint --config ct.yaml --helm-repo-extra-args "aserto-helm=-u gh -p ${READ_WRITE_TOKEN}" + - + name: Install uv package manager + uses: astral-sh/setup-uv@v3 + with: + version: ${{ env.UV_VERSION }} + - + uses: AbsaOSS/k3d-action@v2 + name: Create k8s cluster + with: + cluster-name: "test" + args: --agents 1 + - + name: Generate admin ssh key + id: sshkey + run: | + ssh-keygen -t ed25519 -N "" -f ~/.ssh/admin_ed25519 + echo "public_key=~/.ssh/admin_ed25519.pub" >> $GITHUB_OUTPUT + echo "private_key=~/.ssh/admin_ed25519" >> $GITHUB_OUTPUT + - + name: Test Directory + env: + SSH_PUBLIC_KEY: ${{ steps.sshkey.public_key }} + SSH_PRIVATE_KEY: ${{ steps.sshkey.private_key }} + run: | + cd tools/ktest + uv run ktest.py ../../test/directory/tests.yaml diff --git a/.gitignore b/.gitignore index fe60ce6..0c2225c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ # python **/__pycache__/ +.venv-path diff --git a/charts/directory/ci/minimal-values.yaml b/charts/directory/ci/minimal-values.yaml index 02e2813..8d98684 100644 --- a/charts/directory/ci/minimal-values.yaml +++ b/charts/directory/ci/minimal-values.yaml @@ -9,5 +9,6 @@ rootDirectory: tenantDirectory: database: host: tenant-db-host -sshAdminKeys: | - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDf6 +sshAdminKeys: + keys: | + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDf6 diff --git a/charts/directory/templates/_helpers.tpl b/charts/directory/templates/_helpers.tpl index e8899b0..f3bc67d 100644 --- a/charts/directory/templates/_helpers.tpl +++ b/charts/directory/templates/_helpers.tpl @@ -112,3 +112,13 @@ valueFrom: key: api-key {{- end }} {{- end }} + +{{- define "directory.adminKeysConfigMapName" -}} +{{ ((.Values.sshAdminKeys).configMap).name | default + (printf "%s-admin-keys" (include "directory.fullname" .)) }} +{{- end }} + +{{- define "directory.adminKeysConfigMapKey" -}} +{{ ((.Values.sshAdminKeys).configMap).key | default "authorized_keys" }} +{{- end }} + diff --git a/charts/directory/templates/admin_keys.yaml b/charts/directory/templates/admin_keys.yaml index 80a2708..103deef 100644 --- a/charts/directory/templates/admin_keys.yaml +++ b/charts/directory/templates/admin_keys.yaml @@ -1,9 +1,13 @@ +{{- if empty .Values.sshAdminKeys -}} + {{ fail "sshAdminKeys is required" }} +{{- end -}} +{{- if (.Values.sshAdminKeys).keys -}} --- apiVersion: v1 kind: ConfigMap metadata: - name: {{ include "directory.fullname" . }}-admin-keys + name: {{ include "directory.adminKeysConfigMapName" . }} data: - authorized_keys: | - {{- .Values.sshAdminKeys | required "sshAdminKeys is required" | nindent 4 }} - + {{ include "directory.adminKeysConfigMapKey" . }}: | + {{- $.Values.sshAdminKeys.keys | required "sshAdminKeys.keys is required" | nindent 4 }} +{{- end -}} diff --git a/charts/directory/templates/config.yaml b/charts/directory/templates/config.yaml index ca6ae5c..cc3c3b0 100644 --- a/charts/directory/templates/config.yaml +++ b/charts/directory/templates/config.yaml @@ -22,7 +22,7 @@ stringData: metrics: {{- include "aserto-lib.metricsService" . | nindent 8 }} admin: - authorized_keys_path: /admin-keys/authorized_keys + authorized_keys_path: /admin-keys/{{ include "directory.adminKeysConfigMapKey" . }} {{ if .Values.rootDirectory.runService }} {{- with .Values.rootDirectory.database -}} diff --git a/charts/directory/templates/deployment.yaml b/charts/directory/templates/deployment.yaml index 1c55d5f..fa401ac 100644 --- a/charts/directory/templates/deployment.yaml +++ b/charts/directory/templates/deployment.yaml @@ -35,7 +35,7 @@ spec: path: config.yaml - name: admin-keys configMap: - name: {{ include "directory.fullname" . }}-admin-keys + name: {{ include "directory.adminKeysConfigMapName" . }} {{- with (include "aserto-lib.grpcConfig" . | fromYaml).certSecret }} - name: grpc-certs secret: diff --git a/charts/directory/values.yaml b/charts/directory/values.yaml index 3836d0d..0b75b84 100644 --- a/charts/directory/values.yaml +++ b/charts/directory/values.yaml @@ -21,10 +21,16 @@ oidc: # audience: "" # Required: Provide one or more SSH public keys to be granted admin access. -# sshAdminKeys: | -# # Add your authorized SSH public keys here -# ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDf6 -# ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDa7 +# sshAdminKeys: +# # Keys can be provided inline as a multi-line string under keys: +# keys: | +# # Add your authorized SSH public keys here +# ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDf6 +# ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDa7 +# # Keys can also be provided in a ConfigMap +# configMap: +# name: directory-admin-keys +# key: authorized_keys rootDirectory: runService: true diff --git a/test/directory/directory.values.yaml b/test/directory/directory.values.yaml index 1a971e8..cae0b2e 100644 --- a/test/directory/directory.values.yaml +++ b/test/directory/directory.values.yaml @@ -28,8 +28,10 @@ tenantDirectory: cache: sizeMB: 100 -sshAdminKeys: | - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMEAOAWPotrmDRxyLRSswrectb8NJ4pH/i09N91Hd4Lj ronen@hilewi.cz +sshAdminKeys: + configMap: + name: directory-admin-keys + key: authorized_keys tenants: - name: test diff --git a/test/directory/tests.yaml b/test/directory/tests.yaml index deb083d..239dac9 100644 --- a/test/directory/tests.yaml +++ b/test/directory/tests.yaml @@ -11,6 +11,11 @@ tests: values: reader: apikey_tenant_reader writer: apikey_tenant_writer + config_maps: + - name: directory-admin-keys + keys: + - name: authorized_keys + file: $SSH_PUBLIC_KEY deployments: - chart: directory values: directory.values.yaml @@ -19,15 +24,15 @@ tests: 8282: 8282 run: - > - ssh -p 2222 -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR + ssh -i ${SSH_PRIVATE_KEY:-$(ls -1 ~/.ssh/id_* | head -1)} -p 2222 -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR localhost provision root-keys - > - ssh -p 2222 -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR + ssh -i ${SSH_PRIVATE_KEY:-$(ls -1 ~/.ssh/id_* | head -1)} -p 2222 -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR localhost provision tenant test --id 3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1 - > ${TOPAZ:-topaz} ds get manifest -H localhost:8282 --tenant-id 3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1 -k apikey_tenant_reader --stdout --plaintext cleanup: - > - ssh -p 2222 -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR + ssh -i ${SSH_PRIVATE_KEY:-$(ls -1 ~/.ssh/id_* | head -1)} -p 2222 -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR localhost provision tenant test --id 3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1 --delete diff --git a/tools/ktest/ktest.py b/tools/ktest/ktest.py index 9b15fd5..a82d82e 100755 --- a/tools/ktest/ktest.py +++ b/tools/ktest/ktest.py @@ -39,6 +39,12 @@ def run(self): ) ns.create_secret(secret) + for config_map in self.test.config_maps: + click.echo( + f"πŸ“‹ {click.style("Creating sconfig map", fg=COLOR_HARNESS)} {config_map.name}" + ) + ns.create_config_map(config_map) + for deployment in self.test.deployments: click.echo( f"πŸ—ΊοΈ {click.style("Installing chart:", fg=COLOR_HARNESS)} {deployment.chart}" @@ -54,6 +60,8 @@ def run(self): for deployment in self.test.deployments: ns.wait(ns.svc_pod(deployment.chart)) + click.echo("\nβœ… Deployment complete.\n") + with ExitStack() as stack: for deployment in self.test.deployments: click.echo( @@ -62,7 +70,6 @@ def run(self): ) stack.enter_context(ns.forward(deployment.chart, deployment.ports)) - click.echo("\nβœ… Deployment complete.\n") try: self.execute_steps() @@ -71,7 +78,7 @@ def run(self): self.execute_cleanup() def execute_steps(self): - click.echo(f"πŸƒ {click.style("Running tests", fg=COLOR_HARNESS)}\n") + click.echo(f"\nπŸƒ {click.style("Running tests", fg=COLOR_HARNESS)}\n") for step in self.test.run: click.echo(f"πŸ§ͺ {click.style(step, fg=COLOR_STEP)}") self.subprocess(step) diff --git a/tools/ktest/model.py b/tools/ktest/model.py index 096a13b..d0155f3 100644 --- a/tools/ktest/model.py +++ b/tools/ktest/model.py @@ -2,6 +2,18 @@ from pydantic import BaseModel +@dataclass +class ConfigMapKey: + name: str + file: str + + +@dataclass +class ConfigMap: + name: str + keys: list[ConfigMapKey] + + @dataclass class Secret: name: str @@ -20,6 +32,7 @@ class Test: name: str pull_secret: str secrets: list[Secret] + config_maps: list[ConfigMap] deployments: list[Deployment] run: list[str] cleanup: list[str] diff --git a/tools/ktest/namespace.py b/tools/ktest/namespace.py index dc7ad09..4e8dabe 100644 --- a/tools/ktest/namespace.py +++ b/tools/ktest/namespace.py @@ -11,7 +11,7 @@ from kubernetes import client from kubernetes.client.rest import ApiException -from model import Secret +from model import ConfigMap, Secret logger = logging.getLogger("namespace") @@ -49,6 +49,13 @@ def create_secret(self, secret: Secret): *keys, ) + def create_config_map(self, config_map: ConfigMap): + keys = ( + f"--from-file={key.name}={path.expandvars(key.file)}" + for key in config_map.keys + ) + self.kubectl("create", "configmap", config_map.name, *keys) + def kubectl(self, *args): kubectl(*args, "-n", self.namespace) From 029536468d5b1202fbd5f5e0f093709b9741a7c5 Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Fri, 22 Nov 2024 08:52:52 -0500 Subject: [PATCH 07/22] Run ktest in CI --- .github/workflows/ci.yaml | 76 ++++++++--- .gitignore | 3 + charts/directory/Chart.lock | 6 +- charts/directory/Chart.yaml | 2 +- .../directory/templates/db_credentials.yaml | 19 ++- test/directory/directory.values.yaml | 6 +- test/directory/tests.yaml | 8 ++ tools/ktest/ktest.py | 118 ++++++++++++------ tools/ktest/namespace.py | 17 ++- 9 files changed, 174 insertions(+), 81 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4c7c0c5..c97d46f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -6,23 +6,17 @@ on: pull_request: env: + GH_TOKEN: ${{ github.token }} HELM_VERSION: v3.14.4 PYTHON_VERSION: 3.13 + POSTGRES_PASSWORD: postgres UV_VERSION: 0.5.3 + TOPAZ_VERSION: 0.32.36 jobs: - test: + lint: runs-on: ubuntu-latest steps: - - - name: Read Configuration - uses: hashicorp/vault-action@v3 - id: vault - with: - url: https://vault.eng.aserto.com/ - token: ${{ secrets.VAULT_TOKEN }} - secrets: | - kv/data/github "READ_WRITE_TOKEN" | READ_WRITE_TOKEN; - uses: actions/checkout@v4 with: @@ -46,7 +40,32 @@ jobs: - name: Lint run: | - ct lint --config ct.yaml --helm-repo-extra-args "aserto-helm=-u gh -p ${READ_WRITE_TOKEN}" + ct lint --config ct.yaml --helm-repo-extra-args "aserto-helm=-u gh -p ${{ secrets.GITHUB_TOKEN }}" + + test: + runs-on: ubuntu-latest + steps: + - + uses: actions/checkout@v4 + - + name: Set up Helm + uses: azure/setup-helm@v4 + with: + version: ${{ env.HELM_VERSION }} + - + name: Install topaz CLI + run: | + gh release download v${{env.TOPAZ_VERSION}} --repo aserto-dev/topaz --pattern "topaz_linux_x86_64.zip" \ + --output ./ext/topaz.zip --clobber + unzip ./ext/topaz.zip -d bin + chmod +x ./bin/topaz + ./bin/topaz version + echo "TOPAZ=$(realpath ./bin/topaz)" >> "$GITHUB_ENV" + - + name: Install topazd container + run: | + ${TOPAZ} install --container-tag=${{ env.TOPAZ_VERSION }} + ${TOPAZ} version - name: Install uv package manager uses: astral-sh/setup-uv@v3 @@ -57,19 +76,42 @@ jobs: name: Create k8s cluster with: cluster-name: "test" - args: --agents 1 + args: > + --agents 1 + --k3s-arg "--disable=metrics-server@server:*" + - + name: Deploy Postgres + run: | + helm install postgresql oci://registry-1.docker.io/bitnamicharts/postgresql \ + --namespace postgres --create-namespace \ + --set auth.postgresPassword=${{ env.POSTGRES_PASSWORD }} + - + name: Wait for Postgres + run: | + echo "Waiting for postgres to be ready" + kubectl wait pods --selector app.kubernetes.io/name=postgresql \ + --for condition=Ready --namespace postgres --timeout=60s - name: Generate admin ssh key id: sshkey run: | - ssh-keygen -t ed25519 -N "" -f ~/.ssh/admin_ed25519 - echo "public_key=~/.ssh/admin_ed25519.pub" >> $GITHUB_OUTPUT - echo "private_key=~/.ssh/admin_ed25519" >> $GITHUB_OUTPUT + ssh-keygen -t ed25519 -N "" -f ${HOME}/.ssh/admin_ed25519 + echo "public_key=${HOME}/.ssh/admin_ed25519.pub" >> "$GITHUB_OUTPUT" + echo "private_key=${HOME}/.ssh/admin_ed25519" >> "$GITHUB_OUTPUT" + + cat << EOF > ${HOME}/.ssh/config + Host localhost + StrictHostKeyChecking no + EOF + + chmod 400 ~/.ssh/config - name: Test Directory + timeout-minutes: 10 env: - SSH_PUBLIC_KEY: ${{ steps.sshkey.public_key }} - SSH_PRIVATE_KEY: ${{ steps.sshkey.private_key }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SSH_PUBLIC_KEY: ${{ steps.sshkey.outputs.public_key }} + SSH_PRIVATE_KEY: ${{ steps.sshkey.outputs.private_key }} run: | cd tools/ktest uv run ktest.py ../../test/directory/tests.yaml diff --git a/.gitignore b/.gitignore index 0c2225c..faf8b63 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ # python **/__pycache__/ .venv-path + +# env +.envrc diff --git a/charts/directory/Chart.lock b/charts/directory/Chart.lock index 8baca77..a93cd8e 100644 --- a/charts/directory/Chart.lock +++ b/charts/directory/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: aserto-lib - repository: file:///Users/ronenhilewicz/aserto-dev/helm/charts/aserto-lib + repository: file://../aserto-lib version: 0.1.5 -digest: sha256:d4b6380105553ea5153be1973c56dcf386900d68679a53c987a163760ca8bacd -generated: "2024-11-15T16:05:15.959277-05:00" +digest: sha256:26ffec3faa8dafcb519fd066e04d0d6c33dffaf8aa4052a39166500fb0343b66 +generated: "2024-11-22T09:31:01.633697-05:00" diff --git a/charts/directory/Chart.yaml b/charts/directory/Chart.yaml index e6fcc31..368df02 100644 --- a/charts/directory/Chart.yaml +++ b/charts/directory/Chart.yaml @@ -33,4 +33,4 @@ dependencies: - name: aserto-lib version: ~0.1.5 # repository: oci://ghcr.io/aserto-dev/helm - repository: file:///Users/ronenhilewicz/aserto-dev/helm/charts/aserto-lib + repository: file://../aserto-lib diff --git a/charts/directory/templates/db_credentials.yaml b/charts/directory/templates/db_credentials.yaml index d4fd735..a8a7bc2 100644 --- a/charts/directory/templates/db_credentials.yaml +++ b/charts/directory/templates/db_credentials.yaml @@ -1,28 +1,23 @@ +{{- $rootData := (lookup "v1" "Secret" .Release.Namespace .Values.rootDirectory.database.reader.credentialsSecret).data -}} +{{- if empty $rootData -}} --- apiVersion: v1 kind: Secret metadata: name: {{ .Values.rootDirectory.database.reader.credentialsSecret }} data: - {{- $data := (lookup "v1" "Secret" .Release.Namespace .Values.rootDirectory.database.reader.credentialsSecret).data }} - {{- if $data }} - username: {{ $data.username }} - password: {{ $data.password }} - {{- else }} username: {{ "root_reader" | b64enc }} password: {{ randAlphaNum 20 | b64enc}} - {{- end }} +{{- end }} + +{{- $tenantData := (lookup "v1" "Secret" .Release.Namespace .Values.tenantDirectory.database.reader.credentialsSecret).data -}} +{{- if empty $tenantData -}} --- apiVersion: v1 kind: Secret metadata: name: {{ .Values.tenantDirectory.database.reader.credentialsSecret }} data: - {{- $data := (lookup "v1" "Secret" .Release.Namespace .Values.tenantDirectory.database.reader.credentialsSecret).data }} - {{- if $data }} - username: {{ $data.username }} - password: {{ $data.password }} - {{- else }} username: {{ "tenant_reader" | b64enc }} password: {{ randAlphaNum 20 | b64enc }} - {{- end }} +{{- end }} diff --git a/test/directory/directory.values.yaml b/test/directory/directory.values.yaml index cae0b2e..d08d30c 100644 --- a/test/directory/directory.values.yaml +++ b/test/directory/directory.values.yaml @@ -1,13 +1,13 @@ --- image: - tag: 0.33.2-3e32438c-arm64 + tag: 0.33.2-3e32438c-amd64 imagePullSecrets: - name: ghcr-creds rootDirectory: database: - host: host.k3d.internal + host: postgresql.postgres.svc.cluster.local dbName: test-root-ds sslMode: disable admin: @@ -17,7 +17,7 @@ rootDirectory: tenantDirectory: database: - host: host.k3d.internal + host: postgresql.postgres.svc.cluster.local dbName: test-ds sslMode: disable admin: diff --git a/test/directory/tests.yaml b/test/directory/tests.yaml index 239dac9..0186ec3 100644 --- a/test/directory/tests.yaml +++ b/test/directory/tests.yaml @@ -7,6 +7,14 @@ tests: values: username: postgres password: $POSTGRES_PASSWORD + - name: pg-root-reader-credentials + values: + username: root_reader + password: root_reader + - name: pg-tenant-reader-credentials + values: + username: tenant_reader + password: tenant_reader - name: test-tenant-keys values: reader: apikey_tenant_reader diff --git a/tools/ktest/ktest.py b/tools/ktest/ktest.py index a82d82e..769242d 100755 --- a/tools/ktest/ktest.py +++ b/tools/ktest/ktest.py @@ -5,7 +5,7 @@ import subprocess from contextlib import contextmanager, ExitStack from os import path -from typing import Iterator +from typing import Iterator, Sequence import click import git @@ -13,14 +13,21 @@ from kubernetes import config -from model import Spec, Test +from model import Deployment, Spec, Test from namespace import Namespace logger = logging.getLogger("k3stest") COLOR_HARNESS = "blue" COLOR_STEP = "magenta" -COLOR_CLEANUP = "cyan" +COLOR_ERROR = "red" + + +def echo(emoji: str, heading: str, msg: str = "", *, cl=COLOR_HARNESS, nl=False): + out = f"{emoji} {click.style(heading, fg=cl)} {msg}" + if nl: + out = f"\n{out}\n" + click.echo(out) class Runner: @@ -34,60 +41,87 @@ def run(self): self.set_image_pull_secret(ns) for secret in self.test.secrets: - click.echo( - f"πŸ”’ {click.style("Creating secret:", fg=COLOR_HARNESS)} {secret.name}" - ) + echo("πŸ”’", "Creating secret:", secret.name) ns.create_secret(secret) for config_map in self.test.config_maps: - click.echo( - f"πŸ“‹ {click.style("Creating sconfig map", fg=COLOR_HARNESS)} {config_map.name}" - ) + echo("πŸ“", "Creating config map:", config_map.name) ns.create_config_map(config_map) for deployment in self.test.deployments: - click.echo( - f"πŸ—ΊοΈ {click.style("Installing chart:", fg=COLOR_HARNESS)} {deployment.chart}" - ) - ns.helm( - "install", - deployment.chart, - path.join(self.git_root, "charts", deployment.chart), - "-f", - path.join(self.spec_path, deployment.values), - ) + echo("πŸ—ΊοΈ", "Installing chart:", deployment.chart) + self.deploy_chart(deployment, ns) - for deployment in self.test.deployments: - ns.wait(ns.svc_pod(deployment.chart)) + self.wait_for_deployments(self.test.deployments, ns) - click.echo("\nβœ… Deployment complete.\n") + echo("βœ…", "Deployment complete.", nl=True) with ExitStack() as stack: for deployment in self.test.deployments: - click.echo( - f"πŸ”€ {click.style("Forwarding port(s):", fg=COLOR_HARNESS)} " - f"{deployment.chart} - {deployment.ports}" + echo( + "πŸ”€", + "Forwarding ports:", + f"{deployment.chart} - {deployment.ports}", ) stack.enter_context(ns.forward(deployment.chart, deployment.ports)) try: self.execute_steps() - - click.echo("\nβœ… Tests complete.\n") + echo("βœ…", "Tests complete.", nl=True) + except: + echo("🚨", "Test failed.", nl=True, cl=COLOR_ERROR) + pod = ns.svc_pod(deployment.chart) + echo("πŸ“‹", "Pod logs:", pod) + ns.logs(pod) + click.echo() + raise finally: self.execute_cleanup() + def deploy_chart(self, deployment: Deployment, ns: Namespace): + chart_path = path.join(self.git_root, "charts", deployment.chart) + ns.helm("dep", "build", chart_path) + ns.helm( + "install", + deployment.chart, + chart_path, + "-f", + path.join(self.spec_path, deployment.values), + ) + + def wait_for_deployments(self, deployments: Sequence[Deployment], ns: Namespace): + for deployment in deployments: + pod = ns.svc_pod(deployment.chart) + try: + echo("⏳", "Waiting for pod:", pod) + ns.wait(pod) + except: + echo( + "🚨", + "Error waiting for deployment:", + deployment.chart, + nl=True, + cl=COLOR_ERROR, + ) + echo("πŸ“‹", "Pod logs:", pod) + ns.logs(pod) + click.echo() + raise + def execute_steps(self): - click.echo(f"\nπŸƒ {click.style("Running tests", fg=COLOR_HARNESS)}\n") + echo("πŸƒ", "Running tests", nl=True) for step in self.test.run: - click.echo(f"πŸ§ͺ {click.style(step, fg=COLOR_STEP)}") + echo("πŸ§ͺ", step, cl=COLOR_STEP) self.subprocess(step) def execute_cleanup(self): - click.echo(f"\n🧹 {click.style("Running cleanup", fg=COLOR_HARNESS)}\n") + echo("🧹", "Running cleanup", nl=True) for step in self.test.cleanup: - click.echo(f"🧽 {click.style(step, fg=COLOR_STEP)}") - self.subprocess(step, check=False) + echo("🧹", step, cl=COLOR_STEP) + try: + self.subprocess(step) + except: + pass def set_image_pull_secret(self, ns: Namespace): if self.test.pull_secret: @@ -107,6 +141,7 @@ def subprocess(args: str, check=True): args=args, shell=True, check=check, + timeout=30, ) @staticmethod @@ -117,14 +152,12 @@ def new_namespace(name: str) -> Iterator["Namespace"]: logger.info("namespace '%s' already exists. deleting it...", name) ns.delete_ns() - click.echo(f"🐳 {click.style("Creating namespace:", fg=COLOR_HARNESS)} {name}") + echo("🐳", "Creating namespace:", name) ns.create_ns() yield ns - click.echo( - f"\n🐳 {click.style("Deleting namespace:", fg=COLOR_HARNESS)} {name}" - ) + echo("🐳", "Deleting namespace:", name) ns.delete_ns() @@ -136,14 +169,14 @@ def main(specfile): SPECFILE: path to a YAML file with test definitions. """ - init_logger(logging.DEBUG) + init_logging(logging.DEBUG) config.load_kube_config() spec = Spec(**yaml.safe_load(specfile)) spec_path = path.dirname(specfile.name) for test in spec.tests: - click.echo(f"🏁 {click.style('Starting test:', fg=COLOR_HARNESS)} {test.name}") + echo("🏁", "Starting test:", test.name) Runner(test, spec_path).run() @@ -151,8 +184,13 @@ def git_root(from_path: str) -> str: repo = git.Repo(from_path, search_parent_directories=True) return repo.git.rev_parse("--show-toplevel") +def init_logging(level=logging.INFO): + loggers = (logging.getLogger(name) for name in logging.root.manager.loggerDict if name.startswith("k3test")) + for logger in loggers: + init_logger(logger, level) + -def init_logger(level=logging.INFO): +def init_logger(logger: logging.Logger, level=logging.INFO): logger.setLevel(level) # create console handler and set level to debug @@ -160,7 +198,7 @@ def init_logger(level=logging.INFO): ch.setLevel(logging.DEBUG) # create formatter - formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") + formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s") # add formatter to ch ch.setFormatter(formatter) diff --git a/tools/ktest/namespace.py b/tools/ktest/namespace.py index 4e8dabe..0cc4aba 100644 --- a/tools/ktest/namespace.py +++ b/tools/ktest/namespace.py @@ -13,7 +13,7 @@ from model import ConfigMap, Secret -logger = logging.getLogger("namespace") +logger = logging.getLogger("k3test.namespace") class Namespace: @@ -68,6 +68,9 @@ def wait(self, pod: str): pod, ) + def logs(self, pod: str): + self.kubectl("logs", pod) + @contextmanager def forward(self, svc: str, ports: Mapping[int, int]): pod = self.svc_pod(svc) @@ -88,10 +91,14 @@ def forward(self, svc: str, ports: Mapping[int, int]): try: yield proc finally: - logger.debug("terminating port-forward") - proc.send_signal(signal.SIGINT) - proc.wait() - logger.debug("port-forward terminated") + logger.debug("terminating port-forward: %s", svc) + proc.terminate() + try: + proc.wait(1) + except subprocess.TimeoutExpired: + logger.info("timeout expired. killing port-forward: %s", svc) + proc.kill() + logger.debug("port-forward terminated: %s", svc) @lru_cache(maxsize=32) def svc_pod(self, svc: str) -> str: From 30c99139b04ca9f85039cb067d91498f9a54629e Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Mon, 25 Nov 2024 12:01:29 -0500 Subject: [PATCH 08/22] Test directory with TLS --- ...rectory.values.yaml => no-tls.values.yaml} | 0 test/directory/tests.yaml | 55 ++++++++++++++++++- test/directory/tls.values.yaml | 46 ++++++++++++++++ tools/ktest/ktest.py | 11 +++- tools/ktest/model.py | 9 +-- tools/ktest/namespace.py | 8 ++- 6 files changed, 120 insertions(+), 9 deletions(-) rename test/directory/{directory.values.yaml => no-tls.values.yaml} (100%) create mode 100644 test/directory/tls.values.yaml diff --git a/test/directory/directory.values.yaml b/test/directory/no-tls.values.yaml similarity index 100% rename from test/directory/directory.values.yaml rename to test/directory/no-tls.values.yaml diff --git a/test/directory/tests.yaml b/test/directory/tests.yaml index 0186ec3..c746d2c 100644 --- a/test/directory/tests.yaml +++ b/test/directory/tests.yaml @@ -26,7 +26,7 @@ tests: file: $SSH_PUBLIC_KEY deployments: - chart: directory - values: directory.values.yaml + values: no-tls.values.yaml ports: 2222: 2222 8282: 8282 @@ -44,3 +44,56 @@ tests: - > ssh -i ${SSH_PRIVATE_KEY:-$(ls -1 ~/.ssh/id_* | head -1)} -p 2222 -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR localhost provision tenant test --id 3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1 --delete + - name: directory-tls + pull_secret: $GITHUB_TOKEN + secrets: + - name: pg-credentials + values: + username: postgres + password: $POSTGRES_PASSWORD + - name: pg-root-reader-credentials + values: + username: root_reader + password: root_reader + - name: pg-tenant-reader-credentials + values: + username: tenant_reader + password: tenant_reader + - name: test-tenant-keys + values: + reader: apikey_tenant_reader + writer: apikey_tenant_writer + - name: grpc-cert + files: + tls.crt: $TOPAZ_CERTS_DIR/grpc.crt + tls.key: $TOPAZ_CERTS_DIR/grpc.key + ca.crt: $TOPAZ_CERTS_DIR/grpc-ca.crt + - name: gateway-cert + files: + tls.crt: $TOPAZ_CERTS_DIR/gateway.crt + tls.key: $TOPAZ_CERTS_DIR/gateway.key + config_maps: + - name: directory-admin-keys + keys: + - name: authorized_keys + file: $SSH_PUBLIC_KEY + deployments: + - chart: directory + values: tls.values.yaml + ports: + 2222: 2222 + 8282: 8282 + run: + - > + ssh -i ${SSH_PRIVATE_KEY:-$(ls -1 ~/.ssh/id_* | head -1)} -p 2222 -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR + localhost provision root-keys + - > + ssh -i ${SSH_PRIVATE_KEY:-$(ls -1 ~/.ssh/id_* | head -1)} -p 2222 -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR + localhost provision tenant test --id 3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1 + - > + ${TOPAZ:-topaz} ds get manifest -H localhost:8282 --tenant-id 3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1 + -k apikey_tenant_reader --stdout --insecure + cleanup: + - > + ssh -i ${SSH_PRIVATE_KEY:-$(ls -1 ~/.ssh/id_* | head -1)} -p 2222 -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR + localhost provision tenant test --id 3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1 --delete diff --git a/test/directory/tls.values.yaml b/test/directory/tls.values.yaml new file mode 100644 index 0000000..95ded50 --- /dev/null +++ b/test/directory/tls.values.yaml @@ -0,0 +1,46 @@ +--- +image: + tag: 0.33.2-3e32438c-amd64 + +imagePullSecrets: + - name: ghcr-creds + +rootDirectory: + database: + host: postgresql.postgres.svc.cluster.local + dbName: test-root-ds + sslMode: disable + admin: + credentialsSecret: pg-credentials + reader: + credentialsSecret: pg-root-reader-credentials + +tenantDirectory: + database: + host: postgresql.postgres.svc.cluster.local + dbName: test-ds + sslMode: disable + admin: + credentialsSecret: pg-credentials + reader: + credentialsSecret: pg-tenant-reader-credentials + +cache: + sizeMB: 100 + +sshAdminKeys: + configMap: + name: directory-admin-keys + key: authorized_keys + +tenants: + - name: test + id: 3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1 + keysSecret: + name: test-tenant-keys + +grpc: + certSecret: grpc-cert + +https: + certSecret: gateway-cert diff --git a/tools/ktest/ktest.py b/tools/ktest/ktest.py index 769242d..2109d31 100755 --- a/tools/ktest/ktest.py +++ b/tools/ktest/ktest.py @@ -184,8 +184,13 @@ def git_root(from_path: str) -> str: repo = git.Repo(from_path, search_parent_directories=True) return repo.git.rev_parse("--show-toplevel") + def init_logging(level=logging.INFO): - loggers = (logging.getLogger(name) for name in logging.root.manager.loggerDict if name.startswith("k3test")) + loggers = ( + logging.getLogger(name) + for name in logging.root.manager.loggerDict + if name.startswith("k3test") + ) for logger in loggers: init_logger(logger, level) @@ -198,7 +203,9 @@ def init_logger(logger: logging.Logger, level=logging.INFO): ch.setLevel(logging.DEBUG) # create formatter - formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s") + formatter = logging.Formatter( + "%(asctime)s - %(levelname)s - %(name)s - %(message)s" + ) # add formatter to ch ch.setFormatter(formatter) diff --git a/tools/ktest/model.py b/tools/ktest/model.py index d0155f3..980ba8d 100644 --- a/tools/ktest/model.py +++ b/tools/ktest/model.py @@ -1,5 +1,6 @@ from dataclasses import dataclass -from pydantic import BaseModel +from typing import Optional +from pydantic import BaseModel, Field @dataclass @@ -14,10 +15,10 @@ class ConfigMap: keys: list[ConfigMapKey] -@dataclass -class Secret: +class Secret(BaseModel): name: str - values: dict[str, str] + values: dict[str, str] = Field(default_factory=lambda: {}) + files: dict[str, str] = Field(default_factory=lambda: {}) @dataclass diff --git a/tools/ktest/namespace.py b/tools/ktest/namespace.py index 0cc4aba..11f227a 100644 --- a/tools/ktest/namespace.py +++ b/tools/ktest/namespace.py @@ -38,15 +38,19 @@ def ns_exists(self): return False def create_secret(self, secret: Secret): - keys = ( + literals = ( f"--from-literal={k}={path.expandvars(v)}" for k, v in secret.values.items() ) + files = ( + f"--from-file={k}={path.expandvars(v)}" for k, v in secret.files.items() + ) self.kubectl( "create", "secret", "generic", secret.name, - *keys, + *literals, + *files, ) def create_config_map(self, config_map: ConfigMap): From c2f3285b5baa9fb77ef1b68692da79a3507de0b6 Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Mon, 25 Nov 2024 12:41:25 -0500 Subject: [PATCH 09/22] Bump aserto-lib to 0.2.0 and use relative path to import --- charts/aserto-lib/Chart.yaml | 2 +- charts/directory/Chart.yaml | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/charts/aserto-lib/Chart.yaml b/charts/aserto-lib/Chart.yaml index 1bcdf06..30f0ac6 100644 --- a/charts/aserto-lib/Chart.yaml +++ b/charts/aserto-lib/Chart.yaml @@ -21,7 +21,7 @@ type: library # 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.5 +version: 0.2.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/directory/Chart.yaml b/charts/directory/Chart.yaml index 368df02..bdadf9d 100644 --- a/charts/directory/Chart.yaml +++ b/charts/directory/Chart.yaml @@ -31,6 +31,5 @@ appVersion: "0.33.1" dependencies: - name: aserto-lib - version: ~0.1.5 - # repository: oci://ghcr.io/aserto-dev/helm + version: 0.2.0 repository: file://../aserto-lib From 122ec237099b3efc7569d6ee9655b20cec61f49c Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Mon, 25 Nov 2024 13:09:37 -0500 Subject: [PATCH 10/22] Bump directory to 0.2.0 --- charts/directory/Chart.lock | 6 +++--- charts/directory/Chart.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/charts/directory/Chart.lock b/charts/directory/Chart.lock index a93cd8e..a090ca4 100644 --- a/charts/directory/Chart.lock +++ b/charts/directory/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: aserto-lib repository: file://../aserto-lib - version: 0.1.5 -digest: sha256:26ffec3faa8dafcb519fd066e04d0d6c33dffaf8aa4052a39166500fb0343b66 -generated: "2024-11-22T09:31:01.633697-05:00" + version: 0.2.0 +digest: sha256:e847ea16d4c0c170655af988461152ab61eed5372f1639769dd7d198346da272 +generated: "2024-11-25T13:09:10.771435-05:00" diff --git a/charts/directory/Chart.yaml b/charts/directory/Chart.yaml index bdadf9d..fca0e77 100644 --- a/charts/directory/Chart.yaml +++ b/charts/directory/Chart.yaml @@ -21,7 +21,7 @@ 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.9 +version: 0.2.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to From 584269a3bba973229a5f85548125a09950d1d0c4 Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Mon, 25 Nov 2024 13:51:46 -0500 Subject: [PATCH 11/22] Move end-to-end tests into chart/directory/tests dir --- .github/workflows/ci.yaml | 8 +++-- charts/directory/.helmignore | 1 + .../directory/test}/no-tls.values.yaml | 0 .../directory/test}/tests.yaml | 0 .../directory/test}/tls.values.yaml | 0 tools/ktest/ktest.py | 33 +++---------------- tools/ktest/namespace.py | 27 ++++++++++----- 7 files changed, 31 insertions(+), 38 deletions(-) rename {test/directory => charts/directory/test}/no-tls.values.yaml (100%) rename {test/directory => charts/directory/test}/tests.yaml (100%) rename {test/directory => charts/directory/test}/tls.values.yaml (100%) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c97d46f..1e68aa7 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -61,11 +61,15 @@ jobs: chmod +x ./bin/topaz ./bin/topaz version echo "TOPAZ=$(realpath ./bin/topaz)" >> "$GITHUB_ENV" + echo "TOPAZ_CERTS_DIR=$(./bin/topaz config info | jq '.config.topaz_certs_dir' -r)" >> "$GITHUB_ENV" - name: Install topazd container run: | ${TOPAZ} install --container-tag=${{ env.TOPAZ_VERSION }} ${TOPAZ} version + - + name: Generate topaz certs + run: ${TOPAZ} certs generate - name: Install uv package manager uses: astral-sh/setup-uv@v3 @@ -112,6 +116,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SSH_PUBLIC_KEY: ${{ steps.sshkey.outputs.public_key }} SSH_PRIVATE_KEY: ${{ steps.sshkey.outputs.private_key }} + TOPAZ_CERTS_DIR: ${{ env.TOPAZ_CERTS_DIR }} run: | - cd tools/ktest - uv run ktest.py ../../test/directory/tests.yaml + uv run --project tools/ktest tools/ktest/ktest.py charts/directory/test/tests.yaml diff --git a/charts/directory/.helmignore b/charts/directory/.helmignore index c1c4640..9038f21 100644 --- a/charts/directory/.helmignore +++ b/charts/directory/.helmignore @@ -24,3 +24,4 @@ build/ ci/ +test/ diff --git a/test/directory/no-tls.values.yaml b/charts/directory/test/no-tls.values.yaml similarity index 100% rename from test/directory/no-tls.values.yaml rename to charts/directory/test/no-tls.values.yaml diff --git a/test/directory/tests.yaml b/charts/directory/test/tests.yaml similarity index 100% rename from test/directory/tests.yaml rename to charts/directory/test/tests.yaml diff --git a/test/directory/tls.values.yaml b/charts/directory/test/tls.values.yaml similarity index 100% rename from test/directory/tls.values.yaml rename to charts/directory/test/tls.values.yaml diff --git a/tools/ktest/ktest.py b/tools/ktest/ktest.py index 2109d31..0d8631c 100755 --- a/tools/ktest/ktest.py +++ b/tools/ktest/ktest.py @@ -16,7 +16,7 @@ from model import Deployment, Spec, Test from namespace import Namespace -logger = logging.getLogger("k3stest") +logger = logging.getLogger(__name__) COLOR_HARNESS = "blue" COLOR_STEP = "magenta" @@ -120,8 +120,8 @@ def execute_cleanup(self): echo("🧹", step, cl=COLOR_STEP) try: self.subprocess(step) - except: - pass + except Exception as e: + logger.error("cleanup failed: %s", e) def set_image_pull_secret(self, ns: Namespace): if self.test.pull_secret: @@ -186,33 +186,10 @@ def git_root(from_path: str) -> str: def init_logging(level=logging.INFO): - loggers = ( - logging.getLogger(name) - for name in logging.root.manager.loggerDict - if name.startswith("k3test") - ) - for logger in loggers: - init_logger(logger, level) - - -def init_logger(logger: logging.Logger, level=logging.INFO): - logger.setLevel(level) - - # create console handler and set level to debug - ch = logging.StreamHandler() - ch.setLevel(logging.DEBUG) - - # create formatter - formatter = logging.Formatter( - "%(asctime)s - %(levelname)s - %(name)s - %(message)s" + logging.basicConfig( + format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", level=level ) - # add formatter to ch - ch.setFormatter(formatter) - - # add ch to logger - logger.addHandler(ch) - if __name__ == "__main__": main() diff --git a/tools/ktest/namespace.py b/tools/ktest/namespace.py index 11f227a..3f90186 100644 --- a/tools/ktest/namespace.py +++ b/tools/ktest/namespace.py @@ -13,7 +13,7 @@ from model import ConfigMap, Secret -logger = logging.getLogger("k3test.namespace") +logger = logging.getLogger(__name__) class Namespace: @@ -96,13 +96,24 @@ def forward(self, svc: str, ports: Mapping[int, int]): yield proc finally: logger.debug("terminating port-forward: %s", svc) - proc.terminate() - try: - proc.wait(1) - except subprocess.TimeoutExpired: - logger.info("timeout expired. killing port-forward: %s", svc) - proc.kill() - logger.debug("port-forward terminated: %s", svc) + for stop in (proc.terminate, proc.kill): + logger.debug("attempting to '%s' port-forward: %s", stop.__name__, svc) + stop() + try: + proc.communicate(timeout=3) + logger.debug("port-forward exited: %s", svc) + # wait for the port to become available again + time.sleep(3) + break + except subprocess.TimeoutExpired: + logger.info("timeout expired waiting for: %s", svc) + + if proc.returncode is None: + logger.debug("unable to stop port-forwarding: %s", svc) + else: + logger.debug( + "port-forward terminated: %s. exit code: %d", svc, proc.returncode + ) @lru_cache(maxsize=32) def svc_pod(self, svc: str) -> str: From e3a1c5cef0b77c47252b2a39678a6259f0b73685 Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Mon, 25 Nov 2024 18:43:45 -0500 Subject: [PATCH 12/22] Remove noTLS option in Topaz chart To enable TLS, just specify the certs you want the service to use. --- charts/directory/test/no-tls.values.yaml | 2 +- charts/directory/test/tests.yaml | 1 + charts/directory/test/tls.values.yaml | 2 +- charts/topaz/.helmignore | 1 + charts/topaz/Chart.lock | 8 +++--- charts/topaz/Chart.yaml | 8 +++--- charts/topaz/templates/_helpers.tpl | 15 ++++++++--- charts/topaz/templates/config.yaml | 8 ------ charts/topaz/templates/deployment.yaml | 21 +++++++--------- charts/topaz/test/tests.yaml | 32 ++++++++++++++++++++++++ charts/topaz/test/tls.values.yaml | 4 +++ charts/topaz/values.yaml | 15 +++++------ tools/ktest/ktest.py | 8 ++++-- tools/ktest/model.py | 17 ++++++------- 14 files changed, 88 insertions(+), 54 deletions(-) create mode 100644 charts/topaz/test/tests.yaml create mode 100644 charts/topaz/test/tls.values.yaml diff --git a/charts/directory/test/no-tls.values.yaml b/charts/directory/test/no-tls.values.yaml index d08d30c..ccae630 100644 --- a/charts/directory/test/no-tls.values.yaml +++ b/charts/directory/test/no-tls.values.yaml @@ -1,6 +1,6 @@ --- image: - tag: 0.33.2-3e32438c-amd64 + tag: 0.33.2-3e32438c-arm64 imagePullSecrets: - name: ghcr-creds diff --git a/charts/directory/test/tests.yaml b/charts/directory/test/tests.yaml index c746d2c..42e3945 100644 --- a/charts/directory/test/tests.yaml +++ b/charts/directory/test/tests.yaml @@ -44,6 +44,7 @@ tests: - > ssh -i ${SSH_PRIVATE_KEY:-$(ls -1 ~/.ssh/id_* | head -1)} -p 2222 -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR localhost provision tenant test --id 3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1 --delete + - name: directory-tls pull_secret: $GITHUB_TOKEN secrets: diff --git a/charts/directory/test/tls.values.yaml b/charts/directory/test/tls.values.yaml index 95ded50..2b076cc 100644 --- a/charts/directory/test/tls.values.yaml +++ b/charts/directory/test/tls.values.yaml @@ -1,6 +1,6 @@ --- image: - tag: 0.33.2-3e32438c-amd64 + tag: 0.33.2-3e32438c-arm64 imagePullSecrets: - name: ghcr-creds diff --git a/charts/topaz/.helmignore b/charts/topaz/.helmignore index c1c4640..9038f21 100644 --- a/charts/topaz/.helmignore +++ b/charts/topaz/.helmignore @@ -24,3 +24,4 @@ build/ ci/ +test/ diff --git a/charts/topaz/Chart.lock b/charts/topaz/Chart.lock index 9fb5c27..a8c8296 100644 --- a/charts/topaz/Chart.lock +++ b/charts/topaz/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: aserto-lib - repository: oci://ghcr.io/aserto-dev/helm - version: 0.1.5 -digest: sha256:d4b6f4909c81802d39c520b76bbcd5a1f7f9897d0b20cee02a9978f3a5b14447 -generated: "2024-11-12T16:35:42.713729-05:00" + repository: file://../aserto-lib + version: 0.2.0 +digest: sha256:e847ea16d4c0c170655af988461152ab61eed5372f1639769dd7d198346da272 +generated: "2024-11-25T15:54:03.190999-05:00" diff --git a/charts/topaz/Chart.yaml b/charts/topaz/Chart.yaml index 31f448c..12ff31f 100644 --- a/charts/topaz/Chart.yaml +++ b/charts/topaz/Chart.yaml @@ -21,15 +21,15 @@ 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.7 +version: 0.2.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. 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: "0.32.33" +appVersion: "0.32.37" dependencies: - name: aserto-lib - version: ~0.1.5 - repository: oci://ghcr.io/aserto-dev/helm + version: 0.2.0 + repository: file://../aserto-lib diff --git a/charts/topaz/templates/_helpers.tpl b/charts/topaz/templates/_helpers.tpl index 8ddaf13..470376e 100644 --- a/charts/topaz/templates/_helpers.tpl +++ b/charts/topaz/templates/_helpers.tpl @@ -233,6 +233,12 @@ Topaz API key configuration {{- $cfg := merge (dig $svc "grpc" dict $values.serviceOverrides) $values.grpc -}} listen_address: 0.0.0.0:{{ ($values.ports).grpc | default "8282" }} connection_timeout_seconds: {{ $cfg.connectionTimeoutSec | default "2" }} +{{- with ($values.tls).grpc }} +certs: + tls_key_path: /grpc-certs/tls.key + tls_cert_path: /grpc-certs/tls.crt + tls_ca_cert_path: /grpc-certs/ca.crt +{{- end }} {{- end }} @@ -264,13 +270,16 @@ allowed_methods: allowed_origins: {{- $origins | toYaml | nindent 2 }} -{{- if $cfg.noTLS }} -http: true -{{- end }} read_timeout: {{ $cfg.readTimeout | default "2s" }} read_header_timeout: {{ $cfg.readHeaderTimeout | default "2s" }} write_timeout: {{ $cfg.writeTimeout | default "2s" }} idle_timeout: {{ $cfg.idleTimeout | default "30s" }} +{{- with ($values.tls).https }} +certs: + tls_key_path: /https-certs/tls.key + tls_cert_path: /https-certs/tls.crt + tls_ca_cert_path: /https-certs/ca.crt +{{- end }} {{- end }} diff --git a/charts/topaz/templates/config.yaml b/charts/topaz/templates/config.yaml index bbcad26..0cf218b 100644 --- a/charts/topaz/templates/config.yaml +++ b/charts/topaz/templates/config.yaml @@ -71,16 +71,8 @@ stringData: {{- end }} grpc: {{- include "topaz.grpcService" (list $.Values .) | nindent 12 }} - certs: - tls_key_path: /grpc-certs/tls.key - tls_cert_path: /grpc-certs/tls.crt - tls_ca_cert_path: /grpc-certs/ca.crt gateway: {{- include "topaz.gatewayService" (list $.Values .) | nindent 12 }} - certs: - tls_key_path: /https-certs/tls.key - tls_cert_path: /https-certs/tls.crt - tls_ca_cert_path: /https-certs/ca.crt {{- end }} {{- if (.Values.decisionLogs).enabled }} diff --git a/charts/topaz/templates/deployment.yaml b/charts/topaz/templates/deployment.yaml index 9b3952c..68dce6b 100644 --- a/charts/topaz/templates/deployment.yaml +++ b/charts/topaz/templates/deployment.yaml @@ -1,3 +1,4 @@ +--- apiVersion: apps/v1 kind: {{ include "topaz.appKind" . }} metadata: @@ -79,16 +80,16 @@ spec: - name: opa mountPath: /opa-persistence readOnly: false + {{- if (.Values.tls).grpc }} - name: grpc-certs mountPath: /grpc-certs - {{- if (.Values.certs).grpc }} readOnly: true - {{- end }} + {{- end }} + {{- if (.Values.tls).https }} - name: https-certs mountPath: /https-certs - {{- if (.Values.certs).https }} readOnly: true - {{- end }} + {{- end }} {{- with (.Values.directory).remote -}} {{ include "topaz.remoteDirectoryCertVolumeMount" . | nindent 12 }} {{- end }} @@ -125,20 +126,16 @@ spec: items: - key: config.yaml path: config.yaml + {{- with (.Values.tls).grpc }} - name: grpc-certs - {{- with (.Values.certs).grpc }} secret: secretName: {{ . }} - {{- else }} - emptyDir: {} - {{- end }} + {{- end }} + {{- with (.Values.tls).https }} - name: https-certs - {{- with (.Values.certs).https }} secret: secretName: {{ . }} - {{- else }} - emptyDir: {} - {{- end }} + {{- end }} {{- if ((.Values.directory).remote).address -}} {{ include "topaz.remoteDirectoryCertVolume" . | nindent 8 }} {{- end }} diff --git a/charts/topaz/test/tests.yaml b/charts/topaz/test/tests.yaml new file mode 100644 index 0000000..ddaab7d --- /dev/null +++ b/charts/topaz/test/tests.yaml @@ -0,0 +1,32 @@ +--- +tests: + - name: topaz-no-tls + deployments: + - chart: topaz + ports: + 8282: 8282 + 8383: 8383 + run: + - ${TOPAZ:-topaz} ds get manifest -H localhost:8282 --stdout --plaintext + - curl -s http://localhost:8383/api/v2/policies > /dev/null + + - name: topaz-tls + secrets: + - name: grpc-cert + files: + tls.crt: $TOPAZ_CERTS_DIR/grpc.crt + tls.key: $TOPAZ_CERTS_DIR/grpc.key + ca.crt: $TOPAZ_CERTS_DIR/grpc-ca.crt + - name: gateway-cert + files: + tls.crt: $TOPAZ_CERTS_DIR/gateway.crt + tls.key: $TOPAZ_CERTS_DIR/gateway.key + deployments: + - chart: topaz + values: tls.values.yaml + ports: + 8282: 8282 + 8383: 8383 + run: + - ${TOPAZ:-topaz} ds get manifest -H localhost:8282 --stdout --insecure --no-check + - curl -ks https://localhost:8383/api/v2/policies > /dev/null diff --git a/charts/topaz/test/tls.values.yaml b/charts/topaz/test/tls.values.yaml new file mode 100644 index 0000000..c22c80a --- /dev/null +++ b/charts/topaz/test/tls.values.yaml @@ -0,0 +1,4 @@ +--- +tls: + grpc: grpc-cert + https: gateway-cert diff --git a/charts/topaz/values.yaml b/charts/topaz/values.yaml index fd21061..c028063 100644 --- a/charts/topaz/values.yaml +++ b/charts/topaz/values.yaml @@ -198,13 +198,12 @@ ports: health: 8484 metrics: 8585 -# [Optional] cutsom TLS certificates for the gRPC and HTTPS servers. -# If not provided, topaz generates self-signed certificates. -# To use your own certificates provide the names of secrets -# of type kubernetes.io/tls -certs: {} - # grpc: topaz-grpc-cert - # https: topaz-https-cert +# TLS configuration. +# To run topaz with TLS, provide the names of Kuebernetes secrets of type kubernetes.io/tls +# If not provided, topaz runs without TLS. +# tls: +# grpc: topaz-grpc-cert +# https: topaz-https-cert # Metrics configuration metrics: @@ -225,8 +224,6 @@ http: # if specified, the domain will automatically be added to the allowedOrigins list. # e.g. 'domain: https://topaz.example.com:8383' domain: "" - # if true, services are exposed over HTTP instead of HTTPS. - noTLS: false # HTTP server timeouts. # See https://golang.org/pkg/net/http/#Server readTimeout: 2s diff --git a/tools/ktest/ktest.py b/tools/ktest/ktest.py index 0d8631c..f447583 100755 --- a/tools/ktest/ktest.py +++ b/tools/ktest/ktest.py @@ -81,12 +81,16 @@ def run(self): def deploy_chart(self, deployment: Deployment, ns: Namespace): chart_path = path.join(self.git_root, "charts", deployment.chart) ns.helm("dep", "build", chart_path) + values = ( + ["-f", path.join(self.spec_path, deployment.values)] + if deployment.values + else [] + ) ns.helm( "install", deployment.chart, chart_path, - "-f", - path.join(self.spec_path, deployment.values), + *values, ) def wait_for_deployments(self, deployments: Sequence[Deployment], ns: Namespace): diff --git a/tools/ktest/model.py b/tools/ktest/model.py index 980ba8d..9b84a10 100644 --- a/tools/ktest/model.py +++ b/tools/ktest/model.py @@ -1,5 +1,4 @@ from dataclasses import dataclass -from typing import Optional from pydantic import BaseModel, Field @@ -21,22 +20,20 @@ class Secret(BaseModel): files: dict[str, str] = Field(default_factory=lambda: {}) -@dataclass -class Deployment: +class Deployment(BaseModel): chart: str - values: str + values: str = Field(default="") ports: dict[int, int] -@dataclass -class Test: +class Test(BaseModel): name: str - pull_secret: str - secrets: list[Secret] - config_maps: list[ConfigMap] + pull_secret: str = Field(default="") + secrets: list[Secret] = Field(default_factory=lambda: []) + config_maps: list[ConfigMap] = Field(default_factory=lambda: []) deployments: list[Deployment] run: list[str] - cleanup: list[str] + cleanup: list[str] = Field(default_factory=lambda: []) class Spec(BaseModel): From 281e42363c30f0512852c54d947a762184b828dd Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Mon, 25 Nov 2024 18:47:38 -0500 Subject: [PATCH 13/22] Run topaz tests in CI --- .github/workflows/ci.yaml | 7 +++++++ charts/directory/test/no-tls.values.yaml | 2 +- charts/directory/test/tls.values.yaml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1e68aa7..e6f5e74 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -83,6 +83,13 @@ jobs: args: > --agents 1 --k3s-arg "--disable=metrics-server@server:*" + - + name: Test Topaz + timeout-minutes: 10 + env: + TOPAZ_CERTS_DIR: ${{ env.TOPAZ_CERTS_DIR }} + run: | + uv run --project tools/ktest tools/ktest/ktest.py charts/topaz/test/tests.yaml - name: Deploy Postgres run: | diff --git a/charts/directory/test/no-tls.values.yaml b/charts/directory/test/no-tls.values.yaml index ccae630..d08d30c 100644 --- a/charts/directory/test/no-tls.values.yaml +++ b/charts/directory/test/no-tls.values.yaml @@ -1,6 +1,6 @@ --- image: - tag: 0.33.2-3e32438c-arm64 + tag: 0.33.2-3e32438c-amd64 imagePullSecrets: - name: ghcr-creds diff --git a/charts/directory/test/tls.values.yaml b/charts/directory/test/tls.values.yaml index 2b076cc..95ded50 100644 --- a/charts/directory/test/tls.values.yaml +++ b/charts/directory/test/tls.values.yaml @@ -1,6 +1,6 @@ --- image: - tag: 0.33.2-3e32438c-arm64 + tag: 0.33.2-3e32438c-amd64 imagePullSecrets: - name: ghcr-creds From ce77d90960a52c00e2d371e986dca22a579fda05 Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Mon, 25 Nov 2024 18:55:34 -0500 Subject: [PATCH 14/22] Fix directory lint --- charts/directory/ci/tenants-values.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/charts/directory/ci/tenants-values.yaml b/charts/directory/ci/tenants-values.yaml index 232ee4f..ac8c1c6 100644 --- a/charts/directory/ci/tenants-values.yaml +++ b/charts/directory/ci/tenants-values.yaml @@ -5,8 +5,9 @@ rootDirectory: tenantDirectory: database: host: tenant-db-host -sshAdminKeys: | - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDf6 +sshAdminKeys: + keys: | + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDf6 tenants: - name: staging id: 3dbaa470-9c7e-11ef-bf36-00fcb2a75cb1 From 5054425c114d2d701dea89223b88269b9726192d Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Mon, 25 Nov 2024 19:00:55 -0500 Subject: [PATCH 15/22] Add serviceName to topaz stateful set --- charts/topaz/templates/deployment.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/charts/topaz/templates/deployment.yaml b/charts/topaz/templates/deployment.yaml index 68dce6b..a0e3b84 100644 --- a/charts/topaz/templates/deployment.yaml +++ b/charts/topaz/templates/deployment.yaml @@ -6,6 +6,7 @@ metadata: labels: {{- include "topaz.labels" . | nindent 4 }} spec: + serviceName: {{ include "topaz.fullname" . }} {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount }} {{- end }} From c551d40da377cacca5891b7a7f5e559f8f69dcff Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Mon, 25 Nov 2024 19:13:29 -0500 Subject: [PATCH 16/22] Sesnd SIGINT to end port-forward --- tools/ktest/namespace.py | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/tools/ktest/namespace.py b/tools/ktest/namespace.py index 3f90186..96f9650 100644 --- a/tools/ktest/namespace.py +++ b/tools/ktest/namespace.py @@ -80,13 +80,10 @@ def forward(self, svc: str, ports: Mapping[int, int]): pod = self.svc_pod(svc) port_mapping = tuple(f"{k}:{v}" for k, v in ports.items()) logger.debug("port-mapping: %s", port_mapping) - args = " ".join( - ("kubectl", "-n", self.namespace, "port-forward", pod) + port_mapping - ) + args = (kctl_path(), "-n", self.namespace, "port-forward", pod) + port_mapping logger.debug("port-forward: %s", args) proc = subprocess.Popen( args=args, - shell=True, stdout=subprocess.DEVNULL, ) @@ -96,14 +93,16 @@ def forward(self, svc: str, ports: Mapping[int, int]): yield proc finally: logger.debug("terminating port-forward: %s", svc) - for stop in (proc.terminate, proc.kill): + + def ctrl_c(): + proc.send_signal(signal.SIGINT) + + for stop in (ctrl_c, proc.terminate, proc.kill): logger.debug("attempting to '%s' port-forward: %s", stop.__name__, svc) stop() try: - proc.communicate(timeout=3) + proc.wait(timeout=15) logger.debug("port-forward exited: %s", svc) - # wait for the port to become available again - time.sleep(3) break except subprocess.TimeoutExpired: logger.info("timeout expired waiting for: %s", svc) @@ -132,15 +131,27 @@ def svc_pod(self, svc: str) -> str: if proc.returncode != 0: proc.check_returncode() - return proc.stdout.decode() + return proc.stdout.decode().strip("''") def helm(self, *args, check=True): args = ("helm", "-n", self.namespace) + args return subprocess.run(args=" ".join(args), shell=True, check=check) -def kubectl(*args, check: bool = True, capture_output: bool = False): - args = ("kubectl",) + args - return subprocess.run( - args=" ".join(args), check=check, shell=True, capture_output=capture_output +@lru_cache(maxsize=1) +def kctl_path(): + return ( + subprocess.run( + args="which kubectl", + shell=True, + check=True, + capture_output=True, + ) + .stdout.decode() + .strip() ) + + +def kubectl(*args, check: bool = True, capture_output: bool = False): + args = (kctl_path(),) + args + return subprocess.run(args=args, check=check, capture_output=capture_output) From f598c7776735ec13bd9509cbcdf8c5f8be0c00da Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Tue, 26 Nov 2024 09:55:09 -0500 Subject: [PATCH 17/22] LInt topaz values.yaml --- charts/topaz/values.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/charts/topaz/values.yaml b/charts/topaz/values.yaml index c028063..80f6b31 100644 --- a/charts/topaz/values.yaml +++ b/charts/topaz/values.yaml @@ -1,3 +1,4 @@ +--- # Default values for topaz. # This is a YAML-formatted file. # Declare variables to be passed into your templates. From d28675e6d880d86a886009f8ce240c6f7ac67926 Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Tue, 26 Nov 2024 12:13:10 -0500 Subject: [PATCH 18/22] Document topaz TLS configuration --- charts/topaz/README.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/charts/topaz/README.md b/charts/topaz/README.md index 2ec2acd..5fba4fe 100644 --- a/charts/topaz/README.md +++ b/charts/topaz/README.md @@ -68,9 +68,6 @@ topaz: The default [values.yaml](https://github.com/aserto-dev/helm/blob/main/charts/topaz/values.yaml) is a good starting point for configuring topaz. -The only required configuration element that does not have a default value is `opa.poilcy` that -must either specify a policy image to load from an OCI registry, or point to a discovery service -for dynamic configuration. The following sections describe the various configuration options available in the chart. @@ -428,3 +425,16 @@ can list all policy modules using: ```shell curl -k -H "Authorization: Basic " https://localhost:8383/api/v2/policies ``` + +## TLS + +By default, the topaz gRPC and HTTP services run without TLS. To enable TLS you must provide certificates +for the services. +Certificates are read from Kubernetes secrets of type `kubernetes.io/tls` defined in the `tls` section +of `values.yaml`: + +```yaml +tls: + grpc: "" + https: "" +``` From bfe7a867c32af7caf076c6b1c23970a4a65b4648 Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Tue, 26 Nov 2024 12:36:25 -0500 Subject: [PATCH 19/22] Bump chart versions --- charts/aserto/Chart.lock | 24 ++++++++++++------------ charts/aserto/Chart.yaml | 22 +++++++++++----------- charts/aserto/ci/test-values.yaml | 5 +++-- charts/authorizer/Chart.lock | 8 ++++---- charts/authorizer/Chart.yaml | 6 +++--- charts/console/Chart.lock | 8 ++++---- charts/console/Chart.yaml | 8 ++++---- charts/discovery/Chart.lock | 8 ++++---- charts/discovery/Chart.yaml | 6 +++--- charts/scim/Chart.lock | 8 ++++---- charts/scim/Chart.yaml | 6 +++--- 11 files changed, 55 insertions(+), 54 deletions(-) diff --git a/charts/aserto/Chart.lock b/charts/aserto/Chart.lock index ae9d356..4aca7ae 100644 --- a/charts/aserto/Chart.lock +++ b/charts/aserto/Chart.lock @@ -1,18 +1,18 @@ dependencies: - name: directory - repository: oci://ghcr.io/aserto-dev/helm - version: 0.1.9 + repository: file://../directory + version: 0.2.0 - name: authorizer - repository: oci://ghcr.io/aserto-dev/helm - version: 0.1.7 + repository: file://../authorizer + version: 0.1.8 - name: discovery - repository: oci://ghcr.io/aserto-dev/helm - version: 0.1.6 + repository: file://../discovery + version: 0.1.7 - name: console - repository: oci://ghcr.io/aserto-dev/helm - version: 0.1.6 + repository: file://../console + version: 0.1.7 - name: scim - repository: oci://ghcr.io/aserto-dev/helm - version: 0.1.5 -digest: sha256:980022ba59e0ff9d2eef12e29607db9c7f579ded5286bf71e63d4181863d530d -generated: "2024-11-12T16:41:28.881217-05:00" + repository: file://../scim + version: 0.1.6 +digest: sha256:7b2bbedd56cd70a19d32bdc8781030556e0a5bfcbea61f0503f59ba4b8aab57d +generated: "2024-11-26T12:35:24.618924-05:00" diff --git a/charts/aserto/Chart.yaml b/charts/aserto/Chart.yaml index 81339d4..0c6757b 100644 --- a/charts/aserto/Chart.yaml +++ b/charts/aserto/Chart.yaml @@ -21,7 +21,7 @@ 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.10 +version: 0.1.11 # 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 @@ -31,17 +31,17 @@ appVersion: "0.1.0" dependencies: - name: directory - version: ~0.1.9 - repository: oci://ghcr.io/aserto-dev/helm + version: 0.2.0 + repository: file://../directory - name: authorizer - version: ~0.1.7 - repository: oci://ghcr.io/aserto-dev/helm + version: 0.1.8 + repository: file://../authorizer - name: discovery - version: ~0.1.6 - repository: oci://ghcr.io/aserto-dev/helm + version: 0.1.7 + repository: file://../discovery - name: console - version: ~0.1.6 - repository: oci://ghcr.io/aserto-dev/helm + version: 0.1.7 + repository: file://../console - name: scim - version: ~0.1.5 - repository: oci://ghcr.io/aserto-dev/helm + version: 0.1.6 + repository: file://../scim diff --git a/charts/aserto/ci/test-values.yaml b/charts/aserto/ci/test-values.yaml index 11af921..32fc254 100644 --- a/charts/aserto/ci/test-values.yaml +++ b/charts/aserto/ci/test-values.yaml @@ -19,5 +19,6 @@ directory: tenantDirectory: database: host: tenant-db-host - sshAdminKeys: | - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDf6 + sshAdminKeys: + keys: | + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDf6 diff --git a/charts/authorizer/Chart.lock b/charts/authorizer/Chart.lock index a45dac6..45a677d 100644 --- a/charts/authorizer/Chart.lock +++ b/charts/authorizer/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: aserto-lib - repository: oci://ghcr.io/aserto-dev/helm - version: 0.1.5 -digest: sha256:d4b6f4909c81802d39c520b76bbcd5a1f7f9897d0b20cee02a9978f3a5b14447 -generated: "2024-11-12T16:32:41.420861-05:00" + repository: file://../aserto-lib + version: 0.2.0 +digest: sha256:e847ea16d4c0c170655af988461152ab61eed5372f1639769dd7d198346da272 +generated: "2024-11-26T12:20:11.279944-05:00" diff --git a/charts/authorizer/Chart.yaml b/charts/authorizer/Chart.yaml index b3a3ea1..eb03047 100644 --- a/charts/authorizer/Chart.yaml +++ b/charts/authorizer/Chart.yaml @@ -21,7 +21,7 @@ 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.7 +version: 0.1.8 # 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 @@ -31,5 +31,5 @@ appVersion: "0.14.8" dependencies: - name: aserto-lib - version: ~0.1.5 - repository: oci://ghcr.io/aserto-dev/helm + version: 0.2.0 + repository: file://../aserto-lib diff --git a/charts/console/Chart.lock b/charts/console/Chart.lock index d28e694..baad547 100644 --- a/charts/console/Chart.lock +++ b/charts/console/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: aserto-lib - repository: oci://ghcr.io/aserto-dev/helm - version: 0.1.5 -digest: sha256:d4b6f4909c81802d39c520b76bbcd5a1f7f9897d0b20cee02a9978f3a5b14447 -generated: "2024-11-12T16:33:55.906929-05:00" + repository: file://../aserto-lib + version: 0.2.0 +digest: sha256:e847ea16d4c0c170655af988461152ab61eed5372f1639769dd7d198346da272 +generated: "2024-11-26T12:31:22.063478-05:00" diff --git a/charts/console/Chart.yaml b/charts/console/Chart.yaml index 11cffef..7058779 100644 --- a/charts/console/Chart.yaml +++ b/charts/console/Chart.yaml @@ -21,15 +21,15 @@ 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.6 +version: 0.1.7 # 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: "0.1.13" +appVersion: "0.1.14" dependencies: - name: aserto-lib - version: ~0.1.5 - repository: oci://ghcr.io/aserto-dev/helm + version: 0.2.0 + repository: file://../aserto-lib diff --git a/charts/discovery/Chart.lock b/charts/discovery/Chart.lock index 25c84ab..a386b76 100644 --- a/charts/discovery/Chart.lock +++ b/charts/discovery/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: aserto-lib - repository: oci://ghcr.io/aserto-dev/helm - version: 0.1.5 -digest: sha256:d4b6f4909c81802d39c520b76bbcd5a1f7f9897d0b20cee02a9978f3a5b14447 -generated: "2024-11-12T16:34:55.39824-05:00" + repository: file://../aserto-lib + version: 0.2.0 +digest: sha256:e847ea16d4c0c170655af988461152ab61eed5372f1639769dd7d198346da272 +generated: "2024-11-26T12:31:46.527788-05:00" diff --git a/charts/discovery/Chart.yaml b/charts/discovery/Chart.yaml index 82f6556..c212734 100644 --- a/charts/discovery/Chart.yaml +++ b/charts/discovery/Chart.yaml @@ -21,7 +21,7 @@ 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.6 +version: 0.1.7 # 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 @@ -31,5 +31,5 @@ appVersion: "0.1.3" dependencies: - name: aserto-lib - version: ~0.1.5 - repository: oci://ghcr.io/aserto-dev/helm + version: 0.2.0 + repository: file://../aserto-lib diff --git a/charts/scim/Chart.lock b/charts/scim/Chart.lock index d4b0cde..2cd0254 100644 --- a/charts/scim/Chart.lock +++ b/charts/scim/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: aserto-lib - repository: oci://ghcr.io/aserto-dev/helm - version: 0.1.5 -digest: sha256:d4b6f4909c81802d39c520b76bbcd5a1f7f9897d0b20cee02a9978f3a5b14447 -generated: "2024-11-12T16:35:17.368622-05:00" + repository: file://../aserto-lib + version: 0.2.0 +digest: sha256:e847ea16d4c0c170655af988461152ab61eed5372f1639769dd7d198346da272 +generated: "2024-11-26T12:32:19.496426-05:00" diff --git a/charts/scim/Chart.yaml b/charts/scim/Chart.yaml index a4a2f10..9293e21 100644 --- a/charts/scim/Chart.yaml +++ b/charts/scim/Chart.yaml @@ -21,7 +21,7 @@ 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.5 +version: 0.1.6 # 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 @@ -31,5 +31,5 @@ appVersion: "0.0.7" dependencies: - name: aserto-lib - version: ~0.1.5 - repository: oci://ghcr.io/aserto-dev/helm + version: 0.2.0 + repository: file://../aserto-lib From 37673f861b67154359f9c624d1389c5fb660a6aa Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Tue, 26 Nov 2024 12:50:18 -0500 Subject: [PATCH 20/22] Remove test-connection.yaml --- charts/aserto/ci/test-values.yaml | 3 +++ .../templates/tests/test-connection.yaml | 17 ----------------- .../templates/tests/test-connection.yaml | 17 ----------------- .../templates/tests/test-connection.yaml | 17 ----------------- .../topaz/templates/tests/test-connection.yaml | 15 --------------- 5 files changed, 3 insertions(+), 66 deletions(-) delete mode 100644 charts/authorizer/templates/tests/test-connection.yaml delete mode 100644 charts/directory/templates/tests/test-connection.yaml delete mode 100644 charts/discovery/templates/tests/test-connection.yaml delete mode 100644 charts/topaz/templates/tests/test-connection.yaml diff --git a/charts/aserto/ci/test-values.yaml b/charts/aserto/ci/test-values.yaml index 32fc254..c1890e7 100644 --- a/charts/aserto/ci/test-values.yaml +++ b/charts/aserto/ci/test-values.yaml @@ -4,14 +4,17 @@ global: oidc: domain: oidc_domain audience: oidc_audience + console: authorizerURL: https://authorizer.aserto.example.com directoryURL: https://directory.aserto.example.com + discovery: registries: ghcr.io: scheme: bearer tokenSecretName: ghcr-token-secret + directory: rootDirectory: database: diff --git a/charts/authorizer/templates/tests/test-connection.yaml b/charts/authorizer/templates/tests/test-connection.yaml deleted file mode 100644 index c0acd11..0000000 --- a/charts/authorizer/templates/tests/test-connection.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "authorizer.fullname" . }}-test-connection" - labels: - {{- include "authorizer.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test -spec: - containers: - - name: grpcurl - image: fullstorydev/grpcurl - args: - - -insecure - - {{ include "authorizer.clusterAddress" . }} - - list - restartPolicy: Never diff --git a/charts/directory/templates/tests/test-connection.yaml b/charts/directory/templates/tests/test-connection.yaml deleted file mode 100644 index 1cdc636..0000000 --- a/charts/directory/templates/tests/test-connection.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "directory.fullname" . }}-test-connection" - labels: - {{- include "directory.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test -spec: - containers: - - name: grpcurl - image: fullstorydev/grpcurl - args: - - -insecure - - {{ include "aserto-lib.directoryAddress" . }} - - list - restartPolicy: Never diff --git a/charts/discovery/templates/tests/test-connection.yaml b/charts/discovery/templates/tests/test-connection.yaml deleted file mode 100644 index c6e1444..0000000 --- a/charts/discovery/templates/tests/test-connection.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "discovery.fullname" . }}-test-connection" - labels: - {{- include "discovery.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test -spec: - containers: - - name: grpcurl - image: fullstorydev/grpcurl - args: - - -insecure - - {{ include "discovery.clusterAddress" . }} - - list - restartPolicy: Never diff --git a/charts/topaz/templates/tests/test-connection.yaml b/charts/topaz/templates/tests/test-connection.yaml deleted file mode 100644 index 184cf74..0000000 --- a/charts/topaz/templates/tests/test-connection.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "topaz.fullname" . }}-test-connection" - labels: - {{- include "topaz.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test -spec: - containers: - - name: wget - image: busybox - command: ['wget'] - args: ['{{ include "topaz.fullname" . }}:{{ .Values.service.port }}'] - restartPolicy: Never From 2b37288a8fd8a7e6f212afddee7724a4dd281a9f Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Tue, 26 Nov 2024 12:53:26 -0500 Subject: [PATCH 21/22] Add aserto-lib dependency in aserto chart --- charts/aserto/Chart.lock | 7 +++++-- charts/aserto/Chart.yaml | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/charts/aserto/Chart.lock b/charts/aserto/Chart.lock index 4aca7ae..b39b060 100644 --- a/charts/aserto/Chart.lock +++ b/charts/aserto/Chart.lock @@ -1,4 +1,7 @@ dependencies: +- name: aserto-lib + repository: file://../aserto-lib + version: 0.2.0 - name: directory repository: file://../directory version: 0.2.0 @@ -14,5 +17,5 @@ dependencies: - name: scim repository: file://../scim version: 0.1.6 -digest: sha256:7b2bbedd56cd70a19d32bdc8781030556e0a5bfcbea61f0503f59ba4b8aab57d -generated: "2024-11-26T12:35:24.618924-05:00" +digest: sha256:d188c2319b1f908c0a8618ad44e8953a62e86230842c85ddbcd1f1966b67c4b5 +generated: "2024-11-26T12:56:40.605962-05:00" diff --git a/charts/aserto/Chart.yaml b/charts/aserto/Chart.yaml index 0c6757b..edde19d 100644 --- a/charts/aserto/Chart.yaml +++ b/charts/aserto/Chart.yaml @@ -30,6 +30,9 @@ version: 0.1.11 appVersion: "0.1.0" dependencies: + - name: aserto-lib + version: 0.2.0 + repository: file://../aserto-lib - name: directory version: 0.2.0 repository: file://../directory From 6e2a468a7c9d9092caabe7a0b9cff3fb25277215 Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Wed, 27 Nov 2024 14:20:38 -0500 Subject: [PATCH 22/22] Set remote_directory.no_tls when no certs are configured --- charts/topaz/Chart.yaml | 4 ++-- charts/topaz/templates/config.yaml | 17 +++++++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/charts/topaz/Chart.yaml b/charts/topaz/Chart.yaml index 12ff31f..7dbc6a7 100644 --- a/charts/topaz/Chart.yaml +++ b/charts/topaz/Chart.yaml @@ -21,13 +21,13 @@ 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.2.0 +version: 0.2.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: "0.32.37" +appVersion: "0.32.38" dependencies: - name: aserto-lib diff --git a/charts/topaz/templates/config.yaml b/charts/topaz/templates/config.yaml index 0cf218b..1e44a52 100644 --- a/charts/topaz/templates/config.yaml +++ b/charts/topaz/templates/config.yaml @@ -15,22 +15,27 @@ stringData: log_level: {{ .Values.logLevel | default "info" }} grpc_log_level: {{ .Values.grpcLogLevel | default "info" }} - {{- if empty ((.Values.directory).remote).address }} + {{- if empty ((.Values.directory).remote).address }} directory: db_path: /db/directory.db request_timeout: {{ ((.Values.directory).edge).openTimeout | default "5s" }} remote_directory: address: 0.0.0.0:{{ (.Values.ports).grpc | default "8282" }} - {{- if (.Values.auth).apiKeys }} + {{- if (.Values.auth).apiKeys }} api_key: {{ include "topaz.apiKeys" . | fromYamlArray | first }} - {{- end }} - ca_cert_path: /grpc-certs/grpc-ca.crt + {{- end }} + insecure: false + {{- if (.Values.tls).grpc }} + ca_cert_path: /grpc-certs/ca.crt + {{- else }} + no_tls: true + {{- end }} timeout_in_seconds: {{ ((.Values.directory).remote).timeoutSeconds | default "5" }} - {{- else -}} + {{- else -}} remote_directory: {{- include "topaz.remoteDirectory" . | nindent 6 }} - {{- end }} + {{- end }} jwt: acceptable_time_skew_seconds: {{ .Values.jwtAcceptableSkewSeconds | default "5"}}