From 00d9f9adab474b0558516343c3b4c4dbd98eed17 Mon Sep 17 00:00:00 2001 From: Marcus Leandro Date: Thu, 21 Mar 2024 14:24:45 -0300 Subject: [PATCH] Enhanced Synchronization and Configuration Handling in Keess (#30) # Enhanced Synchronization and Configuration Handling in Keess This pull request introduces significant enhancements to the Keess application, focusing on improving the reliability of secrets and configmaps synchronization across namespaces and Kubernetes clusters. Additionally, it modernizes the CLI experience and optimizes the Docker image for better performance and usability. ## Key Changes ### Switch from Watch to Poll Method - **Issue**: The previous implementation using the Kubernetes API's watch method occasionally missed important events, leading to synchronization gaps. - **Solution**: Transitioned to a polling mechanism using the list method, ensuring comprehensive coverage of all events without missing updates. ### Adoption of Cobra CLI - **Improvement**: Replaced the existing CLI library with [Cobra](https://github.com/spf13/cobra), a widely adopted framework that offers enhanced features and a more standardized command-line experience. - **Benefits**: Provides better support for command structuring, argument parsing, and documentation, making Keess more accessible and easier to use for the community. ### Docker Image Optimization - **Update**: The Docker image has been revised to use a smaller base image, reducing the overall size and improving the startup time of the application. - **Advantage**: Enhances the efficiency of deploying Keess in diverse environments, from development to production, especially in cloud-native ecosystems with resource constraints. ### Helm Chart Adjustments - **Modification**: Updated the Helm chart to accommodate new command-line options introduced by the switch to Cobra and the changes in application configuration. - **Flexibility**: These updates make deploying Keess with Helm more customizable, allowing users to tailor the deployment to their specific needs. ## Command Line Options The new CLI, powered by Cobra, introduces several command line options to enhance flexibility and usability: - `--config`: Specify the configuration file path (default is `$HOME/.keess.yaml`). - `--logLevel`: Set the logging level (e.g., debug, info, warn). - `--localCluster`: Name of the local Kubernetes cluster. - `--kubeConfigPath`: Path to the kubeconfig file for cluster authentication. - `--namespacePollingInterval`: Interval in seconds to poll for namespace changes. - `--pollingInterval`: Interval in seconds to poll for secrets and configmaps. - `--housekeepingInterval`: Interval in seconds for orphan object cleanup. ### Using Command Line Options To use these options, include them when running Keess commands: ```shell keess run --config /your/config/path.yaml --logLevel info ``` ## Implementation Details - **Commands Integration**: Migrated core application logic into Cobra-based commands, enhancing modularity and testability. - **Configuration Management**: Streamlined configuration handling using Viper, supporting both environment variables and configuration files for greater flexibility. - **Logging and Monitoring**: Upgraded logging mechanism for better clarity and troubleshooting support, crucial for maintaining synchronization integrity. ## How to Test 1. Deploy the updated Keess application using the revised Helm chart. 2. Configure synchronization tasks across namespaces and clusters as needed. 3. Change the `test.py` with the correct values for `source_cluster` and `destination_cluster`. 4. Run `make test`. ## Helm diff ```diff Comparing release=keess, chart=keess/keess keess, keess, ClusterRole (rbac.authorization.k8s.io) has changed: # Source: keess/templates/cluster-role.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: keess labels: - helm.sh/chart: keess-0.2.15 + helm.sh/chart: keess-1.0.0 app.kubernetes.io/name: keess app.kubernetes.io/instance: keess - app.kubernetes.io/version: "0.2.14" + app.kubernetes.io/version: "1.0.0" app.kubernetes.io/managed-by: Helm rules: - apiGroups: [""] resources: - configmaps - secrets verbs: ["get", "create", "update", "patch", "delete", "list", "watch"] - apiGroups: [""] resources: - namespaces verbs: ["get", "list", "watch"] - apiGroups: [""] resources: - nodes verbs: ["list"] - apiGroups: [""] resources: - events verbs: ["create"] keess, keess, ClusterRoleBinding (rbac.authorization.k8s.io) has changed: # Source: keess/templates/cluster-role-binding.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: keess labels: - helm.sh/chart: keess-0.2.15 + helm.sh/chart: keess-1.0.0 app.kubernetes.io/name: keess app.kubernetes.io/instance: keess - app.kubernetes.io/version: "0.2.14" + app.kubernetes.io/version: "1.0.0" app.kubernetes.io/managed-by: Helm roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: keess subjects: - kind: ServiceAccount name: keess namespace: keess keess, keess, Deployment (apps) has changed: # Source: keess/templates/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: keess namespace: keess labels: - helm.sh/chart: keess-0.2.15 + helm.sh/chart: keess-1.0.0 app.kubernetes.io/name: keess app.kubernetes.io/instance: keess - app.kubernetes.io/version: "0.2.14" + app.kubernetes.io/version: "1.0.0" app.kubernetes.io/managed-by: Helm spec: selector: matchLabels: app.kubernetes.io/name: keess app.kubernetes.io/instance: keess template: metadata: labels: app.kubernetes.io/name: keess app.kubernetes.io/instance: keess spec: serviceAccountName: keess securityContext: {} volumes: - name: config secret: secretName: keess defaultMode: 420 containers: - name: keess securityContext: {} - image: "image-registry.powerapp.cloud/keess/keess:0.2.14" - env: - - name: KEESS_SOURCE_CONTEXT - value: "app-alpha-gm" - - name: KEESS_DESTINATION_CONTEXTS - value: "app-beta-px" - - name: LOG_LEVEL - value: "DEBUG" + image: "image-registry.powerapp.cloud/keess/keess:PR-30-dac89984ddfead89fc06aafa0e80f0d8c2d4dc6a-1" + args: + - --logLevel=debug + - --localCluster=app-alpha-gm + - --kubeConfigPath=/root/.kube/config + - --pollingInterval=60 + - --namespacePollingInterval=60 + - --housekeepingInterval=60 imagePullPolicy: IfNotPresent volumeMounts: - name: config mountPath: /root/.kube readOnly: true ports: - name: http containerPort: 8080 protocol: TCP livenessProbe: httpGet: path: /health port: http initialDelaySeconds: 60 readinessProbe: httpGet: path: /health port: http resources: limits: ephemeral-storage: 150Mi memory: 128Mi requests: cpu: 100m ephemeral-storage: 150Mi memory: 64Mi keess, keess, Secret (v1) has changed: # Source: keess/templates/secret.yaml apiVersion: v1 kind: Secret metadata: labels: app.kubernetes.io/instance: keess app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: keess - app.kubernetes.io/version: 0.2.14 - helm.sh/chart: keess-0.2.15 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: keess-1.0.0 name: keess namespace: keess data: config: 'REDACTED # (7651 bytes)' keess, keess, ServiceAccount (v1) has changed: # Source: keess/templates/serviceaccount.yaml apiVersion: v1 kind: ServiceAccount metadata: name: keess namespace: keess labels: - helm.sh/chart: keess-0.2.15 + helm.sh/chart: keess-1.0.0 app.kubernetes.io/name: keess app.kubernetes.io/instance: keess - app.kubernetes.io/version: "0.2.14" + app.kubernetes.io/version: "1.0.0" app.kubernetes.io/managed-by: Helm ``` ## Test execution result ``` log -- /Users/marcusvinicius.leandro/Source/keess/test.py [INFO] Deleted destination namespace 'test-namespace-dest-1'. [INFO] Deleted destination namespace 'test-namespace-dest-2'. [INFO] Created source namespace 'test-namespace'. [INFO] Created destination namespace 'test-namespace-dest-1' with label 'keess.powerhrg.com/testing=yes'. [INFO] Created destination namespace 'test-namespace-dest-2' with label 'keess.powerhrg.com/testing=yes'. [INFO] Created secret 'new-test-secret' in source namespace 'test-namespace'. [INFO] Created ConfigMap 'new-test-configmap' in source namespace 'test-namespace'. [INFO] Waiting for resources to be created... [INFO] Applied labels and annotations to secret 'new-test-secret' in namespace 'test-namespace'. [INFO] Applied labels and annotations to configmap 'new-test-configmap' in namespace 'test-namespace'. [INFO] Waiting for synchronization to complete... [SUCCESS] Secret 'new-test-secret' in namespace 'test-namespace-dest-1' has been verified successfully with all annotations. [SUCCESS] Configmap 'new-test-configmap' in namespace 'test-namespace-dest-1' has been verified successfully with all annotations. [SUCCESS] Secret 'new-test-secret' in namespace 'test-namespace-dest-2' has been verified successfully with all annotations. [SUCCESS] Configmap 'new-test-configmap' in namespace 'test-namespace-dest-2' has been verified successfully with all annotations. [INFO] Applied labels and annotations to secret 'new-test-secret' in namespace 'test-namespace'. [INFO] Applied labels and annotations to configmap 'new-test-configmap' in namespace 'test-namespace'. [INFO] Waiting for synchronization to matching namespaces... [SUCCESS] Secret 'new-test-secret' in namespace 'test-namespace-dest-1' has been verified successfully with all annotations. [SUCCESS] Configmap 'new-test-configmap' in namespace 'test-namespace-dest-1' has been verified successfully with all annotations. [SUCCESS] Secret 'new-test-secret' in namespace 'test-namespace-dest-2' has been verified successfully with all annotations. [SUCCESS] Configmap 'new-test-configmap' in namespace 'test-namespace-dest-2' has been verified successfully with all annotations. [INFO] Test scenario 3 completed. [INFO] Applied labels and annotations to secret 'new-test-secret' in namespace 'test-namespace'. [INFO] Applied labels and annotations to configmap 'new-test-configmap' in namespace 'test-namespace'. [INFO] Labels and annotations applied for cross-cluster synchronization. Waiting for synchronization to complete... [SUCCESS] Secret 'new-test-secret' in namespace 'test-namespace' has been verified successfully with all annotations. [SUCCESS] Configmap 'new-test-configmap' in namespace 'test-namespace' has been verified successfully with all annotations. [INFO] Test scenario 4 completed. [INFO] Deleted secret 'new-test-secret' in source namespace 'test-namespace'. [INFO] Deleted configmap 'new-test-configmap' in source namespace 'test-namespace'. [INFO] Deleted source namespace 'test-namespace'. [INFO] Deleted destination namespace 'test-namespace-dest-1'. [INFO] Deleted destination namespace 'test-namespace-dest-2'. ``` ## Conclusion These improvements make Keess a more reliable and user-friendly tool for Kubernetes configuration management. By addressing previous limitations and leveraging more robust technologies, we're setting the stage for more scalable and secure infrastructure management practices. --------- Signed-off-by: Igor Valente Blackman Co-authored-by: Igor Blackman --- .dockerignore | 2 + .gitignore | 2 +- Dockerfile | 3 - Dockerfile.localTest | 5 + LICENSE | 14 +- Makefile | 80 +- README.md | 110 +- application/application.go | 107 -- chart/Chart.yaml | 4 +- chart/templates/deployment.yaml | 38 +- chart/values.yaml | 14 +- cmd/root.go | 93 + cmd/run.go | 228 +++ cmd/version.go | 25 + go.mod | 58 +- go.sum | 1642 +---------------- kube_syncer/abstractions/abstractions.go | 114 -- kube_syncer/abstractions/configmap_event.go | 124 -- kube_syncer/abstractions/entity_event.go | 11 - kube_syncer/abstractions/event_type.go | 10 - kube_syncer/abstractions/isynchronizable.go | 8 - kube_syncer/abstractions/iwatchable.go | 5 - kube_syncer/abstractions/kubernetes_entity.go | 241 --- .../abstractions/kubernetes_entity_type.go | 9 - kube_syncer/abstractions/namespace_event.go | 121 -- kube_syncer/abstractions/secret_event.go | 129 -- kube_syncer/abstractions/sync_type.go | 22 - kube_syncer/abstractions/sync_type_test.go | 43 - kube_syncer/configmap_watcher.go | 75 - kube_syncer/namespace_watcher.go | 61 - kube_syncer/secret_watcher.go | 75 - kube_syncer/syncer.go | 395 ---- main.go | 68 +- pkg/services/abstractions.go | 40 + pkg/services/configmap_poller.go | 73 + pkg/services/configmap_poller_test.go | 94 + pkg/services/configmap_synchronizer.go | 356 ++++ pkg/services/i_kube_client.go | 9 + pkg/services/namespace_poller.go | 83 + pkg/services/pac_configmap.go | 52 + pkg/services/pac_configmap_test.go | 152 ++ pkg/services/pac_namespace.go | 11 + pkg/services/pac_secret.go | 52 + pkg/services/pac_secret_test.go | 152 ++ pkg/services/secret_poller.go | 73 + pkg/services/secret_poller_test.go | 94 + pkg/services/secret_synchronizer.go | 356 ++++ requirements.txt | 2 + test.py | 745 ++++++++ 49 files changed, 2991 insertions(+), 3289 deletions(-) create mode 100644 Dockerfile.localTest delete mode 100644 application/application.go create mode 100644 cmd/root.go create mode 100644 cmd/run.go create mode 100644 cmd/version.go delete mode 100644 kube_syncer/abstractions/abstractions.go delete mode 100644 kube_syncer/abstractions/configmap_event.go delete mode 100644 kube_syncer/abstractions/entity_event.go delete mode 100644 kube_syncer/abstractions/event_type.go delete mode 100644 kube_syncer/abstractions/isynchronizable.go delete mode 100644 kube_syncer/abstractions/iwatchable.go delete mode 100644 kube_syncer/abstractions/kubernetes_entity.go delete mode 100644 kube_syncer/abstractions/kubernetes_entity_type.go delete mode 100644 kube_syncer/abstractions/namespace_event.go delete mode 100644 kube_syncer/abstractions/secret_event.go delete mode 100644 kube_syncer/abstractions/sync_type.go delete mode 100644 kube_syncer/abstractions/sync_type_test.go delete mode 100644 kube_syncer/configmap_watcher.go delete mode 100644 kube_syncer/namespace_watcher.go delete mode 100644 kube_syncer/secret_watcher.go delete mode 100644 kube_syncer/syncer.go create mode 100644 pkg/services/abstractions.go create mode 100644 pkg/services/configmap_poller.go create mode 100644 pkg/services/configmap_poller_test.go create mode 100644 pkg/services/configmap_synchronizer.go create mode 100644 pkg/services/i_kube_client.go create mode 100644 pkg/services/namespace_poller.go create mode 100644 pkg/services/pac_configmap.go create mode 100644 pkg/services/pac_configmap_test.go create mode 100644 pkg/services/pac_namespace.go create mode 100644 pkg/services/pac_secret.go create mode 100644 pkg/services/pac_secret_test.go create mode 100644 pkg/services/secret_poller.go create mode 100644 pkg/services/secret_poller_test.go create mode 100644 pkg/services/secret_synchronizer.go create mode 100644 requirements.txt create mode 100644 test.py diff --git a/.dockerignore b/.dockerignore index 8acd801..d7962ad 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,3 +5,5 @@ .sops.yaml keess README.md +Dockerfile.localtest +localTestKubeconfig diff --git a/.gitignore b/.gitignore index d64f341..dfee3a1 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,4 @@ apiserver.local.config/** .vscode keess coverage.* - +localTestKubeconfig diff --git a/Dockerfile b/Dockerfile index 03701f5..0e8e506 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,9 +18,6 @@ RUN go mod download # Copy the code into the container COPY . . -# Run tests -RUN go test ./... - # Build the application RUN go build -o keess . diff --git a/Dockerfile.localTest b/Dockerfile.localTest new file mode 100644 index 0000000..098ab55 --- /dev/null +++ b/Dockerfile.localTest @@ -0,0 +1,5 @@ +FROM python:3.12.2-bullseye + +COPY ./requirements.txt . +RUN pip install -r requirements.txt +COPY ./test.py . diff --git a/LICENSE b/LICENSE index 8936fc6..537ea09 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ -MIT License +The MIT License (MIT) -Copyright (c) 2023 Power Home Remodeling Group, LLC +Copyright © 2024 Power Home Remodeling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -8,12 +8,14 @@ in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Makefile b/Makefile index 4a840fd..0c0564d 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ DOCKER_TAG := "latest" GOBASE := $(shell pwd) GOBIN := $(GOBASE)/bin -.PHONY: build test docker-build coverage run docker-run help +.PHONY: build test docker-build coverage run docker-run create-local-clusters delete-local-clusters local-docker-run local-test help # Build the project build: @@ -15,18 +15,21 @@ build: @GOBIN=$(GOBIN) go build -o $(GOBIN)/$(PROJECT_NAME) $(GOBASE) # Run tests -test: +gotest: @echo "Running tests..." @go test ./... # Build Docker image docker-build: @echo "Building Docker image..." + @docker build -t $(DOCKER_IMAGE_NAME):$(DOCKER_TAG) . -# Remove the existing builder - @docker buildx rm mybuilder - @docker buildx create --name mybuilder --use - @docker buildx build --platform linux/amd64,linux/arm64 -t $(DOCKER_IMAGE_NAME):$(DOCKER_TAG) . +# Install requirements and run test.py +test: + @echo "Installing requirements..." + @pip3 install -r requirements.txt + @echo "Running test.py..." + @python3 test.py # New target for code coverage coverage: @@ -39,22 +42,65 @@ coverage: # Target to execute the application run: build @echo "Running the application..." - @./bin/keess run + @./bin/keess run --localCluster=$(LOCAL_CLUSTER) --logLevel=debug # Target to run the Docker image with the .kube directory mounted docker-run: @echo "Running Docker image with .kube directory mounted..." - @docker run --rm -it -v ${HOME}/.kube:/root/.kube $(DOCKER_IMAGE_NAME):$(DOCKER_TAG) + @docker run --rm -it -v ${HOME}/.kube:/root/.kube $(DOCKER_IMAGE_NAME):$(DOCKER_TAG) run --localCluster=$(LOCAL_CLUSTER) --logLevel=debug -# Help -help: - @echo "Makefile commands:" - @echo "build - Build the project" - @echo "test - Run tests" - @echo "docker-build - Build Docker image" - @echo "coverage - Generate and view code coverage report" - @echo "run - Run the application" - @echo "docker-run - Run the Docker image with .kube directory mounted" +LOCAL_TEST_KUBECONFIG_FILE := "localTestKubeconfig" +# Target to start local kube clusters for testing purposes +create-local-clusters: + @kind create cluster --image=kindest/node:v1.22.17 -n source-cluster --kubeconfig $(LOCAL_TEST_KUBECONFIG_FILE) + @kind create cluster --image=kindest/node:v1.22.17 -n destination-cluster --kubeconfig $(LOCAL_TEST_KUBECONFIG_FILE) +# Target to delete local kube clusters +delete-local-clusters: + @kind delete clusters source-cluster destination-cluster +# Target to run the Docker image with local test kubeconfig mounted +local-docker-run: + @echo "Running Docker image with $(LOCAL_TEST_KUBECONFIG_FILE) mounted..." + @docker run \ + --rm \ + -it \ + -v ./$(LOCAL_TEST_KUBECONFIG_FILE):/root/.kube/config \ + --network host \ + $(DOCKER_IMAGE_NAME):$(DOCKER_TAG) \ + run \ + --localCluster=kind-source-cluster \ + --remoteCluster=kind-destination-cluster \ + --kubeConfigPath=/root/.kube/config \ + --pollingInterval=10 \ + --housekeepingInterval=10 \ + --namespacePollingInterval=10 \ + --logLevel=debug +# Test locally using kind +local-test: + @echo "Building keess-test image" + @docker build -f Dockerfile.localTest -t keess-test:1.0 . + @echo "Running tests" + @docker run \ + -it \ + --rm \ + --mount type=bind,source="./$(LOCAL_TEST_KUBECONFIG_FILE)",target=/root/.kube/config,readonly \ + --network host \ + --name keess-test \ + keess-test:1.0 \ + python test.py + +# Help +help: + @echo "Makefile commands:" + @echo "build - Build the project" + @echo "test - Run tests" + @echo "docker-build - Build Docker image" + @echo "coverage - Generate and view code coverage report" + @echo "run - Run the application" + @echo "docker-run - Run the Docker image with .kube directory mounted" + @echo "create-local-clusters - Create 2 clusters locally using Kind" + @echo "delete-local-clusters - Delete the 2 local clusters created with Kind" + @echo "local-docker-run - Run the application locally using docker and pointing to the local cluster created with Kind" + @echo "local-test - Run the tests pointing to the local cluster created with Kind" diff --git a/README.md b/README.md index cf93d3f..6f634b7 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,109 @@ -# Keess -Keess keeps **Secrets** and **ConfigMaps** synchronized across namespaces and remote clusters. +# Keess: Kubernetes Secrets and ConfigMaps Synchronization -## Usage +Keess (Keep Stuff Synchronized) is a versatile command-line tool designed to synchronize secrets and configmaps across different namespaces and Kubernetes clusters. Built with simplicity and efficiency in mind, it ensures that your Kubernetes environments are consistently updated, secure, and easy to manage. -Keess uses labels and annotations from Secrets and ConfigMaps to work. +## Features -To enable the synchronization you have two steps: +- **Cross-Namespace Synchronization**: Effortlessly sync secrets and configmaps across multiple namespaces within a single Kubernetes cluster. +- **Inter-Cluster Synchronization**: Extend your synchronization capabilities to multiple clusters, keeping your configurations consistent across different environments. +- **Secure and Reliable**: Implements robust mechanisms to securely transfer sensitive information, ensuring data integrity and confidentiality. +- **Automation**: Automates the synchronization process, reducing manual overhead and minimizing human error. +- **Customizable**: Offers flexible command line options and Kubernetes annotations to tailor the synchronization process to your specific needs. +- **Efficient Monitoring**: Provides detailed logs for tracking operations and auditing changes. -First, you have to add a label indicating which type of synchronization you want: namespace or cluster. See the options below: +## Getting Started -`keess.powerhrg.com/sync: namespace` +### Prerequisites -`keess.powerhrg.com/sync: cluster` +- Kubernetes cluster setup +- kubectl installed and configured +- Helm (optional, for Helm chart deployment) -Then you need to configure the synchronization using annotations, which will be described in the next topics. +### Installation -### Namespace synchronization +Refer to the previous section on installing Keess via binaries, source, or Helm. -The namespace synchronization counts with three options. You can specify the destination namespaces names, and labels or you can choose to synchronize with all namespaces. +### Configuration -`keess.powerhrg.com/namespaces-names: all` +#### Using Configuration Files -`keess.powerhrg.com/namespaces-names: namespacea, namespaceb, namespacec` +Create a `.keess.yaml` configuration file as previously described or specify the path using the `--config` flag. -`keess.powerhrg.com/namespace-label: keess.powerhrg.com/sync="true"` +#### Using Command Line Flags +Keess supports various command line flags for on-the-fly configuration: -### Cluster synchronization +```shell +./keess run --logLevel debug --localCluster my-cluster --kubeConfigPath /path/to/kubeconfig +``` -The cluster synchronization occurs across different clusters and the same namespaces. -You need to specify the cluster names using the annotation below: +For a full list of available flags, use: -`keess.powerhrg.com/clusters: clustera, clusterb, clusterc` +```shell +./keess --help +``` -## Known flaws +### Configuring Synchronization -Changes made to synchronised resources while Keess is bootstrapping may not be detected and synchronised. Restarting the service should catch up if synchronisation is found to be behind as a result. +Keess uses Kubernetes labels and annotations to manage synchronization of Secrets and ConfigMaps. + +#### Enable Synchronization + +Add a label to your Secret or ConfigMap to indicate the synchronization type: + +- For namespace synchronization: `keess.powerhrg.com/sync: namespace` +- For cluster synchronization: `keess.powerhrg.com/sync: cluster` + +#### Namespace Synchronization + +Configure which namespaces to synchronize with: + +- All namespaces: `keess.powerhrg.com/namespaces-names: all` +- Specific namespaces: `keess.powerhrg.com/namespaces-names: namespacea, namespaceb` +- Based on labels: `keess.powerhrg.com/namespace-label: keess.powerhrg.com/sync="true"` + +#### Cluster Synchronization + +Specify the remote clusters for synchronization: `keess.powerhrg.com/clusters: clustera, clusterb` + +## Contributing + +Contributions are welcome! Please refer to our [Contributing Guidelines](CONTRIBUTING.md) for more information. + +## Support + +If you encounter any issues or have questions, please file an issue on the [GitHub Issues page](https://github.com/your-repo/keess/issues). + +## License + +Keess is open-source software licensed under the MIT license. See the [LICENSE](LICENSE) file for details. + +## Local testing +We will use [kind](https://kind.sigs.k8s.io/) for this + +First of all, create 2 clusters: +``` +make create-local-clusters +``` + +Now build and run the application locally pointing to these new clusters: +``` +make docker-build local-docker-run +``` + +To execute the local test: +``` +make local-test +``` + +If you want to investigate the cluster you can do it by: +``` +kubectl cluster-info --context kind-source-cluster --kubeconfig test/kubeconfig +kubectl cluster-info --context kind-destination-cluster --kubeconfig test/kubeconfig +``` + +Once we are done with the test and don't need the local clusters anymore you can delete them with +``` +make delete-local-clusters +``` diff --git a/application/application.go b/application/application.go deleted file mode 100644 index 839fbbe..0000000 --- a/application/application.go +++ /dev/null @@ -1,107 +0,0 @@ -package application - -import ( - "fmt" - "keess/kube_syncer" - - "github.com/fsnotify/fsnotify" - "github.com/spf13/viper" - "github.com/urfave/cli/v2" -) - -// Creates a new instance of the program to run. -func New() *cli.App { - app := cli.NewApp() - app.Name = "Keess" - app.Version = "0.2.14" - app.Usage = "Keep stuff synchronized." - app.Description = "Keep secrets and configmaps synchronized." - app.Suggest = true - app.Authors = []*cli.Author{ - { - Name: "Marcus Vinicius Leandro", - Email: "mvleandro@gmail.com", - }, - } - app.Copyright = "Power Home Remodeling Group, LLC" - - flags := []cli.Flag{ - &cli.StringFlag{ - Name: "config", - Usage: "Path to the kubeconfig file", - }, - &cli.StringFlag{ - Name: "sourceContext", - Usage: "The context to be watched.", - }, - &cli.StringSliceFlag{ - Name: "destinationContexts", - Usage: "A list with the contexts where the events will by synched to", - }, - &cli.BoolFlag{ - Name: "developmentMode", - Usage: "If true, the logs will be shown as text plain instead of json inline", - }, - } - - app.Commands = []*cli.Command{ - { - Name: "run", - Usage: "Keep secrets and configmaps syncronized across clusters and namespaces", - Flags: flags, - Action: run, - }, - } - - return app -} - -// Run the program. -func run(c *cli.Context) error { - - viper := viper.New() - viper.SetEnvPrefix("KEESS") - viper.AutomaticEnv() - - viper.WatchConfig() - - kubeConfigPath := c.String("config") - sourceContext := c.String("sourceContext") - destinationContexts := c.StringSlice("destinationContexts") - developmentMode := c.Bool("developmentMode") - initialLogLevel := viper.GetString("LOG_LEVEL") - if initialLogLevel == "" { - initialLogLevel = "INFO" - } - - if kubeConfigPath == "" { - kubeConfigPath = viper.GetString("CONFIG_PATH") - } - - if sourceContext == "" { - sourceContext = viper.GetString("SOURCE_CONTEXT") - } - - if destinationContexts == nil { - destinationContexts = viper.GetStringSlice("DESTINATION_CONTEXTS") - } - - if !developmentMode { - developmentMode = viper.GetBool("DEVELOPMENT_MODE") - } - - fmt.Printf("Starting %s %s\n", c.App.Name, c.App.Version) - - var syncer kube_syncer.Syncer - err := syncer.Start(kubeConfigPath, developmentMode, initialLogLevel, sourceContext, destinationContexts) - - viper.OnConfigChange(func(e fsnotify.Event) { - syncer.SetLogLevel(viper.GetString("LOG_LEVEL")) - }) - - if err == nil { - return syncer.Run() - } - - return nil -} diff --git a/chart/Chart.yaml b/chart/Chart.yaml index 8dbf1c6..f633b0b 100644 --- a/chart/Chart.yaml +++ b/chart/Chart.yaml @@ -15,10 +15,10 @@ 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.15 +version: 1.0.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.2.14" +appVersion: "1.0.0" diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml index b048ebe..2032cd9 100644 --- a/chart/templates/deployment.yaml +++ b/chart/templates/deployment.yaml @@ -34,22 +34,19 @@ spec: - name: {{ .Chart.Name }} securityContext: {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - env: - - name: KEESS_SOURCE_CONTEXT - value: "{{ .Values.clusterName }}" - {{- if .Values.remoteClusters }} - - name: KEESS_DESTINATION_CONTEXTS - value: "{{ .Values.remoteClusters }}" - {{- end }} - {{- if .Values.developmentMode }} - - name: KEESS_DEVELOPMENT_MODE - value: "{{ .Values.developmentMode }}" - {{- end }} - {{- if .Values.logLevel }} - - name: LOG_LEVEL - value: "{{ .Values.logLevel }}" - {{- end }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + args: + - --logLevel={{ .Values.logLevel }} + - --localCluster={{ .Values.localCluster }} + {{- if .Values.remoteClusters }} + {{- range .Values.remoteClusters }} + - --remoteCluster={{ . }} + {{- end }} + {{- end }} + - --kubeConfigPath={{ .Values.kubeConfigPath }} + - --pollingInterval={{ .Values.pollingInterval }} + - --namespacePollingInterval={{ .Values.namespacePollingInterval }} + - --housekeepingInterval={{ .Values.housekeepingInterval }} imagePullPolicy: {{ .Values.image.pullPolicy }} volumeMounts: - name: config @@ -60,9 +57,14 @@ spec: containerPort: {{ .Values.service.port }} protocol: TCP livenessProbe: - {{- toYaml .Values.livenessProbe | nindent 12 }} + httpGet: + path: /health + port: http + initialDelaySeconds: 60 readinessProbe: - {{- toYaml .Values.readinessProbe | nindent 12 }} + httpGet: + path: /health + port: http resources: {{- toYaml .Values.resources | nindent 12 }} {{- with .Values.nodeSelector }} diff --git a/chart/values.yaml b/chart/values.yaml index 46606ff..2a582e4 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -10,10 +10,16 @@ image: # Default Keess config -# clusterName: "" -# remoteClusters: "" -# developmentMode: "true" -# logLevel: "INFO" +logLevel: "info" + +# localCluster must be specified for the chart to work correctly. +localCluster: "" +remoteClusters: [] +kubeConfigPath: "/root/.kube/config" +pollingInterval: "60" +namespacePollingInterval: "60" +housekeepingInterval: "60" + imagePullSecrets: [] nameOverride: "" diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..7129cff --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,93 @@ +/* +Copyright © 2024 Marcus Vinicius + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package cmd + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +var cfgFile string + +// rootCmd represents the base command when called without any subcommands +var rootCmd = &cobra.Command{ + Use: "keess", + Short: "Synchronize secrets and configmaps across namespaces and clusters.", + // Long: `Keess (Keep Stuff Synchronized) is a command-line tool designed to operate within Kubernetes clusters, focusing on the synchronization of secrets and configmaps. It efficiently handles the transfer of these critical components across different namespaces within a single cluster, as well as across multiple clusters, ensuring consistency and security in distributed environments. + + // Key Features: + // - Cross-Namespace Synchronization: Sync secrets and configmaps across various namespaces in a single cluster. + // - Inter-Cluster Synchronization: Extend synchronization capabilities to multiple Kubernetes clusters. + // - Security-Oriented: Ensures secure transfer, maintaining data integrity and confidentiality. + // - Automation and Efficiency: Automates synchronization processes, reducing manual effort and error. + // - Customizable Rules: Offers flexibility in defining specific synchronization conditions. + // - User-Friendly CLI: Easy-to-use command-line interface for seamless integration and operation. + // - Logging and Monitoring: Provides comprehensive logs for tracking and auditing purposes. + + // Keess streamlines Kubernetes configuration management, making it an essential tool for DevOps teams and Kubernetes administrators seeking to maintain uniformity and security across their infrastructure.`, +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } +} + +func init() { + cobra.OnInitialize(initConfig) + + // Here you will define your flags and configuration settings. + // Cobra supports persistent flags, which, if defined here, + // will be global for your application. + + rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.keess.yaml)") +} + +// initConfig reads in config file and ENV variables if set. +func initConfig() { + if cfgFile != "" { + // Use config file from the flag. + viper.SetConfigFile(cfgFile) + } else { + // Find home directory. + home, err := os.UserHomeDir() + cobra.CheckErr(err) + + // Search config in home directory with name ".keess" (without extension). + viper.AddConfigPath(home) + viper.SetConfigType("yaml") + viper.SetConfigName(".keess") + } + + viper.AutomaticEnv() // read in environment variables that match + + // If a config file is found, read it in. + if err := viper.ReadInConfig(); err == nil { + fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) + } +} diff --git a/cmd/run.go b/cmd/run.go new file mode 100644 index 0000000..4f0118e --- /dev/null +++ b/cmd/run.go @@ -0,0 +1,228 @@ +/* +Copyright © 2024 Marcus Vinicius + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package cmd + +import ( + "context" + "fmt" + "keess/pkg/services" + "net/http" + "os" + "path/filepath" + "time" + + "github.com/spf13/cobra" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" // required for oidc + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" +) + +// runCmd represents the run command +var runCmd = &cobra.Command{ + Use: "run", + Short: "Initiates the synchronization of secrets and configmaps.", + // Long: `The 'run' command in Keess is the primary trigger for initiating the synchronization process of secrets and configmaps across Kubernetes namespaces and clusters. When executed, it activates Keess’s core functionality, seamlessly transferring specified configurations and sensitive data according to predefined rules and parameters. This command ensures that all targeted Kubernetes environments are updated with the latest configurations and secrets, maintaining consistency and enhancing security across your distributed infrastructure. + + // Features: + // - Automated Synchronization: Executes the automated process of syncing secrets and configmaps. + // - Cross-Namespace and Cluster Operation: Works across different namespaces and multiple clusters. + // - Secure Transfer: Adheres to strict security protocols to ensure safe data transfer. + // - Custom Synchronization: Respects user-defined rules and conditions for targeted synchronization. + // - Real-Time Execution: Performs synchronization in real time, ensuring timely updates. + // - Logging: Generates logs for the synchronization process, aiding in monitoring and auditing. + + // Usage of the 'run' command is essential for keeping your Kubernetes environments synchronized and secure, forming the backbone of Keess's operational capabilities.`, + Run: func(cmd *cobra.Command, args []string) { + atom := zap.NewAtomicLevel() + var level zapcore.Level + err := level.UnmarshalText([]byte(logLevel)) + if err != nil { + fmt.Printf("Error setting log level: %s\n", err.Error()) + return + } + atom.SetLevel(level) + + cfg, _ := configureLogger() + logger := zap.New(zapcore.NewCore( + zapcore.NewJSONEncoder(*cfg), + zapcore.Lock(os.Stdout), + atom, + )) + defer logger.Sync() + + config, err := rest.InClusterConfig() + if err != nil { + kubeconfig := filepath.Join(os.Getenv("HOME"), ".kube", "config") + config, err = buildConfigWithContextFromFlags(localCluster, kubeconfig) + if err != nil { + logger.Sugar().Error("Error building localCluster kubeconfig: ", err) + return + } + } + + // create the clientset + localKubeClient, err := kubernetes.NewForConfig(config) + if err != nil { + logger.Sugar().Error("Error creating inCluster clientset: ", err) + return + } + + // Create a context + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + namespacePollingInterval, _ := cmd.Flags().GetInt32("namespacePollingInterval") + pollingInterval, _ := cmd.Flags().GetInt32("pollingInterval") + housekeepingInterval, _ := cmd.Flags().GetInt32("housekeepingInterval") + + logger.Sugar().Infof("Starting Keess. Running on local cluster: %s", localCluster) + logger.Sugar().Debugf("Namespace polling interval: %d seconds", namespacePollingInterval) + logger.Sugar().Debugf("Polling interval: %d seconds", pollingInterval) + logger.Sugar().Debugf("Housekeeping interval: %d seconds", housekeepingInterval) + logger.Sugar().Debugf("Log level: %s", logLevel) + logger.Sugar().Debugf("Kubeconfig path: %s", kubeConfigPath) + + // Create a map of remote clients + remoteKubeClients := make(map[string]services.IKubeClient) + + if len(remoteClusters) > 0 { + + // Add the remote clientset to the map + for _, cluster := range remoteClusters { + remoteClusterConfig, err := buildConfigWithContextFromFlags(cluster, kubeConfigPath) + if err != nil { + logger.Sugar().Errorf("Error building remote kubeconfig for cluster '%s': %s", cluster, err) + } + + remoteClusterClient, err := kubernetes.NewForConfig(remoteClusterConfig) + if err != nil { + logger.Sugar().Errorf("Error creating remote clientset for cluster '%s': %s", cluster, err) + } + + remoteKubeClients[cluster] = remoteClusterClient + } + + logger.Sugar().Infof("Remote clusters: %v", remoteClusters) + } else { + logger.Sugar().Info("No remote clusters to synchronize") + } + + // Create a NamespacePoller + namespacePoller := services.NewNamespacePoller(localKubeClient, logger.Sugar()) + namespacePoller.PollNamespaces(ctx, metav1.ListOptions{}, time.Duration(namespacePollingInterval)*time.Second, localCluster) + + // Create a SecretPoller + secretPoller := services.NewSecretPoller(localCluster, localKubeClient, logger.Sugar()) + + // Create a SecretSynchronizer + secretSynchronizer := services.NewSecretSynchronizer( + localKubeClient, + remoteKubeClients, + secretPoller, + namespacePoller, + logger.Sugar(), + ) + + // Start the secret synchronizer + secretSynchronizer.Start(ctx, time.Duration(pollingInterval)*time.Second, time.Duration(housekeepingInterval)*time.Second) + + // Create a ConfigMapPoller + configMapPoller := services.NewConfigMapPoller(localCluster, localKubeClient, logger.Sugar()) + + // Create a ConfigMapSynchronizer + configMapSynchronizer := services.NewConfigMapSynchronizer( + localKubeClient, + remoteKubeClients, + configMapPoller, + namespacePoller, + logger.Sugar(), + ) + + // Start the configMap synchronizer + configMapSynchronizer.Start(ctx, time.Duration(pollingInterval)*time.Second, time.Duration(housekeepingInterval)*time.Second) + + // Create an HTTP server and add the health check handler as a handler + http.HandleFunc("/health", healthHandler) + http.ListenAndServe(":8080", nil) + + select {} + }, +} + +func healthHandler(w http.ResponseWriter, r *http.Request) { + // Check the health of the server and return a status code accordingly + if serverIsHealthy() { + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, "Server is healthy") + } else { + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprint(w, "Server is not healthy") + } +} + +func serverIsHealthy() bool { + // Check the health of the server and return true or false accordingly + // For example, check if the server can connect to the database + return true +} + +var logLevel string +var localCluster string +var remoteClusters []string +var kubeConfigPath string + +func init() { + rootCmd.AddCommand(runCmd) + + // Here you will define your flags and configuration settings. + runCmd.Flags().StringVarP(&logLevel, "logLevel", "l", "info", "Log level") + runCmd.Flags().StringVarP(&localCluster, "localCluster", "c", "", "Local cluster name") + runCmd.Flags().StringVarP(&kubeConfigPath, "kubeConfigPath", "p", "", "Path to the kubeconfig file") + runCmd.Flags().Int32P("namespacePollingInterval", "n", int32(60), "Interval in seconds to poll the Kubernetes API for namespaces.") + runCmd.Flags().StringArrayVarP(&remoteClusters, "remoteCluster", "r", []string{}, "Remote cluster to synchronize") + runCmd.Flags().Int32P("pollingInterval", "s", int32(60), "Interval in seconds to poll the Kubernetes API for secrets and configmaps.") + runCmd.Flags().Int32P("housekeepingInterval", "k", int32(300), "Interval in seconds to delete orphans objects.") + +} + +// Configure the logger +func configureLogger() (*zapcore.EncoderConfig, error) { + cfg := zap.NewProductionEncoderConfig() + cfg.TimeKey = "timestamp" + cfg.EncodeTime = zapcore.TimeEncoderOfLayout(time.RFC3339) + cfg.CallerKey = "caller" + + return &cfg, nil +} + +// buildConfigWithContextFromFlags builds a Kubernetes client configuration from the provided context and kubeconfig path. +func buildConfigWithContextFromFlags(context string, kubeconfigPath string) (*rest.Config, error) { + return clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfigPath}, + &clientcmd.ConfigOverrides{ + CurrentContext: context, + }).ClientConfig() +} diff --git a/cmd/version.go b/cmd/version.go new file mode 100644 index 0000000..e1220dd --- /dev/null +++ b/cmd/version.go @@ -0,0 +1,25 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// Version of the application, set this variable during build +var version = "1.0.0" + +// versionCmd represents the version command +var versionCmd = &cobra.Command{ + Use: "version", + + Short: "Print the version number of the application", + Long: `Print the version number of the application`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("Keess v" + version) + }, +} + +func init() { + rootCmd.AddCommand(versionCmd) +} diff --git a/go.mod b/go.mod index 1081ef1..d21df4e 100644 --- a/go.mod +++ b/go.mod @@ -3,33 +3,36 @@ module keess go 1.21 require ( - github.com/appscode/go v0.0.0-20201105063637-5613f3b8169f - github.com/fsnotify/fsnotify v1.7.0 + github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.18.2 - github.com/urfave/cli/v2 v2.27.1 - go.uber.org/zap v1.26.0 + go.uber.org/zap v1.21.0 k8s.io/api v0.29.1 k8s.io/apimachinery v0.29.1 k8s.io/client-go v0.29.1 ) require ( - github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect + github.com/evanphx/json-patch v4.12.0+incompatible // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect +) + +require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.11.2 // indirect - github.com/go-logr/logr v1.4.1 // indirect - github.com/go-openapi/jsonpointer v0.20.2 // indirect - github.com/go-openapi/jsonreference v0.20.4 // indirect - github.com/go-openapi/swag v0.22.9 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-logr/logr v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/google/gnostic v0.7.0 // indirect - github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect - github.com/google/go-cmp v0.6.0 // indirect + github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.6.0 // indirect + github.com/google/uuid v1.4.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect + github.com/imdario/mergo v0.3.6 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -39,35 +42,34 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml/v2 v2.1.1 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/testify v1.8.4 github.com/subosito/gotenv v1.6.0 // indirect - github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect - go.uber.org/atomic v1.11.0 // indirect + go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect - golang.org/x/net v0.20.0 // indirect - golang.org/x/oauth2 v0.16.0 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/oauth2 v0.15.0 // indirect golang.org/x/sys v0.16.0 // indirect - golang.org/x/term v0.16.0 // indirect + golang.org/x/term v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect - google.golang.org/appengine v1.6.8 // indirect - google.golang.org/protobuf v1.32.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/klog/v2 v2.120.1 // indirect - k8s.io/kube-openapi v0.0.0-20240126223410-2919ad4fcfec // indirect - k8s.io/utils v0.0.0-20240102154912-e7106e64919e // indirect + k8s.io/klog/v2 v2.110.1 // indirect + k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 0a93b5e..0ef87db 100644 --- a/go.sum +++ b/go.sum @@ -1,1775 +1,227 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= -cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= -cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= -cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= -cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= -cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= -cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= -cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= -cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= -cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= -cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= -cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= -cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= -cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= -cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= -cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= -cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= -cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= -cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= -cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= -cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= -cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= -cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= -cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= -cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= -cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= -cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= -cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= -cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= -cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= -cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= -cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= -cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= -cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= -cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= -cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= -cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= -cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= -cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= -cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= -cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= -cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= -cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= -cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= -cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= -cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= -cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= -cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= -cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= -cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= -cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= -cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= -cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= -cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= -cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= -cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= -cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= -cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= -cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= -cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= -cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= -cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= -cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= -cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= -cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= -cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= -cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= -cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= -cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= -cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= -cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= -cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= -cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= -cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= -cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= -cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= -cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= -cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= -cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= -cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= -cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= -cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= -cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= -cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= -cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= -cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= -cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= -cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= -cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= -cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= -cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= -cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= -cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= -cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= -cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= -cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= -cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= -cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= -cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= -cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= -cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= -cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= -cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= -cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= -cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= -cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= -cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= -cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= -cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= -cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= -cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= -cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= -cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= -cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= -cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= -cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= -cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= -cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= -cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= -cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= -cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= -cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= -cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= -cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= -cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= -cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= -cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= -cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= -cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= -cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= -cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= -cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= -cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= -cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= -cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= -cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= -cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= -cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= -cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= -cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= -cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= -cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= -cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= -cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= -cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= -cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= -cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= -cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= -cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= -cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= -cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= -cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= -cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= -cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= -cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= -cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= -cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= -cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= -cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= -cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= -cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= -cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= -cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= -cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= -cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= -cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= -cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= -cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= -cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= -cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= -cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= -cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= -cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= -cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= -cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= -cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= -cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= -cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= -cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= -cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= -cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= -cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= -cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= -cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= -cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= -cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= -cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= -cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= -cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= -cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= -cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= -cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= -cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= -cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= -cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= -cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= -cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= -cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= -cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= -cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= -cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= -cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= -cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= -cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= -cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= -cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= -cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= -cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= -cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= -cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= -cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= -cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= -cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= -cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= -cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= -cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= -cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= -cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= -cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= -cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= -cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= -cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= -cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= -cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= -cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= -cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= -cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= -cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= -cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= -cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= -cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= -cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= -cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= -cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= -cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= -cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= -cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= -cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= -cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= -cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= -cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= -cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= -cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= -cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= -cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= -cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= -cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= -cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= -cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= -cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= -cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= -cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= -cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= -cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= -cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= -cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= -cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= -cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= -cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= -cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= -cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= -cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= -cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= -cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= -cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= -cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= -cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= -cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= -cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= -cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= -cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= -cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= -cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= -cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= -cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= -cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= -cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= -cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= -cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= -cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= -cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= -cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= -cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= -cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= -cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= -cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= -cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= -cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= -cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= -cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= -cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= -cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= -cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= -cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= -cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= -cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= -cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= -cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= -cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= -cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= -cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= -cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= -cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= -cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= -cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= -cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= -cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= -cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= -cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= -cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= -cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= -cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= -cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= -cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= -cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= -cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= -cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= -cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= -cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= -cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= -cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= -cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= -cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= -cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= -cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= -cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= -cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= -cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= -cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= -cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= -cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= -cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= -cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= -cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= -cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= -cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= -cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= -cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= -cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= -cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= -cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= -cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= -cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= -cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= -cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= -cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= -cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= -cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= -cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= -cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= -cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= -cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= -cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= -cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= -cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= -cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= -cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= -cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= -cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= -cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= -cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= -cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= -cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= -cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= -cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= -cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= -cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= -cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= -cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= -cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= -cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= -cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= -cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= -cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= -cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= -cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= -cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= -cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= -cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= -cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= -cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= -cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= -cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= -cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= -cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= -cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= -cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= -cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= -cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= -cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= -cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= -cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= -cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= -cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= -cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= -cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= -cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= -cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= -cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= -cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= -cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= -cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= -cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= -cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= -cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= -cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= -cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= -cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= -cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= -cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= -cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= -cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= -cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= -cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= -cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= -cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= -cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= -cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= -cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= -cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= -cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= -cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= -cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= -cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= -cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= -cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= -cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= -cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= -cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= -cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= -cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= -cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= -cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= -cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= -cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= -cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= -cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= -cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= -cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= -cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= -cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= -cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= -cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= -cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= -cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= -cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= -cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= -cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= -cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= -cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= -cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= -cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= -cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= -cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= -cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= -cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= -cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= -cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= -cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= -cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= -cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= -cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= -cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= -cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= -cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= -cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= -cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= -cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= -cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= -cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= -cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= -cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= -cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= -cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= -cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= -cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= -cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= -cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= -cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= -cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= -cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= -cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= -cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= -cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= -cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= -cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= -cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= -cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= -cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= -cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= -cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= -cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= -cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= -cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= -cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= -cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= -cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= -cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= -cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= -cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= -cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= -cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= -cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= -cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= -cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= -cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= -cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= -cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= -cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= -cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= -cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= -cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= -cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= -cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= -cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= -git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= -github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= -github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= -github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= -github.com/appscode/go v0.0.0-20201105063637-5613f3b8169f h1:heDuWjdnY2rJIgLwIQjWPgOc0BUWWX6OGOeB+0t8v/s= -github.com/appscode/go v0.0.0-20201105063637-5613f3b8169f/go.mod h1:piHRpQ9+NTTuV3V98INxjU7o2KlAJMznaxvB6wHKkfU= -github.com/beevik/ntp v0.3.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= -github.com/codeskyblue/go-sh v0.0.0-20200712050446-30169cf553fe/go.mod h1:VQx0hjo2oUeQkQUET7wRwradO6f+fN5jzXgB/zROxxE= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= -github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+BtnqkLAU= -github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= -github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= -github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= -github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= -github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= -github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= -github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= -github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= -github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= -github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= -github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= -github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= -github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= -github.com/google/gnostic v0.7.0 h1:d7EpuFp8vVdML+y0JJJYiKeOLjKTdH/GvVkLOBWqJpw= -github.com/google/gnostic v0.7.0/go.mod h1:IAcUyMl6vtC95f60EZ8oXyqTsOersP6HbwjeG7EyDPM= -github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU= -github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= -github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= -github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= -github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= -github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo= -github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= -github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= +github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= +github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= -github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= -github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= -github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= -github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= -github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= -github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI= -github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= -github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= -go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= -golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= +golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -gomodules.xyz/password-generator v0.2.4/go.mod h1:TvwYYTx9+P1pPwKQKfZgB/wr2Id9MqAQ3B5auY7reNg= -gomodules.xyz/version v0.1.0/go.mod h1:Y8xuV02mL/45psyPKG3NCVOwvAOy6T5Kx0l3rCjKSjU= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= -gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= -google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= -google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= -google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= -google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= -google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= -google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= -google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= -google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= -google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= -google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= -google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= -google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= -google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= -google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230525234025-438c736192d0/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY= -google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -k8s.io/api v0.27.3 h1:yR6oQXXnUEBWEWcvPWS0jQL575KoAboQPfJAuKNrw5Y= -k8s.io/api v0.27.3/go.mod h1:C4BNvZnQOF7JA/0Xed2S+aUyJSfTGkGFxLXz9MnpIpg= k8s.io/api v0.29.1 h1:DAjwWX/9YT7NQD4INu49ROJuZAAAP/Ijki48GUPzxqw= k8s.io/api v0.29.1/go.mod h1:7Kl10vBRUXhnQQI8YR/R327zXC8eJ7887/+Ybta+RoQ= -k8s.io/apimachinery v0.27.3 h1:Ubye8oBufD04l9QnNtW05idcOe9Z3GQN8+7PqmuVcUM= -k8s.io/apimachinery v0.27.3/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= k8s.io/apimachinery v0.29.1 h1:KY4/E6km/wLBguvCZv8cKTeOwwOBqFNjwJIdMkMbbRc= k8s.io/apimachinery v0.29.1/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= -k8s.io/client-go v0.27.3 h1:7dnEGHZEJld3lYwxvLl7WoehK6lAq7GvgjxpA3nv1E8= -k8s.io/client-go v0.27.3/go.mod h1:2MBEKuTo6V1lbKy3z1euEGnhPfGZLKTS9tiJ2xodM48= k8s.io/client-go v0.29.1 h1:19B/+2NGEwnFLzt0uB5kNJnfTsbV8w6TgQRz9l7ti7A= k8s.io/client-go v0.29.1/go.mod h1:TDG/psL9hdet0TI9mGyHJSgRkW3H9JZk2dNEUS7bRks= -k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= -k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= -k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= -k8s.io/kube-openapi v0.0.0-20240126223410-2919ad4fcfec h1:iGTel2aR8vCZdxJDgmbeY0zrlXy9Qcvyw4R2sB4HLrA= -k8s.io/kube-openapi v0.0.0-20240126223410-2919ad4fcfec/go.mod h1:Pa1PvrP7ACSkuX6I7KYomY6cmMA0Tx86waBhDUgoKPw= -k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= -k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ= -k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= -modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= -modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= -modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= -modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= -modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= -modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= -modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= -modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= +k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/kube_syncer/abstractions/abstractions.go b/kube_syncer/abstractions/abstractions.go deleted file mode 100644 index e9705ac..0000000 --- a/kube_syncer/abstractions/abstractions.go +++ /dev/null @@ -1,114 +0,0 @@ -package abstractions - -import ( - "strings" - "time" - - "go.uber.org/zap" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -// Label that must be applied to the Secrets and ConfigMaps that are managed by keess. -const ManagedLabelSelector string = "keess.powerhrg.com/managed" - -// Label that must be applied to the Secrets and ConfigMaps that will be synchronized. -const LabelSelector string = "keess.powerhrg.com/sync" - -// Accepted annotation to configure the synchronization across clusters. -const ClusterAnnotation string = "keess.powerhrg.com/clusters" - -// Accepted annotation to configure the synchronization across namespaces. -const NamespaceNameAnnotation string = "keess.powerhrg.com/namespaces-names" - -// Accepted annotation to configure the synchronization across namespaces. -const NamespaceLabelAnnotation string = "keess.powerhrg.com/namespace-label" - -// Annotation with the source cluster of the object managed by kees. -const SourceClusterAnnotation string = "keess.powerhrg.com/source-cluster" - -// Annotation with the source namespace of the object managed by kees. -const SourceNamespaceAnnotation string = "keess.powerhrg.com/source-namespace" - -// Annotation with the source resource version of the object managed by kees. -const SourceResourceVersionAnnotation string = "keess.powerhrg.com/source-resource-version" - -// Constant that represents the synchronization across all namespaces. -const All string = "all" - -// Constant with the annotation created by the kubectl apply command -const KubectlApplyAnnotation string = "kubectl.kubernetes.io/last-applied-configuration" - -// The timeout for watching. -var WatchTimeOut int64 = int64(time.Duration(60 * 60 * 24 * 365 * 10)) - -// Logger object. -var Logger *zap.SugaredLogger - -// A list with all existent namespaces. -var Namespaces map[string]*corev1.Namespace = map[string]*corev1.Namespace{} - -// A map containing the ConfigMaps that sould be present in every Namespace -var EntitiesToAllNamespaces map[string]map[string]runtime.Object = make(map[string]map[string]runtime.Object) - -// A map containing the ConfigMaps that sould be present in every Namespace that matches with the configured label -var EntitiesToLabeledNamespaces map[string]map[string]runtime.Object = make(map[string]map[string]runtime.Object) - -// === Functions === // - -// Check if exists a valid annotation in an annotation map. -func ContainsAValidAnnotation(annotations map[string]string) bool { - contains := false - - for key := range annotations { - if key == ClusterAnnotation || key == NamespaceNameAnnotation || key == NamespaceLabelAnnotation { - contains = true - break - } - } - - return contains -} - -// Check if a label value is valid. -func IsAValidLabelValue(labelValue string) bool { - switch labelValue { - case string(Namespace): - return true - case string(Cluster): - return true - default: - return false - } -} - -// Check if an eventType is valid. -func IsAValidEvent(eventType string) bool { - switch eventType { - case string(Added): - return true - case string(Modified): - return true - case string(Deleted): - return true - default: - return false - } -} - -// Returns a slice with the items in a list like: item1, item2, item3. -func StringToSlice(text string) []string { - var slices []string - - if !strings.Contains(text, ",") { - slices = append(slices, strings.Trim(text, " ")) - return slices - } - - // Getting the namespaces to replicate - for _, slice := range strings.Split(text, ",") { - slices = append(slices, strings.Trim(slice, " ")) - } - - return slices -} diff --git a/kube_syncer/abstractions/configmap_event.go b/kube_syncer/abstractions/configmap_event.go deleted file mode 100644 index e270671..0000000 --- a/kube_syncer/abstractions/configmap_event.go +++ /dev/null @@ -1,124 +0,0 @@ -package abstractions - -import ( - "strings" - - str "github.com/appscode/go/strings" - corev1 "k8s.io/api/core/v1" - "k8s.io/client-go/kubernetes" -) - -type ConfigMapEvent struct { - EntityEvent -} - -func (c ConfigMapEvent) Sync(sourceContext string, kubeClients *map[string]*kubernetes.Clientset) { - configMap := c.Entity.(*corev1.ConfigMap) - sourceNamespace := configMap.Namespace - - // Check the synchronization type - syncType := GetSyncType(configMap.Labels[LabelSelector]) - - // Treating namespace synchronization - if syncType == Namespace { - - namespaceNameAnnotation := configMap.Annotations[NamespaceNameAnnotation] - namespaceLabelAnnotation := configMap.Annotations[NamespaceLabelAnnotation] - - var namespaces []string - - // If the replication is by name - if !str.IsEmpty(&namespaceNameAnnotation) { - - // Getting the namespaces to replicate - if namespaceNameAnnotation != All { - namespaces = StringToSlice(namespaceNameAnnotation) - delete(EntitiesToAllNamespaces["ConfigMaps"], configMap.Name) - } else { - // Getting all existing namespaces - for key := range Namespaces { - namespaces = append(namespaces, key) - } - EntitiesToAllNamespaces["ConfigMaps"][configMap.Name] = configMap - } - - } - - // If the replication is by label - if !str.IsEmpty(&namespaceLabelAnnotation) { - label, value, found := strings.Cut(namespaceLabelAnnotation, "=") - - if !found { - Logger.Warnf("The value '%s' for label '%s' is invalid.", namespaceLabelAnnotation, NamespaceLabelAnnotation) - } else { - // Getting all existing namespaces - for namespaceName, namespace := range Namespaces { - - if namespace.Labels[label] == strings.Trim(value, "\"") { - namespaces = append(namespaces, namespaceName) - Logger.Debugf("The namespace '%s' contains the synchronization label '%s'. The configmap '%s' will be synchronized.", namespaceName, namespaceLabelAnnotation, configMap.Name) - } - } - EntitiesToLabeledNamespaces["ConfigMaps"][configMap.Name] = configMap - } - } - - for _, destinationNamespace := range namespaces { - if configMap.Namespace == destinationNamespace { - continue - } - - kubeEntity := NewKubernetesEntity(*kubeClients, configMap, ConfigMapEntity, sourceNamespace, destinationNamespace, sourceContext, sourceContext) - - switch c.Type { - case Added: - kubeEntity.Create() - case Modified: - kubeEntity.Update() - case Deleted: - kubeEntity.Delete() - } - } - } - - if syncType == Cluster { - - // Getting the configuration annotation - annotation := configMap.Annotations[ClusterAnnotation] - clusters := StringToSlice(annotation) - - for _, destinationContext := range clusters { - if sourceContext == destinationContext { - continue - } - - kubeEntity := NewKubernetesEntity(*kubeClients, configMap, ConfigMapEntity, sourceNamespace, sourceNamespace, sourceContext, destinationContext) - - switch c.Type { - case Added: - kubeEntity.Create() - case Modified: - kubeEntity.Update() - case Deleted: - kubeEntity.Delete() - } - } - } - - if c.Type == Modified { - namespaceNameAnnotation := configMap.Annotations[NamespaceNameAnnotation] - if namespaceNameAnnotation != All { - delete(EntitiesToAllNamespaces["ConfigMaps"], configMap.Name) - } - - namespaceLabelAnnotation := configMap.Annotations[NamespaceLabelAnnotation] - if namespaceLabelAnnotation == "" { - delete(EntitiesToLabeledNamespaces["ConfigMaps"], configMap.Name) - } - } - - if c.Type == Deleted { - delete(EntitiesToAllNamespaces["ConfigMaps"], configMap.Name) - delete(EntitiesToLabeledNamespaces["ConfigMaps"], configMap.Name) - } -} diff --git a/kube_syncer/abstractions/entity_event.go b/kube_syncer/abstractions/entity_event.go deleted file mode 100644 index 50cfbd0..0000000 --- a/kube_syncer/abstractions/entity_event.go +++ /dev/null @@ -1,11 +0,0 @@ -package abstractions - -import ( - "k8s.io/apimachinery/pkg/runtime" -) - -type EntityEvent struct { - Type EventType - - Entity runtime.Object -} diff --git a/kube_syncer/abstractions/event_type.go b/kube_syncer/abstractions/event_type.go deleted file mode 100644 index ebd1c8e..0000000 --- a/kube_syncer/abstractions/event_type.go +++ /dev/null @@ -1,10 +0,0 @@ -package abstractions - -// EventType defines the possible types of events. -type EventType string - -const ( - Added EventType = "ADDED" - Modified EventType = "MODIFIED" - Deleted EventType = "DELETED" -) diff --git a/kube_syncer/abstractions/isynchronizable.go b/kube_syncer/abstractions/isynchronizable.go deleted file mode 100644 index 976ff17..0000000 --- a/kube_syncer/abstractions/isynchronizable.go +++ /dev/null @@ -1,8 +0,0 @@ -package abstractions - -import "k8s.io/client-go/kubernetes" - -// Used to synchronize events -type ISynchronizable interface { - Sync(sourceContext string, kubeClients *map[string]*kubernetes.Clientset) -} diff --git a/kube_syncer/abstractions/iwatchable.go b/kube_syncer/abstractions/iwatchable.go deleted file mode 100644 index 006ae55..0000000 --- a/kube_syncer/abstractions/iwatchable.go +++ /dev/null @@ -1,5 +0,0 @@ -package abstractions - -type IWatchable interface { - Watch() <-chan ISynchronizable -} diff --git a/kube_syncer/abstractions/kubernetes_entity.go b/kube_syncer/abstractions/kubernetes_entity.go deleted file mode 100644 index dd58fbd..0000000 --- a/kube_syncer/abstractions/kubernetes_entity.go +++ /dev/null @@ -1,241 +0,0 @@ -package abstractions - -import ( - "context" - "errors" - - corev1 "k8s.io/api/core/v1" - errorsTypes "k8s.io/apimachinery/pkg/api/errors" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes" -) - -type KubernetesEntity struct { - Entity runtime.Object - Type KubernetesEntityType - SourceNamespace string - DestinationNamespace string - SourceContext string - DestinationContext string - Client *kubernetes.Clientset -} - -func NewKubernetesEntity(clients map[string]*kubernetes.Clientset, entity runtime.Object, entityType KubernetesEntityType, sourceNamespace, destinationNamespace, sourceContext, destinationContext string) KubernetesEntity { - clientList := clients - return KubernetesEntity{ - Entity: entity, - Type: entityType, - SourceNamespace: sourceNamespace, - DestinationNamespace: destinationNamespace, - SourceContext: sourceContext, - DestinationContext: destinationContext, - Client: clientList[destinationContext], - } -} - -func (e *KubernetesEntity) Create() error { - defer recoverExecution() - - if e.Type == ConfigMapEntity { - client := e.Client.CoreV1().ConfigMaps(e.DestinationNamespace) - - sourceEntity := e.Entity.(*corev1.ConfigMap) - entity := getNewConfigMap(*sourceEntity, e.DestinationNamespace, e.SourceContext) - - _, error := client.Create(context.TODO(), &entity, v1.CreateOptions{}) - - if error == nil { - Logger.Infof("The configMap '%s' was added in the namespace '%s' on context '%s'.", entity.Name, entity.Namespace, e.DestinationContext) - } else { - if !errorsTypes.IsAlreadyExists(error) { - Logger.Error(error) - } else { - // If alredy exists it need to be updated. - _, error := client.Update(context.TODO(), &entity, v1.UpdateOptions{}) - if error == nil { - Logger.Infof("The configMap '%s' was updated in the namespace '%s' on context '%s'.", entity.Name, entity.Namespace, e.DestinationContext) - } else { - Logger.Error(error) - } - } - } - - return error - } - - if e.Type == SecretEntity { - client := e.Client.CoreV1().Secrets(e.DestinationNamespace) - - sourceEntity := e.Entity.(*corev1.Secret) - entity := getNewSecret(*sourceEntity, e.DestinationNamespace, e.SourceContext) - - _, error := client.Create(context.TODO(), &entity, v1.CreateOptions{}) - - if error == nil { - Logger.Infof("The secret '%s' was added in the namespace '%s' on context '%s'.", entity.Name, entity.Namespace, e.DestinationContext) - } else { - if !errorsTypes.IsAlreadyExists(error) { - Logger.Error(error) - } else { - // If alredy exists it need to be updated. - _, error := client.Update(context.TODO(), &entity, v1.UpdateOptions{}) - if error == nil { - Logger.Infof("The secret '%s' was updated in the namespace '%s' on context '%s'.", entity.Name, entity.Namespace, e.DestinationContext) - } else { - Logger.Error(error) - } - } - } - - return error - } - - return errors.New("unsuported type") -} - -func (e *KubernetesEntity) Update() error { - defer recoverExecution() - - if e.Type == ConfigMapEntity { - client := e.Client.CoreV1().ConfigMaps(e.DestinationNamespace) - - sourceEntity := e.Entity.(*corev1.ConfigMap) - entity := getNewConfigMap(*sourceEntity, e.DestinationNamespace, e.SourceContext) - - _, error := client.Update(context.TODO(), &entity, v1.UpdateOptions{}) - - if error == nil { - Logger.Infof("The configmap '%s' was updated in the namespace '%s' on context '%s'.", entity.Name, entity.Namespace, e.DestinationContext) - } else { - if !errorsTypes.IsNotFound(error) { - Logger.Error(error) - } else { - // If not exists it need to be created. - _, error := client.Create(context.TODO(), &entity, v1.CreateOptions{}) - if error == nil { - Logger.Infof("The configmap '%s' was created in the namespace '%s' on context '%s'.", entity.Name, entity.Namespace, e.DestinationContext) - } else { - Logger.Error(error) - } - } - } - - return error - } - - if e.Type == SecretEntity { - client := e.Client.CoreV1().Secrets(e.DestinationNamespace) - - sourceEntity := e.Entity.(*corev1.Secret) - entity := getNewSecret(*sourceEntity, e.DestinationNamespace, e.SourceContext) - - _, error := client.Update(context.TODO(), &entity, v1.UpdateOptions{}) - - if error == nil { - Logger.Infof("The secret '%s' was updated in the namespace '%s' on context '%s'.", entity.Name, entity.Namespace, e.DestinationContext) - } else { - if !errorsTypes.IsNotFound(error) { - Logger.Error(error) - } else { - // If not exists it need to be created. - _, error := client.Create(context.TODO(), &entity, v1.CreateOptions{}) - if error == nil { - Logger.Infof("The secret '%s' was created in the namespace '%s' on context '%s'.", entity.Name, entity.Namespace, e.DestinationContext) - } else { - Logger.Error(error) - } - } - } - - return error - } - - return errors.New("unsuported type") -} - -func (e *KubernetesEntity) Delete() error { - defer recoverExecution() - - if e.Type == ConfigMapEntity { - client := e.Client.CoreV1().ConfigMaps(e.DestinationNamespace) - - sourceEntity := e.Entity.(*corev1.ConfigMap) - entity := getNewConfigMap(*sourceEntity, e.DestinationNamespace, e.SourceContext) - - error := client.Delete(context.TODO(), entity.Name, v1.DeleteOptions{}) - - if error == nil { - Logger.Infof("The configmap '%s' was deleted from namespace '%s' on context '%s'.", entity.Name, entity.Namespace, e.DestinationContext) - } else { - if !errorsTypes.IsNotFound(error) { - Logger.Error(error) - } else { - Logger.Debugf("The configmap '%s' was already deleted from namespace '%s' on context '%s'.", entity.Name, entity.Namespace, e.DestinationContext) - } - } - - return error - } - - if e.Type == SecretEntity { - client := e.Client.CoreV1().Secrets(e.DestinationNamespace) - - sourceEntity := e.Entity.(*corev1.Secret) - entity := getNewSecret(*sourceEntity, e.DestinationNamespace, e.SourceContext) - - error := client.Delete(context.TODO(), entity.Name, v1.DeleteOptions{}) - - if error == nil { - Logger.Infof("The secret '%s' was deleted from namespace '%s' on context '%s'.", entity.Name, entity.Namespace, e.DestinationContext) - } else { - if !errorsTypes.IsNotFound(error) { - Logger.Error(error) - } else { - Logger.Debugf("The secret '%s' was already deleted from namespace '%s' on context '%s'.", entity.Name, entity.Namespace, e.DestinationContext) - } - } - - return error - } - - return errors.New("unsuported type") -} - -func getNewConfigMap(sourceConfigMap corev1.ConfigMap, namespace, sourceContext string) corev1.ConfigMap { - destinationConfigMap := sourceConfigMap.DeepCopy() - - destinationConfigMap.UID = "" - destinationConfigMap.Labels = map[string]string{} - destinationConfigMap.Labels[ManagedLabelSelector] = "true" - destinationConfigMap.Annotations = map[string]string{} - destinationConfigMap.Annotations[SourceClusterAnnotation] = sourceContext - destinationConfigMap.Annotations[SourceNamespaceAnnotation] = sourceConfigMap.Namespace - destinationConfigMap.Annotations[SourceResourceVersionAnnotation] = sourceConfigMap.ResourceVersion - destinationConfigMap.Namespace = namespace - destinationConfigMap.ResourceVersion = "" - - return *destinationConfigMap -} - -func getNewSecret(sourceSecret corev1.Secret, namespace, sourceContext string) corev1.Secret { - destinationSecret := sourceSecret.DeepCopy() - - destinationSecret.UID = "" - destinationSecret.Labels = map[string]string{} - destinationSecret.Labels[ManagedLabelSelector] = "true" - destinationSecret.Annotations = map[string]string{} - destinationSecret.Annotations[SourceClusterAnnotation] = sourceContext - destinationSecret.Annotations[SourceNamespaceAnnotation] = sourceSecret.Namespace - destinationSecret.Annotations[SourceResourceVersionAnnotation] = sourceSecret.ResourceVersion - destinationSecret.Namespace = namespace - destinationSecret.ResourceVersion = "" - - return *destinationSecret -} - -func recoverExecution() { - if r := recover(); r != nil { - Logger.Debugln("Program recovered.") - } -} diff --git a/kube_syncer/abstractions/kubernetes_entity_type.go b/kube_syncer/abstractions/kubernetes_entity_type.go deleted file mode 100644 index 908a034..0000000 --- a/kube_syncer/abstractions/kubernetes_entity_type.go +++ /dev/null @@ -1,9 +0,0 @@ -package abstractions - -// Accepted kuberentes entity types -type KubernetesEntityType string - -const ( - ConfigMapEntity KubernetesEntityType = "configmap" - SecretEntity KubernetesEntityType = "secret" -) diff --git a/kube_syncer/abstractions/namespace_event.go b/kube_syncer/abstractions/namespace_event.go deleted file mode 100644 index aae310a..0000000 --- a/kube_syncer/abstractions/namespace_event.go +++ /dev/null @@ -1,121 +0,0 @@ -package abstractions - -import ( - "strings" - - corev1 "k8s.io/api/core/v1" - errorsTypes "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/client-go/kubernetes" -) - -type NamespaceEvent struct { - EntityEvent -} - -func (c NamespaceEvent) Sync(sourceContext string, kubeClients *map[string]*kubernetes.Clientset) { - namespace := c.Entity.(*corev1.Namespace) - namespaceName := namespace.Name - - clients := *kubeClients - - switch c.Type { - case Added: - Namespaces[namespaceName] = namespace.DeepCopy() - c.addConfigMaps(sourceContext, clients, namespaceName) - c.addSecrets(sourceContext, clients, namespaceName) - case Deleted: - delete(Namespaces, namespaceName) - default: - // Do nothing. - } -} - -// Creates the ConfigMaps that should be synched to every namespace in this namespace. -func (n NamespaceEvent) addConfigMaps(sourceContext string, kubeClients map[string]*kubernetes.Clientset, namespace string) { - - for _, entity := range EntitiesToAllNamespaces["ConfigMaps"] { - configMap := entity.(*corev1.ConfigMap) - sourceNamespace := configMap.Namespace - - entity := NewKubernetesEntity(kubeClients, configMap, ConfigMapEntity, sourceNamespace, namespace, sourceContext, sourceContext) - - err := entity.Create() - if err != nil { - if !errorsTypes.IsAlreadyExists(err) { - Logger.Error(err) - } else { - Logger.Debugf("The configmap '%s' already exists in namespace '%s' on context '%s'.", configMap.Name, namespace, sourceContext) - } - } - } - - for _, entity := range EntitiesToLabeledNamespaces["ConfigMaps"] { - configMap := entity.(*corev1.ConfigMap) - namespaceLabelAnnotation := configMap.Annotations[NamespaceLabelAnnotation] - label, value, _ := strings.Cut(namespaceLabelAnnotation, "=") - - namespaceEntity := n.Entity.(*corev1.Namespace) - currentNamespaceLabel := namespaceEntity.Labels[label] - - if currentNamespaceLabel != strings.Trim(value, "\"") { - continue - } - - sourceNamespace := configMap.Namespace - entity := NewKubernetesEntity(kubeClients, configMap, ConfigMapEntity, sourceNamespace, namespace, sourceContext, sourceContext) - - err := entity.Create() - if err != nil { - if !errorsTypes.IsAlreadyExists(err) { - Logger.Error(err) - } else { - Logger.Debugf("The configmap '%s' already exists in namespace '%s' on context '%s'.", configMap.Name, namespace, sourceContext) - } - } - } -} - -// Creates the Secrets that should be synched to every namespace in this namespace. -func (n NamespaceEvent) addSecrets(sourceContext string, kubeClients map[string]*kubernetes.Clientset, namespace string) { - - for _, entity := range EntitiesToAllNamespaces["Secrets"] { - secret := entity.(*corev1.Secret) - sourceNamespace := secret.Namespace - - entity := NewKubernetesEntity(kubeClients, secret, SecretEntity, sourceNamespace, namespace, sourceContext, sourceContext) - - err := entity.Create() - if err != nil { - if !errorsTypes.IsAlreadyExists(err) { - Logger.Error(err) - } else { - Logger.Debugf("The secret '%s' already exists in namespace '%s' on context '%s'.", secret.Name, namespace, sourceContext) - } - } - } - - for _, entity := range EntitiesToLabeledNamespaces["Secrets"] { - secret := entity.(*corev1.Secret) - namespaceLabelAnnotation := secret.Annotations[NamespaceLabelAnnotation] - label, value, _ := strings.Cut(namespaceLabelAnnotation, "=") - - namespaceEntity := n.Entity.(*corev1.Namespace) - currentNamespaceLabel := namespaceEntity.Labels[label] - - if currentNamespaceLabel != strings.Trim(value, "\"") { - continue - } - - sourceNamespace := secret.Namespace - entity := NewKubernetesEntity(kubeClients, secret, SecretEntity, sourceNamespace, namespace, sourceContext, sourceContext) - - err := entity.Create() - if err != nil { - if !errorsTypes.IsAlreadyExists(err) { - Logger.Error(err) - } else { - Logger.Debugf("The secret '%s' already exists in namespace '%s' on context '%s'.", secret.Name, namespace, sourceContext) - } - } - } -} diff --git a/kube_syncer/abstractions/secret_event.go b/kube_syncer/abstractions/secret_event.go deleted file mode 100644 index b8ad2e7..0000000 --- a/kube_syncer/abstractions/secret_event.go +++ /dev/null @@ -1,129 +0,0 @@ -package abstractions - -import ( - "strings" - - str "github.com/appscode/go/strings" - corev1 "k8s.io/api/core/v1" - "k8s.io/client-go/kubernetes" -) - -type SecretEvent struct { - EntityEvent -} - -func (c SecretEvent) Sync(sourceContext string, kubeClients *map[string]*kubernetes.Clientset) { - secret := c.Entity.(*corev1.Secret) - sourceNamespace := secret.Namespace - - // Check the synchronization type - syncType := GetSyncType(secret.Labels[LabelSelector]) - - // Treating namespace synchronization - if syncType == Namespace { - - namespaceNameAnnotation := secret.Annotations[NamespaceNameAnnotation] - namespaceLabelAnnotation := secret.Annotations[NamespaceLabelAnnotation] - - var namespaces []string - - // If the replication is by name - if !str.IsEmpty(&namespaceNameAnnotation) { - - // Getting the namespaces to replicate - if namespaceNameAnnotation != All { - namespaces = StringToSlice(namespaceNameAnnotation) - delete(EntitiesToAllNamespaces["Secrets"], secret.Name) - } else { - // Getting all existing namespaces - for key := range Namespaces { - namespaces = append(namespaces, key) - } - EntitiesToAllNamespaces["Secrets"][secret.Name] = secret - } - - } - - // If the replication is by label - if !str.IsEmpty(&namespaceLabelAnnotation) { - label, value, found := strings.Cut(namespaceLabelAnnotation, "=") - - if !found { - Logger.Warnf("The value '%s' for label '%s' is invalid.", namespaceLabelAnnotation, NamespaceLabelAnnotation) - } else { - // Getting all existing namespaces - for namespaceName, namespace := range Namespaces { - - if namespace.Labels[label] == strings.Trim(value, "\"") { - namespaces = append(namespaces, namespaceName) - Logger.Debugf("The namespace '%s' contains the synchronization label '%s'. The secret '%s' will be synchronized.", namespaceName, namespaceLabelAnnotation, secret.Name) - } - - } - EntitiesToLabeledNamespaces["Secrets"][secret.Name] = secret - } - } - - if c.Type == Deleted { - delete(EntitiesToAllNamespaces["Secrets"], secret.Name) - } - - for _, destinationNamespace := range namespaces { - if secret.Namespace == destinationNamespace { - continue - } - - kubeEntity := NewKubernetesEntity(*kubeClients, secret, SecretEntity, sourceNamespace, destinationNamespace, sourceContext, sourceContext) - - switch c.Type { - case Added: - kubeEntity.Create() - case Modified: - kubeEntity.Update() - case Deleted: - kubeEntity.Delete() - } - } - } - - if syncType == Cluster { - - // Getting the configuration annotation - annotation := secret.Annotations[ClusterAnnotation] - clusters := StringToSlice(annotation) - - for _, destinationContext := range clusters { - if sourceContext == destinationContext { - continue - } - - kubeEntity := NewKubernetesEntity(*kubeClients, secret, SecretEntity, sourceNamespace, sourceNamespace, sourceContext, destinationContext) - - switch c.Type { - case Added: - kubeEntity.Create() - case Modified: - kubeEntity.Update() - case Deleted: - kubeEntity.Delete() - } - } - } - - if c.Type == Modified { - namespaceNameAnnotation := secret.Annotations[NamespaceNameAnnotation] - if namespaceNameAnnotation != All { - delete(EntitiesToAllNamespaces["Secrets"], secret.Name) - } - - namespaceLabelAnnotation := secret.Annotations[NamespaceLabelAnnotation] - if namespaceLabelAnnotation == "" { - delete(EntitiesToLabeledNamespaces["Secrets"], secret.Name) - } - } - - if c.Type == Deleted { - delete(EntitiesToAllNamespaces["Secrets"], secret.Name) - delete(EntitiesToLabeledNamespaces["Secrets"], secret.Name) - } -} diff --git a/kube_syncer/abstractions/sync_type.go b/kube_syncer/abstractions/sync_type.go deleted file mode 100644 index c2896f0..0000000 --- a/kube_syncer/abstractions/sync_type.go +++ /dev/null @@ -1,22 +0,0 @@ -package abstractions - -// Accepted synchronization types. -type SyncType string - -const ( - Namespace SyncType = "namespace" - Cluster SyncType = "cluster" -) - -// Returns the SyncType. -func GetSyncType(syncType string) SyncType { - if syncType == "namespace" { - return Namespace - } - - if syncType == "cluster" { - return Cluster - } - - return "" -} diff --git a/kube_syncer/abstractions/sync_type_test.go b/kube_syncer/abstractions/sync_type_test.go deleted file mode 100644 index df4d78d..0000000 --- a/kube_syncer/abstractions/sync_type_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package abstractions - -import "testing" - -func TestGetSyncType(t *testing.T) { - type args struct { - syncType string - } - tests := []struct { - name string - args args - want SyncType - }{ - { - name: "namespace", - args: args{ - syncType: "namespace", - }, - want: Namespace, - }, - { - name: "cluster", - args: args{ - syncType: "cluster", - }, - want: Cluster, - }, - { - name: "invalid", - args: args{ - syncType: "context", - }, - want: "", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := GetSyncType(tt.args.syncType); got != tt.want { - t.Errorf("GetSyncType() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/kube_syncer/configmap_watcher.go b/kube_syncer/configmap_watcher.go deleted file mode 100644 index ecf9eef..0000000 --- a/kube_syncer/configmap_watcher.go +++ /dev/null @@ -1,75 +0,0 @@ -package kube_syncer - -import ( - "context" - abstractions "keess/kube_syncer/abstractions" - - "go.uber.org/zap" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/kubernetes" -) - -// The configMap watcher. -type ConfigMapWatcher struct { - // The kubeapi client. - kubeClient *kubernetes.Clientset - - // The logger object. - logger *zap.SugaredLogger -} - -func (w ConfigMapWatcher) Watch() <-chan abstractions.ISynchronizable { - configMapsChan := make(chan abstractions.ISynchronizable) - - go func() { - watcher, err := w.kubeClient.CoreV1().ConfigMaps(metav1.NamespaceAll).Watch(context.TODO(), metav1.ListOptions{ - LabelSelector: abstractions.LabelSelector, - TimeoutSeconds: &abstractions.WatchTimeOut, - }) - - if err != nil { - w.logger.Error(err) - } - - w.logger.Info("Watching configMaps events.") - - for event := range watcher.ResultChan() { - - // If it's not a valid event jumps to the next. - if !abstractions.IsAValidEvent(string(event.Type)) { - w.logger.Debugf("Invalid event type '%s'.", event.Type) - continue - } - - var configMapEvent abstractions.ConfigMapEvent - configMapEvent.Entity = event.Object - - switch event.Type { - case watch.Added: - configMapEvent.Type = abstractions.Added - case watch.Modified: - configMapEvent.Type = abstractions.Modified - case watch.Deleted: - configMapEvent.Type = abstractions.Deleted - } - - item := event.Object.(*corev1.ConfigMap) - - if !abstractions.IsAValidLabelValue(item.Labels[abstractions.LabelSelector]) { - w.logger.Warnf("Invalid label value '%s' for '%s'. Accepted values are: '%s' and '%s' ", item.Labels[abstractions.LabelSelector], abstractions.LabelSelector, abstractions.Namespace, abstractions.Cluster) - continue - } - - // If this config map don't contains the mandatory configuration annotation, skip to the next one. - if !abstractions.ContainsAValidAnnotation(item.Annotations) { - w.logger.Warnf("Missing or invalid configuration annotation on ConfigMap '%s' in namespace '%s'.", item.Name, item.Namespace) - } - - configMapsChan <- configMapEvent - } - }() - - return configMapsChan -} diff --git a/kube_syncer/namespace_watcher.go b/kube_syncer/namespace_watcher.go deleted file mode 100644 index 00786f1..0000000 --- a/kube_syncer/namespace_watcher.go +++ /dev/null @@ -1,61 +0,0 @@ -package kube_syncer - -import ( - "context" - abstractions "keess/kube_syncer/abstractions" - - "go.uber.org/zap" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/kubernetes" -) - -// The namespace watcher. -type NamespaceWatcher struct { - // The kubeapi client. - kubeClient *kubernetes.Clientset - - // The logger object. - logger *zap.SugaredLogger -} - -func (w NamespaceWatcher) Watch() <-chan abstractions.ISynchronizable { - namespacesChan := make(chan abstractions.ISynchronizable) - - go func() { - watcher, err := w.kubeClient.CoreV1().Namespaces().Watch(context.TODO(), metav1.ListOptions{ - TimeoutSeconds: &abstractions.WatchTimeOut, - }) - - if err != nil { - w.logger.Error(err) - } - - w.logger.Info("Watching namespaces events.") - - for event := range watcher.ResultChan() { - - // If it's not a valid event jumps to the next. - if !abstractions.IsAValidEvent(string(event.Type)) { - w.logger.Debug("Invalid event type '%s'.", event.Type) - continue - } - - var namespaceEvent abstractions.NamespaceEvent - namespaceEvent.Entity = event.Object - - switch event.Type { - case watch.Added: - namespaceEvent.Type = abstractions.Added - case watch.Modified: - namespaceEvent.Type = abstractions.Modified - case watch.Deleted: - namespaceEvent.Type = abstractions.Deleted - } - - namespacesChan <- namespaceEvent - } - }() - - return namespacesChan -} diff --git a/kube_syncer/secret_watcher.go b/kube_syncer/secret_watcher.go deleted file mode 100644 index f2060a3..0000000 --- a/kube_syncer/secret_watcher.go +++ /dev/null @@ -1,75 +0,0 @@ -package kube_syncer - -import ( - "context" - abstractions "keess/kube_syncer/abstractions" - - "go.uber.org/zap" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/kubernetes" -) - -// The secret watcher. -type SecretWatcher struct { - // The kubeapi client. - kubeClient *kubernetes.Clientset - - // The logger object. - logger *zap.SugaredLogger -} - -func (w SecretWatcher) Watch() <-chan abstractions.ISynchronizable { - secretsChan := make(chan abstractions.ISynchronizable) - - go func() { - watcher, err := w.kubeClient.CoreV1().Secrets(metav1.NamespaceAll).Watch(context.TODO(), metav1.ListOptions{ - LabelSelector: abstractions.LabelSelector, - TimeoutSeconds: &abstractions.WatchTimeOut, - }) - - if err != nil { - w.logger.Error(err) - } - - w.logger.Info("Watching secrets events.") - - for event := range watcher.ResultChan() { - - // If it's not a valid event jumps to the next. - if !abstractions.IsAValidEvent(string(event.Type)) { - w.logger.Debugf("Invalid event type '%s'.", event.Type) - continue - } - - var secretEvent abstractions.SecretEvent - secretEvent.Entity = event.Object - - switch event.Type { - case watch.Added: - secretEvent.Type = abstractions.Added - case watch.Modified: - secretEvent.Type = abstractions.Modified - case watch.Deleted: - secretEvent.Type = abstractions.Deleted - } - - item := event.Object.(*corev1.Secret) - - if !abstractions.IsAValidLabelValue(item.Labels[abstractions.LabelSelector]) { - w.logger.Warnf("Invalid label value '%s' for '%s'. Accepted values are: '%s' and '%s' ", item.Labels[abstractions.LabelSelector], abstractions.LabelSelector, abstractions.Namespace, abstractions.Cluster) - continue - } - - // If this config map don't contains the mandatory configuration annotation, skip to the next one. - if !abstractions.ContainsAValidAnnotation(item.Annotations) { - w.logger.Warnf("Missing or invalid configuration annotation on Secret '%s' in namespace '%s'.", item.Name, item.Namespace) - } - - secretsChan <- secretEvent - } - }() - - return secretsChan -} diff --git a/kube_syncer/syncer.go b/kube_syncer/syncer.go deleted file mode 100644 index dc964a7..0000000 --- a/kube_syncer/syncer.go +++ /dev/null @@ -1,395 +0,0 @@ -package kube_syncer - -import ( - "context" - "flag" - "os" - "path/filepath" - "time" - - abstractions "keess/kube_syncer/abstractions" - - errorsTypes "k8s.io/apimachinery/pkg/api/errors" - - "github.com/appscode/go/strings" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes" - _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/util/homedir" -) - -// Represents a base structure for any syncer. -type Syncer struct { - kubeClients map[string]*kubernetes.Clientset - - sourceContext string - - destinationContexts []string - - // The logger object. - logger *zap.SugaredLogger - - atom zap.AtomicLevel -} - -func init() { - abstractions.EntitiesToAllNamespaces["ConfigMaps"] = make(map[string]runtime.Object) - abstractions.EntitiesToAllNamespaces["Secrets"] = make(map[string]runtime.Object) - abstractions.EntitiesToLabeledNamespaces["ConfigMaps"] = make(map[string]runtime.Object) - abstractions.EntitiesToLabeledNamespaces["Secrets"] = make(map[string]runtime.Object) -} - -func (s *Syncer) SetLogLevel(logLevel string) { - level, err := zapcore.ParseLevel(logLevel) - - if err == nil { - s.atom.SetLevel(level) - } else { - s.logger.Error(err) - } -} - -// Load the kubeClient based in the given configuration. -func (s *Syncer) Start(kubeConfigPath string, developmentMode bool, initialLogLevel string, sourceContext string, destinationContexts []string) error { - s.atom = zap.NewAtomicLevel() - - // To keep the example deterministic, disable timestamps in the output. - var encoderCfg zapcore.EncoderConfig - - if developmentMode { - encoderCfg = zap.NewDevelopmentEncoderConfig() - } else { - encoderCfg = zap.NewProductionEncoderConfig() - } - - encoderCfg.TimeKey = "timestamp" - encoderCfg.EncodeTime = zapcore.TimeEncoderOfLayout(time.RFC3339) - - logger := zap.New(zapcore.NewCore( - zapcore.NewJSONEncoder(encoderCfg), - zapcore.Lock(os.Stdout), - s.atom, - )) - defer logger.Sync() - - s.SetLogLevel(initialLogLevel) - - abstractions.Logger = logger.Sugar() - s.logger = abstractions.Logger - - s.sourceContext = sourceContext - s.destinationContexts = destinationContexts - var kubeconfig *string - - if kubeConfigPath == "" { - - flagLookup := flag.Lookup("kubeconfig") - if flagLookup == nil { - if home := homedir.HomeDir(); home != "" { - kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file") - } else { - kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file") - } - flag.Parse() - } else { - lookup := flagLookup.Value.String() - kubeconfig = &lookup - } - - } else { - kubeconfig = &kubeConfigPath - } - - // use the current context in kubeconfig - config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig) - if err != nil { - s.logger.Error(err) - } - - inClusterConfig, err := rest.InClusterConfig() - var client *kubernetes.Clientset - - if err == nil { - // create the clientset - client, err = kubernetes.NewForConfig(inClusterConfig) - if err != nil { - s.logger.Error(err) - } - s.logger.Info("Config loaded from service account.") - } else { - // create the clientset - client, err = kubernetes.NewForConfig(config) - if err != nil { - s.logger.Error(err) - } - s.logger.Info("Config loaded from kube config.") - } - - client.RESTClient().Get().Timeout(time.Duration(abstractions.WatchTimeOut)) - s.kubeClients = map[string]*kubernetes.Clientset{} - s.kubeClients[s.sourceContext] = client - - for _, context := range destinationContexts { - config, err := buildConfigWithContextFromFlags(context, *kubeconfig) - if err != nil { - s.logger.Error(err) - } - - // create the clientset - client, err := kubernetes.NewForConfig(config) - if err != nil { - s.logger.Error(err) - } - - client.RESTClient().Get().Timeout(time.Duration(abstractions.WatchTimeOut)) - s.kubeClients[context] = client - } - - s.logger.Info("Starting Keess.") - s.logger.Infof("Watching source cluster '%s'.", s.sourceContext) - s.logger.Infof("Connected to destination clusters %s.", s.destinationContexts) - - return nil -} - -func (s *Syncer) Run() error { - kubeClient := *s.kubeClients[s.sourceContext] - - var namespaceWatcher = NamespaceWatcher{ - kubeClient: &kubeClient, - logger: s.logger, - } - - var configMapWatcher = ConfigMapWatcher{ - kubeClient: &kubeClient, - logger: s.logger, - } - - var secretWatcher = SecretWatcher{ - kubeClient: &kubeClient, - logger: s.logger, - } - - s.logger.Info("Executing bootstrap process.") - - // First of all we need to load all namespaces. - namespaceList, err := kubeClient.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{}) - if err != nil { - s.logger.Error(err) - } - - for _, namespace := range namespaceList.Items { - abstractions.Namespaces[namespace.Name] = namespace.DeepCopy() - } - - // Now list all ConfigMaps that must be synchronized. - configMapList, err := kubeClient.CoreV1().ConfigMaps(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{ - LabelSelector: abstractions.LabelSelector, - }) - if err != nil { - s.logger.Error(err) - } - - for _, configMap := range configMapList.Items { - if configMap.Annotations[abstractions.NamespaceNameAnnotation] == abstractions.All { - abstractions.EntitiesToAllNamespaces["ConfigMaps"][configMap.Name] = configMap.DeepCopyObject() - } - namespaceLabelAnnotation := configMap.Annotations[abstractions.NamespaceLabelAnnotation] - if !strings.IsEmpty(&namespaceLabelAnnotation) { - abstractions.EntitiesToLabeledNamespaces["ConfigMaps"][configMap.Name] = configMap.DeepCopyObject() - } - } - - // Now list all Secrets that must be synchronized. - secretList, err := kubeClient.CoreV1().Secrets(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{ - LabelSelector: abstractions.LabelSelector, - }) - if err != nil { - s.logger.Error(err) - } - - for _, secret := range secretList.Items { - if secret.Annotations[abstractions.NamespaceNameAnnotation] == abstractions.All { - abstractions.EntitiesToAllNamespaces["Secrets"][secret.Name] = secret.DeepCopyObject() - } - namespaceLabelAnnotation := secret.Annotations[abstractions.NamespaceLabelAnnotation] - if !strings.IsEmpty(&namespaceLabelAnnotation) { - abstractions.EntitiesToLabeledNamespaces["Secrets"][secret.Name] = secret.DeepCopyObject() - } - } - - for currentContext, kubeClient := range s.kubeClients { - - // Don't look to another clusters on backward synchronization. - if currentContext != s.sourceContext { - continue - } - - // Now list all ConfigMaps that are managed by Keess. - managedConfigMapList, err := kubeClient.CoreV1().ConfigMaps(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{ - LabelSelector: abstractions.ManagedLabelSelector, - }) - if err != nil { - s.logger.Error(err) - } - - for _, configMap := range managedConfigMapList.Items { - var entity abstractions.KubernetesEntity - - // Get the source namespace name. - sourceNamespace := configMap.Annotations[abstractions.SourceNamespaceAnnotation] - sourceContext := configMap.Annotations[abstractions.SourceClusterAnnotation] - - if sourceNamespace == "" || sourceContext == "" { - s.logger.Warnf("The managed configmap contains invalid annotations values.") - continue - } - - // Only do back synchronization between namespaces of the same cluster. - if sourceContext != currentContext { - continue - } - - sourceKubeClient := s.kubeClients[sourceContext] - sourceConfigMap, err := sourceKubeClient.CoreV1().ConfigMaps(sourceNamespace).Get(context.TODO(), configMap.Name, metav1.GetOptions{}) - - if err != nil && !errorsTypes.IsNotFound(err) { - s.logger.Error(err) - } - - // Check if source configmap was deleted. - if errorsTypes.IsNotFound(err) { - entity = abstractions.NewKubernetesEntity(s.kubeClients, &configMap, abstractions.ConfigMapEntity, sourceNamespace, configMap.Namespace, sourceContext, currentContext) - - err := entity.Delete() - if err != nil && !errorsTypes.IsNotFound(err) { - s.logger.Error(err) - } else { - s.logger.Infof("The ConfigMap '%s' was deleted in namespace '%s' on context '%s' because it was deleted in the source namespace '%s' on the source context '%s'.", configMap.Name, configMap.Namespace, currentContext, sourceNamespace, sourceContext) - } - } - - if err == nil { - // Check if source configmap was changed. - if sourceConfigMap.ResourceVersion != configMap.Annotations[abstractions.SourceResourceVersionAnnotation] { - entity = abstractions.NewKubernetesEntity(s.kubeClients, sourceConfigMap, abstractions.ConfigMapEntity, sourceNamespace, configMap.Namespace, sourceContext, currentContext) - err := entity.Update() - if err != nil { - s.logger.Error(err) - } else { - s.logger.Infof("The ConfigMap '%s' was updated in namespace '%s' on context '%s' because It was updated in the source namespace '%s' on the source context '%s'.", configMap.Name, configMap.Namespace, currentContext, sourceNamespace, sourceContext) - } - } - } - } - - // Now list all Secrets that are managed by Keess. - managedSecretList, err := kubeClient.CoreV1().Secrets(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{ - LabelSelector: abstractions.ManagedLabelSelector, - }) - if err != nil { - s.logger.Error(err) - } - - for _, secret := range managedSecretList.Items { - var entity abstractions.KubernetesEntity - - // Get the source namespace name. - sourceNamespace := secret.Annotations[abstractions.SourceNamespaceAnnotation] - sourceContext := secret.Annotations[abstractions.SourceClusterAnnotation] - - if sourceNamespace == "" || sourceContext == "" { - s.logger.Warnf("The managed secret contains invalid annotations values.") - continue - } - - // Only do back synchronization between namespaces of the same cluster. - if sourceContext != currentContext { - continue - } - - sourceKubeClient := s.kubeClients[sourceContext] - sourceSecret, err := sourceKubeClient.CoreV1().Secrets(sourceNamespace).Get(context.TODO(), secret.Name, metav1.GetOptions{}) - - if err != nil && !errorsTypes.IsNotFound(err) { - s.logger.Error(err) - } - - // Check if source secret was deleted. - if errorsTypes.IsNotFound(err) { - entity = abstractions.NewKubernetesEntity(s.kubeClients, &secret, abstractions.SecretEntity, sourceNamespace, secret.Namespace, sourceContext, currentContext) - - err := entity.Delete() - if err != nil && !errorsTypes.IsNotFound(err) { - s.logger.Error(err) - } else { - s.logger.Infof("The Secret '%s' was deleted in namespace '%s' on context '%s' because It was deleted in the source namespace '%s' on the source context '%s'.", secret.Name, secret.Namespace, currentContext, sourceNamespace, sourceContext) - } - } - - if err == nil { - // Check if source secret was changed. - if sourceSecret.ResourceVersion != secret.Annotations[abstractions.SourceResourceVersionAnnotation] { - entity = abstractions.NewKubernetesEntity(s.kubeClients, sourceSecret, abstractions.SecretEntity, sourceNamespace, secret.Namespace, sourceContext, currentContext) - err := entity.Update() - if err != nil { - s.logger.Error(err) - } else { - s.logger.Infof("The Secret '%s' was updated in namespace '%s' on context '%s' because It was updated in the source namespace '%s' on the source context '%s'.", secret.Name, secret.Namespace, currentContext, sourceNamespace, sourceContext) - } - } - } - } - } - - s.logger.Info("The bootstrap process was finished.") - - // Start watching for changes on configmaps, secrets, and namespaces. - configMapChan := configMapWatcher.Watch() - secretChan := secretWatcher.Watch() - namespaceChan := namespaceWatcher.Watch() - - eventsChan := multiplex(configMapChan, secretChan, namespaceChan) - - go func() { - for { - for event := range eventsChan { - event.Sync(s.sourceContext, &s.kubeClients) - } - } - }() - - return nil -} - -func multiplex(configMapChan, secretChan, namespaceChan <-chan abstractions.ISynchronizable) <-chan abstractions.ISynchronizable { - outputChan := make(chan abstractions.ISynchronizable) - - go func() { - for { - select { - case event := <-configMapChan: - outputChan <- event - case event := <-secretChan: - outputChan <- event - case event := <-namespaceChan: - outputChan <- event - } - } - }() - - return outputChan -} - -func buildConfigWithContextFromFlags(context string, kubeconfigPath string) (*rest.Config, error) { - return clientcmd.NewNonInteractiveDeferredLoadingClientConfig( - &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfigPath}, - &clientcmd.ConfigOverrides{ - CurrentContext: context, - }).ClientConfig() -} diff --git a/main.go b/main.go index 85d14bc..ac50604 100644 --- a/main.go +++ b/main.go @@ -1,50 +1,28 @@ +/* +Copyright © 2024 Marcus Vinicius + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ package main -import ( - "fmt" - "keess/application" - "log" - "net/http" - "os" - "strings" -) +import "keess/cmd" func main() { - app := application.New() - - if error := app.Run(os.Args); error != nil { - log.Fatal(error) - } - - isHelp := false - for _, arg := range os.Args { - if strings.Contains(arg, "--help") || strings.HasPrefix(arg, "-h") { - isHelp = true - } - } - - // Create an HTTP server and add the health check handler as a handler - http.HandleFunc("/health", healthHandler) - http.ListenAndServe(":8080", nil) - - if !isHelp { - select {} - } -} - -func healthHandler(w http.ResponseWriter, r *http.Request) { - // Check the health of the server and return a status code accordingly - if serverIsHealthy() { - w.WriteHeader(http.StatusOK) - fmt.Fprint(w, "Server is healthy") - } else { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprint(w, "Server is not healthy") - } -} - -func serverIsHealthy() bool { - // Check the health of the server and return true or false accordingly - // For example, check if the server can connect to the database - return true + cmd.Execute() } diff --git a/pkg/services/abstractions.go b/pkg/services/abstractions.go new file mode 100644 index 0000000..3020bea --- /dev/null +++ b/pkg/services/abstractions.go @@ -0,0 +1,40 @@ +package services + +import ( + "strings" +) + +// Label that must be applied to the Secrets and ConfigMaps that are managed by keess. +const ManagedLabelSelector string = "keess.powerhrg.com/managed" + +// Label that must be applied to the Secrets and ConfigMaps that will be synchronized. +const LabelSelector string = "keess.powerhrg.com/sync" + +// Accepted annotation to configure the synchronization across clusters. +const ClusterAnnotation string = "keess.powerhrg.com/clusters" + +// Accepted annotation to configure the synchronization across namespaces. +const NamespaceNameAnnotation string = "keess.powerhrg.com/namespaces-names" + +// Accepted annotation to configure the synchronization across namespaces. +const NamespaceLabelAnnotation string = "keess.powerhrg.com/namespace-label" + +// Annotation with the source cluster of the object managed by kees. +const SourceClusterAnnotation string = "keess.powerhrg.com/source-cluster" + +// Annotation with the source namespace of the object managed by kees. +const SourceNamespaceAnnotation string = "keess.powerhrg.com/source-namespace" + +// Annotation with the source resource version of the object managed by kees. +const SourceResourceVersionAnnotation string = "keess.powerhrg.com/source-resource-version" + +// Constant with the annotation created by the kubectl apply command +const KubectlApplyAnnotation string = "kubectl.kubernetes.io/last-applied-configuration" + +func splitAndTrim(input string, separator string) []string { + words := strings.Split(input, separator) + for i, word := range words { + words[i] = strings.TrimSpace(word) + } + return words +} diff --git a/pkg/services/configmap_poller.go b/pkg/services/configmap_poller.go new file mode 100644 index 0000000..5a881ae --- /dev/null +++ b/pkg/services/configmap_poller.go @@ -0,0 +1,73 @@ +package services + +import ( + "context" + "time" + + "go.uber.org/zap" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// A struct that can poll for configMaps in a Kubernetes cluster. +type ConfigMapPoller struct { + cluster string + kubeClient IKubeClient + logger *zap.SugaredLogger + startup bool +} + +// Create a new ConfigMapPoller. +func NewConfigMapPoller(cluster string, kubeClient IKubeClient, logger *zap.SugaredLogger) *ConfigMapPoller { + return &ConfigMapPoller{ + cluster: cluster, + kubeClient: kubeClient, + logger: logger, + startup: true, + } +} + +// Poll for configMaps in a Kubernetes cluster. +func (w *ConfigMapPoller) PollConfigMaps(ctx context.Context, opts metav1.ListOptions, pollInterval time.Duration) (<-chan PacConfigMap, error) { + configMapsChan := make(chan PacConfigMap) + var interval time.Duration + + go func() { + defer close(configMapsChan) + + for { + + if w.startup { + interval = 0 + } else { + interval = pollInterval + } + + select { + case <-time.After(interval): + configMaps, err := w.kubeClient.CoreV1().ConfigMaps(metav1.NamespaceAll).List(ctx, opts) + if err != nil { + w.logger.Error("Failed to list configMaps: ", err) + return + } else { + w.logger.Debugf("Found %d configMaps.", len(configMaps.Items)) + } + + for _, sc := range configMaps.Items { + pacConfigMap := PacConfigMap{ + Cluster: w.cluster, + ConfigMap: sc, + } + configMapsChan <- pacConfigMap + } + + case <-ctx.Done(): + w.logger.Warn("ConfigMap polling stopped by context cancellation.") + return + } + w.startup = false + } + }() + + return configMapsChan, nil +} diff --git a/pkg/services/configmap_poller_test.go b/pkg/services/configmap_poller_test.go new file mode 100644 index 0000000..ef8abc8 --- /dev/null +++ b/pkg/services/configmap_poller_test.go @@ -0,0 +1,94 @@ +package services + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "go.uber.org/zap" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes/fake" +) + +func TestConfigMapPoller_PollConfigMaps(t *testing.T) { + cluster := "test-cluster" + mockKubeClient := fake.NewSimpleClientset() + logger, _ := zap.NewProduction() + sugaredLogger := logger.Sugar() + + configMapPoller := &ConfigMapPoller{ + cluster: cluster, + kubeClient: mockKubeClient, + logger: sugaredLogger, + startup: true, + } + + opts := metav1.ListOptions{} + pollInterval := time.Second * 5 + + ctx := context.Background() + + configMapsChan, err := configMapPoller.PollConfigMaps(ctx, opts, pollInterval) + assert.NoError(t, err, "PollConfigMaps should not return an error") + + // Create test configMaps + testConfigMaps := []*v1.ConfigMap{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "configMap1", + Namespace: "default", + }, + Data: map[string]string{ + "key1": string([]byte("value1")), + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "configMap2", + Namespace: "default", + }, + Data: map[string]string{ + "key2": string([]byte("value2")), + }, + }, + } + + // Add test configMaps to the fake clientset + for _, configMap := range testConfigMaps { + _, err := mockKubeClient.CoreV1().ConfigMaps(configMap.Namespace).Create(ctx, configMap, metav1.CreateOptions{}) + assert.NoError(t, err, "Failed to create test configMap") + } + + receivedConfigMaps := make(map[string]bool) + time.Sleep(2 * time.Second) + + // Verify that the configMaps are received on the channel + go func() { + for configMap := range configMapsChan { + receivedConfigMaps[configMap.ConfigMap.Name] = true + } + }() + + // Wait for the configMaps to be received + for { + if len(receivedConfigMaps) == len(testConfigMaps) { + break + } + time.Sleep(1 * time.Second) + } + + // Verify that all test configMaps are received + for _, configMap := range testConfigMaps { + assert.True(t, receivedConfigMaps[configMap.Name], "Test configMap not received") + } + + ctx.Done() +} + +// GroupVersionKind is a mock method for testing purposes +func (ps *PacConfigMap) GroupVersionKind() schema.GroupVersionKind { + return schema.GroupVersionKind{} +} diff --git a/pkg/services/configmap_synchronizer.go b/pkg/services/configmap_synchronizer.go new file mode 100644 index 0000000..6f73ebb --- /dev/null +++ b/pkg/services/configmap_synchronizer.go @@ -0,0 +1,356 @@ +package services + +import ( + "context" + "strings" + "time" + + "go.uber.org/zap" + "k8s.io/apimachinery/pkg/api/errors" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// A struct that can synchronize configMaps in a Kubernetes cluster. +type ConfigMapSynchronizer struct { + localKubeClient IKubeClient + remoteKubeClients map[string]IKubeClient + configMapPooller *ConfigMapPoller + namespacePoller *NamespacePoller + logger *zap.SugaredLogger + ConfigMaps map[string]*PacConfigMap +} + +func NewConfigMapSynchronizer( + localKubeClient IKubeClient, + remoteKubeClients map[string]IKubeClient, + configMapPooller *ConfigMapPoller, + namespacePoller *NamespacePoller, + logger *zap.SugaredLogger, +) *ConfigMapSynchronizer { + return &ConfigMapSynchronizer{ + localKubeClient: localKubeClient, + remoteKubeClients: remoteKubeClients, + configMapPooller: configMapPooller, + namespacePoller: namespacePoller, + logger: logger, + ConfigMaps: make(map[string]*PacConfigMap), + } +} + +// Start the configMap synchronizer. +func (s *ConfigMapSynchronizer) Start(ctx context.Context, pollInterval time.Duration, housekeepingInterval time.Duration) error { + err := s.startSyncyng(ctx, pollInterval) + if err != nil { + return err + } + + err = s.deleteOrphans(ctx, housekeepingInterval) + if err != nil { + return err + } + + return nil +} + +// Delete orphan configMaps in the local Kubernetes cluster. +func (s *ConfigMapSynchronizer) deleteOrphans(ctx context.Context, pollInterval time.Duration) error { + configMapsChan, err := s.configMapPooller.PollConfigMaps(ctx, v1.ListOptions{ + LabelSelector: ManagedLabelSelector, + }, pollInterval) + + if err != nil { + s.logger.Error("Failed to list managed configMaps: ", err) + return err + } + + go func() { + for { + select { + case configMap, ok := <-configMapsChan: + if !ok { + // Channel closed, stop the goroutine + return + } + + // Process the configMap + sourceCluster := configMap.ConfigMap.Annotations[SourceClusterAnnotation] + sourceNamespace := configMap.ConfigMap.Annotations[SourceNamespaceAnnotation] + + var isLocal bool = false + var remoteKubeClient IKubeClient + if sourceCluster == configMap.Cluster { + remoteKubeClient = s.localKubeClient + isLocal = true + } else { + + if _, ok := s.remoteKubeClients[sourceCluster]; !ok { + s.logger.Error("Remote client not found: ", sourceCluster) + continue + } + + remoteKubeClient = s.remoteKubeClients[sourceCluster] + } + + // Check if the configMap is orphan + remoteConfigMap, err := remoteKubeClient.CoreV1().ConfigMaps(sourceNamespace).Get(ctx, configMap.ConfigMap.Name, v1.GetOptions{}) + + // Delete the orphan configMap + if errors.IsNotFound(err) { + err := s.localKubeClient.CoreV1().ConfigMaps(configMap.ConfigMap.Namespace).Delete(ctx, configMap.ConfigMap.Name, v1.DeleteOptions{}) + if err == nil { + s.logger.Infof("Orphan configMap %s deleted on cluster %s in namespace %s", configMap.ConfigMap.Name, configMap.Cluster, configMap.ConfigMap.Namespace) + } else { + s.logger.Errorf("Failed to delete orphan configMap %s deleted on cluster %s in namespace %s: %s", configMap.ConfigMap.Name, configMap.Cluster, configMap.ConfigMap.Namespace, err) + } + } + + if remoteConfigMap.Labels[LabelSelector] == "cluster" && isLocal { + err := s.localKubeClient.CoreV1().ConfigMaps(configMap.ConfigMap.Namespace).Delete(ctx, configMap.ConfigMap.Name, v1.DeleteOptions{}) + if err == nil { + s.logger.Infof("Orphan configMap %s deleted on cluster %s in namespace %s", configMap.ConfigMap.Name, configMap.Cluster, configMap.ConfigMap.Namespace) + } else { + s.logger.Errorf("Failed to delete orphan configMap %s deleted on cluster %s in namespace %s: %s", configMap.ConfigMap.Name, configMap.Cluster, configMap.ConfigMap.Namespace, err) + } + } + + // Check if the label and annotations of the remote configMap means that this configMap should exists, otherwise delete this configMap + keep := false + if remoteConfigMap.Labels[LabelSelector] == "namespace" { + if remoteConfigMap.Annotations[NamespaceLabelAnnotation] != "" { + keyValue := splitAndTrim(remoteConfigMap.Annotations[NamespaceLabelAnnotation], "=") + key := keyValue[0] + value := strings.Trim(keyValue[1], "\"") + + namespace, err := s.localKubeClient.CoreV1().Namespaces().Get(ctx, configMap.ConfigMap.Namespace, v1.GetOptions{}) + if err != nil { + s.logger.Error("Failed to get namespace: ", err) + continue + } + if namespace.Labels[key] == value { + keep = true + } + } + + if remoteConfigMap.Annotations[NamespaceNameAnnotation] != "" { + if remoteConfigMap.Annotations[NamespaceNameAnnotation] == "all" { + keep = true + } else { + namespaces := splitAndTrim(remoteConfigMap.Annotations[NamespaceNameAnnotation], ",") + for _, namespace := range namespaces { + if namespace == configMap.ConfigMap.Namespace { + keep = true + break + } + } + } + } + } + + if remoteConfigMap.Labels[LabelSelector] == "cluster" { + destinationClusters := splitAndTrim(remoteConfigMap.Annotations[ClusterAnnotation], ",") + for _, cluster := range destinationClusters { + if cluster == configMap.Cluster { + keep = true + } + } + } + + if !keep { + err := s.localKubeClient.CoreV1().ConfigMaps(configMap.ConfigMap.Namespace).Delete(ctx, configMap.ConfigMap.Name, v1.DeleteOptions{}) + if err == nil { + s.logger.Infof("Orphan configMap %s deleted on cluster %s in namespace %s", configMap.ConfigMap.Name, configMap.Cluster, configMap.ConfigMap.Namespace) + } else { + s.logger.Errorf("Failed to delete orphan configMap %s deleted on cluster %s in namespace %s: %s", configMap.ConfigMap.Name, configMap.Cluster, configMap.ConfigMap.Namespace, err) + } + } + + case <-ctx.Done(): + s.logger.Warn("ConfigMap synchronization stopped by context cancellation.") + return + } + } + }() + + return nil +} + +// Synchronize configMaps in Kubernetes clusters. +func (s *ConfigMapSynchronizer) startSyncyng(ctx context.Context, pollInterval time.Duration) error { + s.ConfigMaps = make(map[string]*PacConfigMap) + + configMapsChan, err := s.configMapPooller.PollConfigMaps(ctx, v1.ListOptions{ + LabelSelector: LabelSelector, + }, pollInterval) + + if err != nil { + s.logger.Error("Failed to list configMaps: ", err) + return err + } + + go func() { + for { + select { + case configMap, ok := <-configMapsChan: + if !ok { + // Channel closed, stop the goroutine + return + } + + s.sync(ctx, configMap) + + case <-ctx.Done(): + s.logger.Warn("ConfigMap synchronization stopped by context cancellation.") + return + } + } + }() + + return nil +} + +// Synchronize the configMap in Kubernetes clusters. +func (s *ConfigMapSynchronizer) sync(ctx context.Context, pacConfigMap PacConfigMap) error { + + if pacConfigMap.ConfigMap.Labels[LabelSelector] == "namespace" { + return s.syncLocal(ctx, pacConfigMap) + } + + if pacConfigMap.ConfigMap.Labels[LabelSelector] == "cluster" { + return s.syncRemote(ctx, pacConfigMap) + } + + return nil +} + +// Synchronize the configMap in the local Kubernetes cluster. +func (s *ConfigMapSynchronizer) syncLocal(ctx context.Context, pacConfigMap PacConfigMap) error { + // Synchronize based on the namespace label + if namespaceLabelAnnotationValue, ok := pacConfigMap.ConfigMap.Annotations[NamespaceLabelAnnotation]; ok { + keyValue := splitAndTrim(namespaceLabelAnnotationValue, "=") + key := keyValue[0] + value := strings.Trim(keyValue[1], "\"") + + for _, namespace := range s.namespacePoller.Namespaces { + + // Skip the source namespace + if namespace.Namespace.Name == pacConfigMap.ConfigMap.Namespace { + s.logger.Debugf("Skipping source namespace: %s when synchronizing configMap: %s", namespace.Namespace.Name, pacConfigMap.ConfigMap.Name) + continue + } + + // Synchronize the configMap if the namespace has the label + if labelValue, ok := namespace.Namespace.Labels[key]; ok && labelValue == value { + s.createOrUpdate(ctx, s.localKubeClient, pacConfigMap, pacConfigMap.Cluster, namespace.Namespace.Name) + } + } + + return nil + } + + // Synchronize based on the namespace name + if namespaceNameAnnotationValue, ok := pacConfigMap.ConfigMap.Annotations[NamespaceNameAnnotation]; ok && namespaceNameAnnotationValue != "all" { + namespaces := splitAndTrim(namespaceNameAnnotationValue, ",") + for _, namespace := range namespaces { + _, ok := s.namespacePoller.Namespaces[namespace] + if !ok { + s.logger.Warn("Namespace not found: ", namespace) + continue + } + + // Skip the source namespace + if pacConfigMap.ConfigMap.Namespace == namespace { + s.logger.Debugf("Skipping source namespace: %s when synchronizing configMap: %s", namespace, pacConfigMap.ConfigMap.Name) + continue + } + + s.createOrUpdate(ctx, s.localKubeClient, pacConfigMap, pacConfigMap.Cluster, namespace) + } + return nil + } + + // Synchronize all namespaces + if namespaceNameAnnotationValue, ok := pacConfigMap.ConfigMap.Annotations[NamespaceNameAnnotation]; ok && namespaceNameAnnotationValue == "all" { + namespaces := s.namespacePoller.Namespaces + + for _, namespace := range namespaces { + + // Skip the source namespace + if pacConfigMap.ConfigMap.Namespace == namespace.Namespace.Name { + s.logger.Debugf("Skipping source namespace: %s when synchronizing configMap: %s", namespace.Namespace.Name, pacConfigMap.ConfigMap.Name) + continue + } + + s.createOrUpdate(ctx, s.localKubeClient, pacConfigMap, pacConfigMap.Cluster, namespace.Namespace.Name) + } + return nil + } + + return nil +} + +// Synchronize the configMap in the remote Kubernetes clusters. +func (s *ConfigMapSynchronizer) syncRemote(ctx context.Context, pacConfigMap PacConfigMap) error { + clusters := splitAndTrim(pacConfigMap.ConfigMap.Annotations[ClusterAnnotation], ",") + + for _, cluster := range clusters { + client, ok := s.remoteKubeClients[cluster] + + if !ok { + s.logger.Error("Remote client not found: ", cluster) + continue + } + + // Skip the source cluster. + if pacConfigMap.Cluster == cluster { + s.logger.Debugf("Skipping source cluster: %s when synchronizing configMap: %s", cluster, pacConfigMap.ConfigMap.Name) + continue + } + + s.createOrUpdate(ctx, client, pacConfigMap, cluster, pacConfigMap.ConfigMap.Namespace) + } + + return nil +} + +// Create or update the configMap in the Kubernetes cluster. +func (s *ConfigMapSynchronizer) createOrUpdate(ctx context.Context, client IKubeClient, pacConfigMap PacConfigMap, cluster string, namespace string) error { + gotConfigMap, err := client.CoreV1().ConfigMaps(namespace).Get(ctx, pacConfigMap.ConfigMap.Name, v1.GetOptions{}) + + if err != nil { + + if errors.IsNotFound(err) { + newConfigMap := pacConfigMap.Prepare(namespace) + _, err := client.CoreV1().ConfigMaps(namespace).Create(ctx, &newConfigMap, v1.CreateOptions{}) + + if err == nil { + s.logger.Infof("ConfigMap %s created on cluster %s in namespace %s", pacConfigMap.ConfigMap.Name, cluster, namespace) + } else { + s.logger.Errorf("Failed to create configMap %s on cluster %s in namespace %s: %s", pacConfigMap.ConfigMap.Name, cluster, namespace, err) + } + + return err + } else { + s.logger.Error("Failed to get configMap: ", err) + } + + return err + } + + // Update the configMap if it has changed + if pacConfigMap.HasChanged(*gotConfigMap) { + updatedConfigMap := pacConfigMap.Prepare(namespace) + _, err := client.CoreV1().ConfigMaps(namespace).Update(ctx, &updatedConfigMap, v1.UpdateOptions{}) + + if err == nil { + s.logger.Infof("ConfigMap %s updated on cluster %s in namespace %s", pacConfigMap.ConfigMap.Name, cluster, namespace) + } else { + s.logger.Errorf("Failed to update configMap %s on cluster %s in namespace %s: %s", pacConfigMap.ConfigMap.Name, cluster, namespace, err) + } + + return err + } else { + s.logger.Debugf("ConfigMap %s is up do date on cluster %s in namespace %s", pacConfigMap.ConfigMap.Name, cluster, namespace) + } + + return err +} diff --git a/pkg/services/i_kube_client.go b/pkg/services/i_kube_client.go new file mode 100644 index 0000000..263842c --- /dev/null +++ b/pkg/services/i_kube_client.go @@ -0,0 +1,9 @@ +package services + +import ( + v1 "k8s.io/client-go/kubernetes/typed/core/v1" +) + +type IKubeClient interface { + CoreV1() v1.CoreV1Interface +} diff --git a/pkg/services/namespace_poller.go b/pkg/services/namespace_poller.go new file mode 100644 index 0000000..cef35d4 --- /dev/null +++ b/pkg/services/namespace_poller.go @@ -0,0 +1,83 @@ +package services + +import ( + "context" + "sync" + "time" + + "go.uber.org/zap" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// A struct that can poll for namespaces in a Kubernetes cluster. +type NamespacePoller struct { + kubeClient IKubeClient + logger *zap.SugaredLogger + Namespaces map[string]*PacNamespace + mutex sync.Mutex // Add mutex field + startup bool +} + +// Create a new NamespacePoller. +func NewNamespacePoller(kubeClient IKubeClient, logger *zap.SugaredLogger) *NamespacePoller { + return &NamespacePoller{ + kubeClient: kubeClient, + logger: logger, + Namespaces: make(map[string]*PacNamespace), + startup: true, + } +} + +// Poll for namespaces in a Kubernetes cluster. +func (w *NamespacePoller) PollNamespaces(ctx context.Context, opts metav1.ListOptions, pollInterval time.Duration, cluster string) error { + + var interval time.Duration + go func() { + for { + if w.startup { + interval = 0 + } else { + interval = pollInterval + } + + select { + case <-time.After(interval): + namespaces, err := w.kubeClient.CoreV1().Namespaces().List(ctx, opts) + if err != nil { + w.logger.Error("Failed to list namespaces: ", err) + return + } else { + w.logger.Debugf("Found %d namespaces.", len(namespaces.Items)) + } + + w.updateNamespacesMap(namespaces.Items, cluster) + + case <-ctx.Done(): + w.logger.Warn("Namespace polling stopped by context cancellation.") + return + } + w.startup = false + } + }() + return nil +} + +// Update the namespaces map. +func (w *NamespacePoller) updateNamespacesMap(currentNamespaces []corev1.Namespace, cluster string) { + // Ensure thread-safe access if needed + w.mutex.Lock() + defer w.mutex.Unlock() + + // Clear existing map + w.Namespaces = make(map[string]*PacNamespace) + + // Populate map with current namespaces + for _, ns := range currentNamespaces { + w.Namespaces[ns.Name] = &PacNamespace{ + Namespace: ns.DeepCopy(), + Cluster: cluster, + } + } +} diff --git a/pkg/services/pac_configmap.go b/pkg/services/pac_configmap.go new file mode 100644 index 0000000..c423eff --- /dev/null +++ b/pkg/services/pac_configmap.go @@ -0,0 +1,52 @@ +package services + +import ( + v1 "k8s.io/api/core/v1" +) + +// A struct that represents a configMap in a Kubernetes cluster. +type PacConfigMap struct { + // The name of the cluster where the configMap is located. + Cluster string + + // The configMap. + ConfigMap v1.ConfigMap +} + +// Prepare the configMap for persistence. +func (s *PacConfigMap) Prepare(namespace string) v1.ConfigMap { + newConfigMap := s.ConfigMap.DeepCopy() + newConfigMap.Namespace = namespace + + newConfigMap.UID = "" + newConfigMap.Labels = map[string]string{} + newConfigMap.Labels[ManagedLabelSelector] = "true" + newConfigMap.Annotations = map[string]string{} + newConfigMap.Annotations[SourceClusterAnnotation] = s.Cluster + newConfigMap.Annotations[SourceNamespaceAnnotation] = s.ConfigMap.Namespace + newConfigMap.Annotations[SourceResourceVersionAnnotation] = s.ConfigMap.ResourceVersion + newConfigMap.ResourceVersion = "" + + return *newConfigMap +} + +// Check if the remote configMap has changed. +func (s *PacConfigMap) HasChanged(remote v1.ConfigMap) bool { + if s.ConfigMap.ResourceVersion != remote.Annotations[SourceResourceVersionAnnotation] { + return true + } + + if remote.Labels[ManagedLabelSelector] != "true" { + return true + } + + if remote.Annotations[SourceClusterAnnotation] != s.Cluster { + return true + } + + if remote.Annotations[SourceNamespaceAnnotation] != s.ConfigMap.Namespace { + return true + } + + return false +} diff --git a/pkg/services/pac_configmap_test.go b/pkg/services/pac_configmap_test.go new file mode 100644 index 0000000..edc5faf --- /dev/null +++ b/pkg/services/pac_configmap_test.go @@ -0,0 +1,152 @@ +package services + +import ( + "testing" + + "github.com/stretchr/testify/assert" + core "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestConfigMapPrepare(t *testing.T) { + originalConfigMap := core.ConfigMap{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "original-ns", + UID: "original-uid", + ResourceVersion: "123", + Annotations: map[string]string{ + "SomeOtherAnnotation": "value", + }, + Labels: map[string]string{ + "SomeOtherLabel": "value", + }, + }, + } + pacConfigMap := PacConfigMap{ + ConfigMap: originalConfigMap, + Cluster: "test-cluster", + } + + newNamespace := "new-ns" + preparedConfigMap := pacConfigMap.Prepare(newNamespace) + + // Check if the original ConfigMap was not modified + assert.Equal(t, newNamespace, preparedConfigMap.Namespace, "Namespace should be updated") + + // Check if the UID was cleared + assert.Empty(t, preparedConfigMap.UID, "UID should be empty") + + // Check if the Labels were correctly set + assert.Equal(t, map[string]string{ManagedLabelSelector: "true"}, preparedConfigMap.Labels, "Labels should be correctly set") + + // Check if the Annotations were correctly set + expectedAnnotations := map[string]string{ + SourceClusterAnnotation: "test-cluster", + SourceNamespaceAnnotation: "original-ns", + SourceResourceVersionAnnotation: "123", + } + assert.Equal(t, expectedAnnotations, preparedConfigMap.Annotations, "Annotations should be correctly set") + + // Check if the ResourceVersion was cleared + assert.Empty(t, preparedConfigMap.ResourceVersion, "ResourceVersion should be empty") +} + +func TestConfigMapHasChanged(t *testing.T) { + + localConfigMap := core.ConfigMap{ + ObjectMeta: v1.ObjectMeta{ + ResourceVersion: "1", + Namespace: "local-ns", + }, + } + pacConfigMap := PacConfigMap{ + ConfigMap: localConfigMap, + Cluster: "local-cluster", + } + + tests := []struct { + name string + remote core.ConfigMap + expected bool + }{ + { + name: "Different ResourceVersion", + remote: core.ConfigMap{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + SourceResourceVersionAnnotation: "2", + }, + }, + }, + expected: true, + }, + { + name: "ManagedLabelSelector not true", + remote: core.ConfigMap{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + SourceResourceVersionAnnotation: "1", + }, + Labels: map[string]string{ + ManagedLabelSelector: "false", + }, + }, + }, + expected: true, + }, + { + name: "Different SourceClusterAnnotation", + remote: core.ConfigMap{ + ObjectMeta: v1.ObjectMeta{ + Labels: map[string]string{ + ManagedLabelSelector: "true", + }, + Annotations: map[string]string{ + SourceResourceVersionAnnotation: "1", + SourceClusterAnnotation: "remote-cluster", + }, + }, + }, + expected: true, + }, + { + name: "Different SourceNamespaceAnnotation", + remote: core.ConfigMap{ + ObjectMeta: v1.ObjectMeta{ + Labels: map[string]string{ + ManagedLabelSelector: "true", + }, + Annotations: map[string]string{ + SourceResourceVersionAnnotation: "1", + SourceClusterAnnotation: "local-cluster", + SourceNamespaceAnnotation: "remote-ns", + }, + }, + }, + expected: true, + }, + { + name: "No Changes", + remote: core.ConfigMap{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + SourceResourceVersionAnnotation: "1", + SourceClusterAnnotation: "local-cluster", + SourceNamespaceAnnotation: "local-ns", + }, + Labels: map[string]string{ + ManagedLabelSelector: "true", + }, + }, + }, + expected: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result := pacConfigMap.HasChanged(test.remote) + assert.Equal(t, test.expected, result) + }) + } +} diff --git a/pkg/services/pac_namespace.go b/pkg/services/pac_namespace.go new file mode 100644 index 0000000..419d99d --- /dev/null +++ b/pkg/services/pac_namespace.go @@ -0,0 +1,11 @@ +package services + +import ( + corev1 "k8s.io/api/core/v1" +) + +// Extends corev1.Namespace adding PAC context on that. +type PacNamespace struct { + Namespace *corev1.Namespace + Cluster string +} diff --git a/pkg/services/pac_secret.go b/pkg/services/pac_secret.go new file mode 100644 index 0000000..0734f72 --- /dev/null +++ b/pkg/services/pac_secret.go @@ -0,0 +1,52 @@ +package services + +import ( + v1 "k8s.io/api/core/v1" +) + +// A struct that represents a secret in a Kubernetes cluster. +type PacSecret struct { + // The name of the cluster where the secret is located. + Cluster string + + // The secret. + Secret v1.Secret +} + +// Prepare the secret for persistence. +func (s *PacSecret) Prepare(namespace string) v1.Secret { + newSecret := s.Secret.DeepCopy() + newSecret.Namespace = namespace + + newSecret.UID = "" + newSecret.Labels = map[string]string{} + newSecret.Labels[ManagedLabelSelector] = "true" + newSecret.Annotations = map[string]string{} + newSecret.Annotations[SourceClusterAnnotation] = s.Cluster + newSecret.Annotations[SourceNamespaceAnnotation] = s.Secret.Namespace + newSecret.Annotations[SourceResourceVersionAnnotation] = s.Secret.ResourceVersion + newSecret.ResourceVersion = "" + + return *newSecret +} + +// Check if the remote secret has changed. +func (s *PacSecret) HasChanged(remote v1.Secret) bool { + if s.Secret.ResourceVersion != remote.Annotations[SourceResourceVersionAnnotation] { + return true + } + + if remote.Labels[ManagedLabelSelector] != "true" { + return true + } + + if remote.Annotations[SourceClusterAnnotation] != s.Cluster { + return true + } + + if remote.Annotations[SourceNamespaceAnnotation] != s.Secret.Namespace { + return true + } + + return false +} diff --git a/pkg/services/pac_secret_test.go b/pkg/services/pac_secret_test.go new file mode 100644 index 0000000..64c83bb --- /dev/null +++ b/pkg/services/pac_secret_test.go @@ -0,0 +1,152 @@ +package services + +import ( + "testing" + + "github.com/stretchr/testify/assert" + core "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestPrepare(t *testing.T) { + originalSecret := core.Secret{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "original-ns", + UID: "original-uid", + ResourceVersion: "123", + Annotations: map[string]string{ + "SomeOtherAnnotation": "value", + }, + Labels: map[string]string{ + "SomeOtherLabel": "value", + }, + }, + } + pacSecret := PacSecret{ + Secret: originalSecret, + Cluster: "test-cluster", + } + + newNamespace := "new-ns" + preparedSecret := pacSecret.Prepare(newNamespace) + + // Check if the original Secret was not modified + assert.Equal(t, newNamespace, preparedSecret.Namespace, "Namespace should be updated") + + // Check if the UID was cleared + assert.Empty(t, preparedSecret.UID, "UID should be empty") + + // Check if the Labels were correctly set + assert.Equal(t, map[string]string{ManagedLabelSelector: "true"}, preparedSecret.Labels, "Labels should be correctly set") + + // Check if the Annotations were correctly set + expectedAnnotations := map[string]string{ + SourceClusterAnnotation: "test-cluster", + SourceNamespaceAnnotation: "original-ns", + SourceResourceVersionAnnotation: "123", + } + assert.Equal(t, expectedAnnotations, preparedSecret.Annotations, "Annotations should be correctly set") + + // Check if the ResourceVersion was cleared + assert.Empty(t, preparedSecret.ResourceVersion, "ResourceVersion should be empty") +} + +func TestHasChanged(t *testing.T) { + + localSecret := core.Secret{ + ObjectMeta: v1.ObjectMeta{ + ResourceVersion: "1", + Namespace: "local-ns", + }, + } + pacSecret := PacSecret{ + Secret: localSecret, + Cluster: "local-cluster", + } + + tests := []struct { + name string + remote core.Secret + expected bool + }{ + { + name: "Different ResourceVersion", + remote: core.Secret{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + SourceResourceVersionAnnotation: "2", + }, + }, + }, + expected: true, + }, + { + name: "ManagedLabelSelector not true", + remote: core.Secret{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + SourceResourceVersionAnnotation: "1", + }, + Labels: map[string]string{ + ManagedLabelSelector: "false", + }, + }, + }, + expected: true, + }, + { + name: "Different SourceClusterAnnotation", + remote: core.Secret{ + ObjectMeta: v1.ObjectMeta{ + Labels: map[string]string{ + ManagedLabelSelector: "true", + }, + Annotations: map[string]string{ + SourceResourceVersionAnnotation: "1", + SourceClusterAnnotation: "remote-cluster", + }, + }, + }, + expected: true, + }, + { + name: "Different SourceNamespaceAnnotation", + remote: core.Secret{ + ObjectMeta: v1.ObjectMeta{ + Labels: map[string]string{ + ManagedLabelSelector: "true", + }, + Annotations: map[string]string{ + SourceResourceVersionAnnotation: "1", + SourceClusterAnnotation: "local-cluster", + SourceNamespaceAnnotation: "remote-ns", + }, + }, + }, + expected: true, + }, + { + name: "No Changes", + remote: core.Secret{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + SourceResourceVersionAnnotation: "1", + SourceClusterAnnotation: "local-cluster", + SourceNamespaceAnnotation: "local-ns", + }, + Labels: map[string]string{ + ManagedLabelSelector: "true", + }, + }, + }, + expected: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result := pacSecret.HasChanged(test.remote) + assert.Equal(t, test.expected, result) + }) + } +} diff --git a/pkg/services/secret_poller.go b/pkg/services/secret_poller.go new file mode 100644 index 0000000..be43f1a --- /dev/null +++ b/pkg/services/secret_poller.go @@ -0,0 +1,73 @@ +package services + +import ( + "context" + "time" + + "go.uber.org/zap" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// A struct that can poll for secrets in a Kubernetes cluster. +type SecretPoller struct { + cluster string + kubeClient IKubeClient + logger *zap.SugaredLogger + startup bool +} + +// Create a new SecretPoller. +func NewSecretPoller(cluster string, kubeClient IKubeClient, logger *zap.SugaredLogger) *SecretPoller { + return &SecretPoller{ + cluster: cluster, + kubeClient: kubeClient, + logger: logger, + startup: true, + } +} + +// Poll for secrets in a Kubernetes cluster. +func (w *SecretPoller) PollSecrets(ctx context.Context, opts metav1.ListOptions, pollInterval time.Duration) (<-chan PacSecret, error) { + secretsChan := make(chan PacSecret) + var interval time.Duration + + go func() { + defer close(secretsChan) + + for { + + if w.startup { + interval = 0 + } else { + interval = pollInterval + } + + select { + case <-time.After(interval): + secrets, err := w.kubeClient.CoreV1().Secrets(metav1.NamespaceAll).List(ctx, opts) + if err != nil { + w.logger.Error("Failed to list secrets: ", err) + return + } else { + w.logger.Debugf("Found %d secrets.", len(secrets.Items)) + } + + for _, sc := range secrets.Items { + pacSecret := PacSecret{ + Cluster: w.cluster, + Secret: sc, + } + secretsChan <- pacSecret + } + + case <-ctx.Done(): + w.logger.Warn("Secret polling stopped by context cancellation.") + return + } + w.startup = false + } + }() + + return secretsChan, nil +} diff --git a/pkg/services/secret_poller_test.go b/pkg/services/secret_poller_test.go new file mode 100644 index 0000000..82d6889 --- /dev/null +++ b/pkg/services/secret_poller_test.go @@ -0,0 +1,94 @@ +package services + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "go.uber.org/zap" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes/fake" +) + +func TestSecretPoller_PollSecrets(t *testing.T) { + cluster := "test-cluster" + mockKubeClient := fake.NewSimpleClientset() + logger, _ := zap.NewProduction() + sugaredLogger := logger.Sugar() + + secretPoller := &SecretPoller{ + cluster: cluster, + kubeClient: mockKubeClient, + logger: sugaredLogger, + startup: true, + } + + opts := metav1.ListOptions{} + pollInterval := time.Second * 5 + + ctx := context.Background() + + secretsChan, err := secretPoller.PollSecrets(ctx, opts, pollInterval) + assert.NoError(t, err, "PollSecrets should not return an error") + + // Create test secrets + testSecrets := []*v1.Secret{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "secret1", + Namespace: "default", + }, + Data: map[string][]byte{ + "key1": []byte("value1"), + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "secret2", + Namespace: "default", + }, + Data: map[string][]byte{ + "key2": []byte("value2"), + }, + }, + } + + // Add test secrets to the fake clientset + for _, secret := range testSecrets { + _, err := mockKubeClient.CoreV1().Secrets(secret.Namespace).Create(ctx, secret, metav1.CreateOptions{}) + assert.NoError(t, err, "Failed to create test secret") + } + + receivedSecrets := make(map[string]bool) + time.Sleep(2 * time.Second) + + // Verify that the secrets are received on the channel + go func() { + for secret := range secretsChan { + receivedSecrets[secret.Secret.Name] = true + } + }() + + // Wait for the secrets to be received + for { + if len(receivedSecrets) == len(testSecrets) { + break + } + time.Sleep(1 * time.Second) + } + + // Verify that all test secrets are received + for _, secret := range testSecrets { + assert.True(t, receivedSecrets[secret.Name], "Test secret not received") + } + + ctx.Done() +} + +// GroupVersionKind is a mock method for testing purposes +func (ps *PacSecret) GroupVersionKind() schema.GroupVersionKind { + return schema.GroupVersionKind{} +} diff --git a/pkg/services/secret_synchronizer.go b/pkg/services/secret_synchronizer.go new file mode 100644 index 0000000..98d0225 --- /dev/null +++ b/pkg/services/secret_synchronizer.go @@ -0,0 +1,356 @@ +package services + +import ( + "context" + "strings" + "time" + + "go.uber.org/zap" + "k8s.io/apimachinery/pkg/api/errors" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// A struct that can synchronize secrets in a Kubernetes cluster. +type SecretSynchronizer struct { + localKubeClient IKubeClient + remoteKubeClients map[string]IKubeClient + secretPooller *SecretPoller + namespacePoller *NamespacePoller + logger *zap.SugaredLogger + Secrets map[string]*PacSecret +} + +func NewSecretSynchronizer( + localKubeClient IKubeClient, + remoteKubeClients map[string]IKubeClient, + secretPooller *SecretPoller, + namespacePoller *NamespacePoller, + logger *zap.SugaredLogger, +) *SecretSynchronizer { + return &SecretSynchronizer{ + localKubeClient: localKubeClient, + remoteKubeClients: remoteKubeClients, + secretPooller: secretPooller, + namespacePoller: namespacePoller, + logger: logger, + Secrets: make(map[string]*PacSecret), + } +} + +// Start the secret synchronizer. +func (s *SecretSynchronizer) Start(ctx context.Context, pollInterval time.Duration, housekeepingInterval time.Duration) error { + err := s.startSyncyng(ctx, pollInterval) + if err != nil { + return err + } + + err = s.deleteOrphans(ctx, housekeepingInterval) + if err != nil { + return err + } + + return nil +} + +// Delete orphan secrets in the local Kubernetes cluster. +func (s *SecretSynchronizer) deleteOrphans(ctx context.Context, pollInterval time.Duration) error { + secretsChan, err := s.secretPooller.PollSecrets(ctx, v1.ListOptions{ + LabelSelector: ManagedLabelSelector, + }, pollInterval) + + if err != nil { + s.logger.Error("Failed to list managed secrets: ", err) + return err + } + + go func() { + for { + select { + case secret, ok := <-secretsChan: + if !ok { + // Channel closed, stop the goroutine + return + } + + // Process the secret + sourceCluster := secret.Secret.Annotations[SourceClusterAnnotation] + sourceNamespace := secret.Secret.Annotations[SourceNamespaceAnnotation] + + var isLocal bool = false + var remoteKubeClient IKubeClient + if sourceCluster == secret.Cluster { + remoteKubeClient = s.localKubeClient + isLocal = true + } else { + + if _, ok := s.remoteKubeClients[sourceCluster]; !ok { + s.logger.Error("Remote client not found: ", sourceCluster) + continue + } + + remoteKubeClient = s.remoteKubeClients[sourceCluster] + } + + // Check if the secret is orphan + remoteSecret, err := remoteKubeClient.CoreV1().Secrets(sourceNamespace).Get(ctx, secret.Secret.Name, v1.GetOptions{}) + + // Delete the orphan secret + if errors.IsNotFound(err) { + err := s.localKubeClient.CoreV1().Secrets(secret.Secret.Namespace).Delete(ctx, secret.Secret.Name, v1.DeleteOptions{}) + if err == nil { + s.logger.Infof("Orphan secret %s deleted on cluster %s in namespace %s", secret.Secret.Name, secret.Cluster, secret.Secret.Namespace) + } else { + s.logger.Errorf("Failed to delete orphan secret %s deleted on cluster %s in namespace %s: %s", secret.Secret.Name, secret.Cluster, secret.Secret.Namespace, err) + } + } + + if remoteSecret.Labels[LabelSelector] == "cluster" && isLocal { + err := s.localKubeClient.CoreV1().Secrets(secret.Secret.Namespace).Delete(ctx, secret.Secret.Name, v1.DeleteOptions{}) + if err == nil { + s.logger.Infof("Orphan secret %s deleted on cluster %s in namespace %s", secret.Secret.Name, secret.Cluster, secret.Secret.Namespace) + } else { + s.logger.Errorf("Failed to delete orphan secret %s deleted on cluster %s in namespace %s: %s", secret.Secret.Name, secret.Cluster, secret.Secret.Namespace, err) + } + } + + // Check if the label and annotations of the remote secret means that this secret should exists, otherwise delete this secret + keep := false + if remoteSecret.Labels[LabelSelector] == "namespace" { + if remoteSecret.Annotations[NamespaceLabelAnnotation] != "" { + keyValue := splitAndTrim(remoteSecret.Annotations[NamespaceLabelAnnotation], "=") + key := keyValue[0] + value := strings.Trim(keyValue[1], "\"") + + namespace, err := s.localKubeClient.CoreV1().Namespaces().Get(ctx, secret.Secret.Namespace, v1.GetOptions{}) + if err != nil { + s.logger.Error("Failed to get namespace: ", err) + continue + } + if namespace.Labels[key] == value { + keep = true + } + } + + if remoteSecret.Annotations[NamespaceNameAnnotation] != "" { + if remoteSecret.Annotations[NamespaceNameAnnotation] == "all" { + keep = true + } else { + namespaces := splitAndTrim(remoteSecret.Annotations[NamespaceNameAnnotation], ",") + for _, namespace := range namespaces { + if namespace == secret.Secret.Namespace { + keep = true + break + } + } + } + } + } + + if remoteSecret.Labels[LabelSelector] == "cluster" { + destinationClusters := splitAndTrim(remoteSecret.Annotations[ClusterAnnotation], ",") + for _, cluster := range destinationClusters { + if cluster == secret.Cluster { + keep = true + } + } + } + + if !keep { + err := s.localKubeClient.CoreV1().Secrets(secret.Secret.Namespace).Delete(ctx, secret.Secret.Name, v1.DeleteOptions{}) + if err == nil { + s.logger.Infof("Orphan secret %s deleted on cluster %s in namespace %s", secret.Secret.Name, secret.Cluster, secret.Secret.Namespace) + } else { + s.logger.Errorf("Failed to delete orphan secret %s deleted on cluster %s in namespace %s: %s", secret.Secret.Name, secret.Cluster, secret.Secret.Namespace, err) + } + } + + case <-ctx.Done(): + s.logger.Warn("Secret synchronization stopped by context cancellation.") + return + } + } + }() + + return nil +} + +// Synchronize secrets in Kubernetes clusters. +func (s *SecretSynchronizer) startSyncyng(ctx context.Context, pollInterval time.Duration) error { + s.Secrets = make(map[string]*PacSecret) + + secretsChan, err := s.secretPooller.PollSecrets(ctx, v1.ListOptions{ + LabelSelector: LabelSelector, + }, pollInterval) + + if err != nil { + s.logger.Error("Failed to list secrets: ", err) + return err + } + + go func() { + for { + select { + case secret, ok := <-secretsChan: + if !ok { + // Channel closed, stop the goroutine + return + } + + s.sync(ctx, secret) + + case <-ctx.Done(): + s.logger.Warn("Secret synchronization stopped by context cancellation.") + return + } + } + }() + + return nil +} + +// Synchronize the secret in Kubernetes clusters. +func (s *SecretSynchronizer) sync(ctx context.Context, pacSecret PacSecret) error { + + if pacSecret.Secret.Labels[LabelSelector] == "namespace" { + return s.syncLocal(ctx, pacSecret) + } + + if pacSecret.Secret.Labels[LabelSelector] == "cluster" { + return s.syncRemote(ctx, pacSecret) + } + + return nil +} + +// Synchronize the secret in the local Kubernetes cluster. +func (s *SecretSynchronizer) syncLocal(ctx context.Context, pacSecret PacSecret) error { + // Synchronize based on the namespace label + if namespaceLabelAnnotationValue, ok := pacSecret.Secret.Annotations[NamespaceLabelAnnotation]; ok { + keyValue := splitAndTrim(namespaceLabelAnnotationValue, "=") + key := keyValue[0] + value := strings.Trim(keyValue[1], "\"") + + for _, namespace := range s.namespacePoller.Namespaces { + + // Skip the source namespace + if namespace.Namespace.Name == pacSecret.Secret.Namespace { + s.logger.Debugf("Skipping source namespace: %s when synchronizing secret: %s", namespace.Namespace.Name, pacSecret.Secret.Name) + continue + } + + // Synchronize the secret if the namespace has the label + if labelValue, ok := namespace.Namespace.Labels[key]; ok && labelValue == value { + s.createOrUpdate(ctx, s.localKubeClient, pacSecret, pacSecret.Cluster, namespace.Namespace.Name) + } + } + + return nil + } + + // Synchronize based on the namespace name + if namespaceNameAnnotationValue, ok := pacSecret.Secret.Annotations[NamespaceNameAnnotation]; ok && namespaceNameAnnotationValue != "all" { + namespaces := splitAndTrim(namespaceNameAnnotationValue, ",") + for _, namespace := range namespaces { + _, ok := s.namespacePoller.Namespaces[namespace] + if !ok { + s.logger.Warn("Namespace not found: ", namespace) + continue + } + + // Skip the source namespace + if pacSecret.Secret.Namespace == namespace { + s.logger.Debugf("Skipping source namespace: %s when synchronizing secret: %s", namespace, pacSecret.Secret.Name) + continue + } + + s.createOrUpdate(ctx, s.localKubeClient, pacSecret, pacSecret.Cluster, namespace) + } + return nil + } + + // Synchronize all namespaces + if namespaceNameAnnotationValue, ok := pacSecret.Secret.Annotations[NamespaceNameAnnotation]; ok && namespaceNameAnnotationValue == "all" { + namespaces := s.namespacePoller.Namespaces + + for _, namespace := range namespaces { + + // Skip the source namespace + if pacSecret.Secret.Namespace == namespace.Namespace.Name { + s.logger.Debugf("Skipping source namespace: %s when synchronizing secret: %s", namespace.Namespace.Name, pacSecret.Secret.Name) + continue + } + + s.createOrUpdate(ctx, s.localKubeClient, pacSecret, pacSecret.Cluster, namespace.Namespace.Name) + } + return nil + } + + return nil +} + +// Synchronize the secret in the remote Kubernetes clusters. +func (s *SecretSynchronizer) syncRemote(ctx context.Context, pacSecret PacSecret) error { + clusters := splitAndTrim(pacSecret.Secret.Annotations[ClusterAnnotation], ",") + + for _, cluster := range clusters { + client, ok := s.remoteKubeClients[cluster] + + if !ok { + s.logger.Error("Remote client not found: ", cluster) + continue + } + + // Skip the source cluster. + if pacSecret.Cluster == cluster { + s.logger.Debugf("Skipping source cluster: %s when synchronizing secret: %s", cluster, pacSecret.Secret.Name) + continue + } + + s.createOrUpdate(ctx, client, pacSecret, cluster, pacSecret.Secret.Namespace) + } + + return nil +} + +// Create or update the secret in the Kubernetes cluster. +func (s *SecretSynchronizer) createOrUpdate(ctx context.Context, client IKubeClient, pacSecret PacSecret, cluster string, namespace string) error { + gotSecret, err := client.CoreV1().Secrets(namespace).Get(ctx, pacSecret.Secret.Name, v1.GetOptions{}) + + if err != nil { + + if errors.IsNotFound(err) { + newSecret := pacSecret.Prepare(namespace) + _, err := client.CoreV1().Secrets(namespace).Create(ctx, &newSecret, v1.CreateOptions{}) + + if err == nil { + s.logger.Infof("Secret %s created on cluster %s in namespace %s", pacSecret.Secret.Name, cluster, namespace) + } else { + s.logger.Errorf("Failed to create secret %s on cluster %s in namespace %s: %s", pacSecret.Secret.Name, cluster, namespace, err) + } + + return err + } else { + s.logger.Error("Failed to get secret: ", err) + } + + return err + } + + // Update the secret if it has changed + if pacSecret.HasChanged(*gotSecret) { + updatedSecret := pacSecret.Prepare(namespace) + _, err := client.CoreV1().Secrets(namespace).Update(ctx, &updatedSecret, v1.UpdateOptions{}) + + if err == nil { + s.logger.Infof("Secret %s updated on cluster %s in namespace %s", pacSecret.Secret.Name, cluster, namespace) + } else { + s.logger.Errorf("Failed to update secret %s on cluster %s in namespace %s: %s", pacSecret.Secret.Name, cluster, namespace, err) + } + + return err + } else { + s.logger.Debugf("Secret %s is up do date on cluster %s in namespace %s", pacSecret.Secret.Name, cluster, namespace) + } + + return err +} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ec3620a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +kubernetes>=12.0.0 +deepdiff==6.7.1 diff --git a/test.py b/test.py new file mode 100644 index 0000000..322a574 --- /dev/null +++ b/test.py @@ -0,0 +1,745 @@ +from kubernetes import client, config +from deepdiff import DeepDiff +import time +import json + +# ANSI color codes for colored logging +GREEN = '\033[92m' +RED = '\033[91m' +RESET = '\033[0m' +WAIT_TIME = 30 + +def log_info(message): + print(f"[INFO] {message}") + +def log_success(message): + print(f"{GREEN}[SUCCESS] {message}{RESET}") + +def log_error(message): + print(f"{RED}[ERROR] {message}{RESET}") + +def delete_resource_type(core_api, namespace, resource_name, resource_type='secret'): + # Delete resources in the source namespace + try: + if resource_type == "secret": + core_api.delete_namespaced_secret(resource_name, namespace) + elif resource_type == "configmap": + core_api.delete_namespaced_config_map(resource_name, namespace) + else: + log_error(f"Resource type '{resource_type}' passed isn't valid.") + return + log_info(f"Deleted {resource_type} '{resource_name}' in namespace '{namespace}'.") + except client.exceptions.ApiException as e: + if e.status != 404: # Ignore not found errors + log_error(f"Error deleting {resource_type} '{resource_name}' in namespace '{namespace}': {e}") + +def delete_namespace(core_api, namespace, cluster): + """ + Delete a Kubernetes namespace. + :param core_api: CoreV1Api instance for Kubernetes API interaction. + :param namespace: The name of the namespace to be deleted. + :param cluster: The name of the cluster that will be added in the log. + """ + try: + core_api.delete_namespace(namespace) + log_info(f"Namespace '{namespace}' deleted in {cluster} cluster.") + time.sleep(3) # Give it some time for the namespace to be deleted + except client.exceptions.ApiException as e: + if e.status != 404: # Ignore not found errors + log_error(f"Error deleting namespace '{namespace}': {e} in {cluster} cluster") + +def create_empty_namespace(core_api, namespace, cluster): + """ + Create a Kubernetes namespace. + :param core_api: CoreV1Api instance for Kubernetes API interaction. + :param namespace: The name of the namespace to be created. + :param cluster: The name of the cluster that will be added in the log. + """ + try: + core_api.read_namespace(name=namespace) + log_info(f"Namespace '{namespace}' already exists in {cluster} cluster.") + except client.exceptions.ApiException as e: + if e.status == 404: + core_api.create_namespace(client.V1Namespace(metadata=client.V1ObjectMeta(name=namespace))) + log_info(f"Namespace '{namespace}' created in {cluster} cluster.") + time.sleep(2) # Give it some time for the namespace to be fully set up + + +def get_k8s_client_for_cluster(context_name): + """ + Create a Kubernetes client configured for a specific cluster using a context name. + :param context_name: The name of the context for the target cluster in your kubeconfig. + :return: A CoreV1Api instance connected to the target cluster. + """ + # Load the kubeconfig file and set the context + config.load_kube_config(context=context_name) + + # Create and return the CoreV1Api client instance + return client.CoreV1Api() + +def cleanup_resources(source_core_api, target_core_api, source_namespace, destination_namespaces): + """ + Clean up the source namespace, destination namespaces, and specific resources before and after tests. + :param source_core_api: CoreV1Api instance for source cluster Kubernetes API interaction. + :param target_core_api: CoreV1Api instance for target cluster Kubernetes API interaction. + :param source_namespace: The source namespace from which to delete the secret and configmap. + :param secret_name: The name of the secret to delete. + :param configmap_name: The name of the configmap to delete. + :param destination_namespaces: A list of destination namespaces to clean up. + """ + # Attempt to delete the source namespace in source cluster + delete_namespace(source_core_api,source_namespace,'source') + + # Attempt to delete the source namespace in target cluster + delete_namespace(target_core_api,source_namespace,'target') + + # Clean up destination namespaces in source cluster + for ns in destination_namespaces: + delete_namespace(source_core_api,ns,'source') + time.sleep(2) + +def setup_resources_for_test(source_core_api, target_core_api, source_namespace, secret_name, configmap_name, destination_namespaces, label_selector=None): + """ + Create a namespace (if it doesn't exist), a secret, and a configmap for testing, along with destination namespaces. + :param source_core_api: CoreV1Api instance for source cluster Kubernetes API interaction. + :param target_core_api: CoreV1Api instance for target cluster Kubernetes API interaction. + :param source_namespace: The namespace in which to create the source secret and configmap. + :param secret_name: The name of the secret to create. + :param configmap_name: The name of the configmap to create. + :param destination_namespaces: A list of destination namespaces to create for testing synchronization. + """ + # Create source namespace if it doesn't exist in both clusters + create_empty_namespace(source_core_api,source_namespace,'source') + create_empty_namespace(target_core_api,source_namespace,'target') + + # Create destination namespaces + labels = {} + if label_selector: + key, value = label_selector.split("=") + labels[key] = value + + for dest_namespace in destination_namespaces: + try: + ns = source_core_api.read_namespace(name=dest_namespace) + if label_selector and labels.items() <= ns.metadata.labels.items(): + log_info(f"Destination namespace '{dest_namespace}' with label '{label_selector}' already exists.") + else: + log_info(f"Updating labels for namespace '{dest_namespace}'.") + # Update namespace labels if needed + except client.exceptions.ApiException as e: + if e.status == 404: + # Create namespace with labels if it does not exist + source_core_api.create_namespace(client.V1Namespace( + metadata=client.V1ObjectMeta(name=dest_namespace, labels=labels))) + log_info(f"Created destination namespace '{dest_namespace}' with label '{label_selector}'.") + time.sleep(2) # Give it some time for the namespace to be fully set up + else: + raise + + # Create secret in source namespace + secret_body = client.V1Secret( + metadata=client.V1ObjectMeta(name=secret_name, namespace=source_namespace), + data={"key": "c2VjcmV0VmFsdWU="} # Example: base64 encoded "secretValue" + ) + try: + source_core_api.create_namespaced_secret(source_namespace, secret_body) + log_info(f"Created secret '{secret_name}' in source namespace '{source_namespace}'.") + except client.exceptions.ApiException as e: + log_error(f"Failed to create secret '{secret_name}' in source namespace '{source_namespace}': {e}") + + # Create configmap in source namespace + configmap_body = client.V1ConfigMap( + metadata=client.V1ObjectMeta(name=configmap_name, namespace=source_namespace), + data={"config": "configValue"} # Example configuration data + ) + try: + source_core_api.create_namespaced_config_map(source_namespace, configmap_body) + log_info(f"Created ConfigMap '{configmap_name}' in source namespace '{source_namespace}'.") + except client.exceptions.ApiException as e: + log_error(f"Failed to create ConfigMap '{configmap_name}' in source namespace '{source_namespace}': {e}") + +def get_all_resources_type(core_api, resource_type='secret'): + """ + Get a list of all resoruces of specified kind. + :param core_api: CoreV1Api instance for Kubernetes API interaction. + :param resource_type: Type of resource that will be returned. + :return List of all the resources of Kind specified in the cluster. + """ + try: + if resource_type == "secret": + response = core_api.list_secret_for_all_namespaces() + elif resource_type == "configmap": + response = core_api.list_config_map_for_all_namespaces() + else: + log_error(f"Resource type '{resource_type}' passed isn't valid.") + return + result = {} + # filter fields considered for diff + for i in response.items: + result[f"{i.metadata.namespace}/{i.metadata.name}"] = { + 'data': i.data, + 'metadata': { + 'annotations': i.metadata.annotations, + 'creation_timestamp': i.metadata.creation_timestamp, + 'labels': i.metadata.labels, + 'name': i.metadata.name, + 'namespace': i.metadata.namespace, + 'uid': i.metadata.uid, + } + } + return result + except client.exceptions.ApiException as e: + log_error(f"Failed to get all resources of kind '{resource_type}': {e}") + +def compare_json(json1, json2): + return DeepDiff(json.loads(json1),json.loads(json2)) + +def apply_labels_and_annotations(core_api, namespace, resource_name, labels, annotations, resource_type='secret'): + """ + Apply labels and annotations to a resource. + """ + metadata = client.V1ObjectMeta(labels=labels, annotations=annotations) + if resource_type == 'secret': + body = client.V1Secret(metadata=metadata) + core_api.patch_namespaced_secret(resource_name, namespace, body) + elif resource_type == 'configmap': + body = client.V1ConfigMap(metadata=metadata) + core_api.patch_namespaced_config_map(resource_name, namespace, body) + log_info(f"Applied labels and annotations to {resource_type} '{resource_name}' in namespace '{namespace}'.") + +def verify_resource_in_namespace(source_core_api, target_core_api, namespace, resource_name, resource_type, source_cluster_name, source_namespace): + """ + Verify if a resource is present in a given namespace and check for specific annotations. + :param core_api: CoreV1Api instance for Kubernetes API interaction. + :param namespace: The namespace where the resource will be checked. + :param resource_name: The name of the resource to verify. + :param resource_type: The type of the resource ('secret' or 'configmap'). + :param source_cluster_name: The name of the source cluster where the resource originated. + :param source_namespace: The name of the source namespace where the resource originated. + """ + try: + if resource_type == 'secret': + resource = target_core_api.read_namespaced_secret(resource_name, namespace) + elif resource_type == 'configmap': + resource = target_core_api.read_namespaced_config_map(resource_name, namespace) + + annotations = resource.metadata.annotations + expected_annotations = { + "keess.powerhrg.com/source-cluster": source_cluster_name, + "keess.powerhrg.com/source-namespace": source_namespace, + # The source-resource-version will be checked separately for equality + } + + for key, expected_value in expected_annotations.items(): + if annotations.get(key) != expected_value: + log_error(f"{resource_type.capitalize()} '{resource_name}' in namespace '{namespace}' does not have the correct annotation '{key}': expected '{expected_value}', found '{annotations.get(key)}'") + return False + + # Check source resource version + target_resource_version = annotations.get("keess.powerhrg.com/source-resource-version") + if resource_type == 'secret': + source_resource = source_core_api.read_namespaced_secret(resource_name, source_namespace) + elif resource_type == 'configmap': + source_resource = source_core_api.read_namespaced_config_map(resource_name, source_namespace) + + if source_resource.metadata.resource_version != target_resource_version: + log_error(f"{resource_type.capitalize()} '{resource_name}' in namespace '{namespace}' has mismatched source resource version: expected '{source_resource.metadata.resource_version}', found '{target_resource_version}'") + return False + + log_success(f"{resource_type.capitalize()} '{resource_name}' in namespace '{namespace}' has been verified successfully with all annotations.") + return True + + except client.exceptions.ApiException as e: + log_error(f"Failed to find {resource_type} '{resource_name}' in namespace '{namespace}': {e}") + return False + + +def test_scenario_1(core_api, source_cluster_name, namespace, secret_name, configmap_name, target_namespaces): + """ + Test secret and configmap synchronization to two specific namespaces in the same cluster. + :param core_api: CoreV1Api instance for Kubernetes API interaction. + :param namespace: The source namespace where the secret and configmap are initially created. + :param secret_name: The name of the secret to synchronize. + :param configmap_name: The name of the configmap to synchronize. + :param target_namespaces: A list of namespaces to which the resources will be synchronized. + """ + log_info("Test scenario 1 running...") + # Setup labels and annotations for synchronization + labels = {"keess.powerhrg.com/sync": "namespace"} + annotations = {"keess.powerhrg.com/namespaces-names": ",".join(target_namespaces)} + + # Get all secrets and configmaps before test + initial_all_secrets = get_all_resources_type(core_api, "secret") + initial_all_configmaps = get_all_resources_type(core_api, "configmap") + + # Apply labels and annotations to the secret and configmap + apply_labels_and_annotations(core_api, namespace, secret_name, labels, annotations, 'secret') + apply_labels_and_annotations(core_api, namespace, configmap_name, labels, annotations, 'configmap') + + log_info("Waiting for synchronization to complete...") + time.sleep(WAIT_TIME) # Adjust this delay as necessary for your environment + + # Verify the presence of the secret and configmap in the target namespaces + for target_namespace in target_namespaces: + verify_resource_in_namespace(core_api, core_api, target_namespace, secret_name, 'secret', source_cluster_name, namespace) + verify_resource_in_namespace(core_api, core_api, target_namespace, configmap_name, 'configmap', source_cluster_name, namespace) + + # Compare all secrets and configmaps after test + all_secrets = get_all_resources_type(core_api, "secret") + all_configmaps = get_all_resources_type(core_api, "configmap") + ddiff_secrets = DeepDiff(initial_all_secrets, all_secrets).to_json() + ddiff_configmaps = DeepDiff(initial_all_configmaps, all_configmaps).to_json() + + expected_secrets_result = json.dumps({ + "type_changes": { + "root['test-namespace/new-test-secret']['metadata']['annotations']": { + "old_type": "NoneType", + "new_type": "dict", + "old_value": None, + "new_value": { + "keess.powerhrg.com/namespaces-names": "test-namespace-dest-1,test-namespace-dest-2" + }, + }, + "root['test-namespace/new-test-secret']['metadata']['labels']": { + "old_type": "NoneType", + "new_type": "dict", + "old_value": None, + "new_value": {"keess.powerhrg.com/sync": "namespace"}, + }, + }, + "dictionary_item_added": [ + "root['test-namespace-dest-1/new-test-secret']", + "root['test-namespace-dest-2/new-test-secret']", + ], + }) + + expected_configmaps_result = json.dumps({ + "type_changes": { + "root['test-namespace/new-test-configmap']['metadata']['annotations']": { + "old_type": "NoneType", + "new_type": "dict", + "old_value": None, + "new_value": { + "keess.powerhrg.com/namespaces-names": "test-namespace-dest-1,test-namespace-dest-2" + }, + }, + "root['test-namespace/new-test-configmap']['metadata']['labels']": { + "old_type": "NoneType", + "new_type": "dict", + "old_value": None, + "new_value": {"keess.powerhrg.com/sync": "namespace"}, + }, + }, + "dictionary_item_added": [ + "root['test-namespace-dest-1/new-test-configmap']", + "root['test-namespace-dest-2/new-test-configmap']", + ], + }) + + diff_expected_secrets = compare_json(expected_secrets_result,ddiff_secrets) + if diff_expected_secrets != {}: + log_info(f"ddiff_secrets: \n{ddiff_secrets}") + log_error(f"There were unexpected changes in secrets: \n{diff_expected_secrets}") + + diff_expected_configmaps = compare_json(expected_configmaps_result,ddiff_configmaps) + if diff_expected_configmaps != {}: + log_info(f"ddiff_configmaps: \n{ddiff_configmaps}") + log_error(f"There were unexpected changes in configmaps: \n{diff_expected_configmaps}") + + log_info("Test scenario 1 completed.") + +def test_scenario_2(core_api, namespace, secret_name, configmap_name): + """ + Delete origin secret and configmap, it expects replicas to be deleted. + :param core_api: CoreV1Api instance for Kubernetes API interaction. + :param namespace: The source namespace where the secret and configmap are initially created. + :param secret_name: The name of the secret to synchronize. + :param configmap_name: The name of the configmap to synchronize. + """ + log_info("Test scenario 2 running...") + + # Get all secrets and configmaps before test + initial_all_secrets = get_all_resources_type(core_api, "secret") + initial_all_configmaps = get_all_resources_type(core_api, "configmap") + + # Delete origin secret and configmap + delete_resource_type(core_api, namespace, secret_name, 'secret') + delete_resource_type(core_api, namespace, configmap_name, 'configmap') + + log_info("Waiting for synchronization to complete...") + time.sleep(WAIT_TIME) # Adjust this delay as necessary for your environment + + # Compare all secrets and configmaps after test + all_secrets = get_all_resources_type(core_api, "secret") + all_configmaps = get_all_resources_type(core_api, "configmap") + ddiff_secrets = DeepDiff(initial_all_secrets, all_secrets).to_json() + ddiff_configmaps = DeepDiff(initial_all_configmaps, all_configmaps).to_json() + + expected_secrets_result = json.dumps({ + "dictionary_item_removed": [ + "root['test-namespace-dest-1/new-test-secret']", + "root['test-namespace-dest-2/new-test-secret']", + "root['test-namespace/new-test-secret']", + ] + }) + + expected_configmaps_result = json.dumps({ + "dictionary_item_removed": [ + "root['test-namespace-dest-1/new-test-configmap']", + "root['test-namespace-dest-2/new-test-configmap']", + "root['test-namespace/new-test-configmap']", + ] + }) + + diff_expected_secrets = compare_json(expected_secrets_result,ddiff_secrets) + if diff_expected_secrets != {}: + log_info(f"ddiff_secrets: \n{ddiff_secrets}") + log_error(f"There were unexpected changes in secrets: \n{diff_expected_secrets}") + else: + log_success(f"The deletion of the origin secret '{secret_name}' in namespace '{namespace}' triggered a deletion of its copies.") + + diff_expected_configmaps = compare_json(expected_configmaps_result,ddiff_configmaps) + if diff_expected_configmaps != {}: + log_info(f"ddiff_configmaps: \n{ddiff_configmaps}") + log_error(f"There were unexpected changes in configmaps: \n{diff_expected_configmaps}") + else: + log_success(f"The deletion of the origin configmap '{configmap_name}' in namespace '{namespace}' triggered a deletion of its copies.") + + log_info("Test scenario 2 completed.") + +def test_scenario_3(core_api, source_cluster_name, source_namespace, secret_name, configmap_name, label_selector): + """ + Test synchronization to namespaces matching a specific label, including checks for source cluster and namespace. + :param core_api: CoreV1Api instance for Kubernetes API interaction. + :param source_namespace: The source namespace where resources are created. + :param secret_name: Name of the secret to synchronize. + :param configmap_name: Name of the configmap to synchronize. + :param source_cluster_name: Name of the source cluster for annotation verification. + """ + log_info("Test scenario 3 running...") + + namespaces = core_api.list_namespace(label_selector=label_selector) + target_namespaces = [ns.metadata.name for ns in namespaces.items if ns.metadata.name != source_namespace] + + # Get all secrets and configmaps before test + initial_all_secrets = get_all_resources_type(core_api, "secret") + initial_all_configmaps = get_all_resources_type(core_api, "configmap") + + # Apply labels and annotations for synchronization + labels = {"keess.powerhrg.com/sync": "namespace"} + annotations = { + "keess.powerhrg.com/namespace-label": label_selector, + # Include source cluster and namespace annotations directly if needed for setup + } + apply_labels_and_annotations(core_api, source_namespace, secret_name, labels, annotations, 'secret') + apply_labels_and_annotations(core_api, source_namespace, configmap_name, labels, annotations, 'configmap') + + log_info("Waiting for synchronization to matching namespaces...") + time.sleep(WAIT_TIME) # Adjust based on expected synchronization time + + # Verification + for ns in target_namespaces: + verify_resource_in_namespace(core_api, core_api, ns, secret_name, 'secret', source_cluster_name, source_namespace) + verify_resource_in_namespace(core_api, core_api, ns, configmap_name, 'configmap', source_cluster_name, source_namespace) + + # Compare all secrets and configmaps after test + all_secrets = get_all_resources_type(core_api, "secret") + all_configmaps = get_all_resources_type(core_api, "configmap") + ddiff_secrets = DeepDiff(initial_all_secrets, all_secrets).to_json() + ddiff_configmaps = DeepDiff(initial_all_configmaps, all_configmaps).to_json() + + expected_secrets_result = json.dumps({ + "type_changes": { + "root['test-namespace/new-test-secret']['metadata']['annotations']": { + "old_type": "NoneType", + "new_type": "dict", + "old_value": None, + "new_value": { + "keess.powerhrg.com/namespace-label": "keess.powerhrg.com/testing=yes" + }, + }, + "root['test-namespace/new-test-secret']['metadata']['labels']": { + "old_type": "NoneType", + "new_type": "dict", + "old_value": None, + "new_value": {"keess.powerhrg.com/sync": "namespace"}, + }, + }, + "dictionary_item_added": [ + "root['test-namespace-dest-1/new-test-secret']", + "root['test-namespace-dest-2/new-test-secret']", + ], + }) + + expected_configmaps_result = json.dumps({ + "type_changes": { + "root['test-namespace/new-test-configmap']['metadata']['annotations']": { + "old_type": "NoneType", + "new_type": "dict", + "old_value": None, + "new_value": { + "keess.powerhrg.com/namespace-label": "keess.powerhrg.com/testing=yes" + }, + }, + "root['test-namespace/new-test-configmap']['metadata']['labels']": { + "old_type": "NoneType", + "new_type": "dict", + "old_value": None, + "new_value": {"keess.powerhrg.com/sync": "namespace"}, + }, + }, + "dictionary_item_added": [ + "root['test-namespace-dest-1/new-test-configmap']", + "root['test-namespace-dest-2/new-test-configmap']", + ], + }) + + diff_expected_secrets = compare_json(expected_secrets_result,ddiff_secrets) + if diff_expected_secrets != {}: + log_info(f"ddiff_secrets: \n{ddiff_secrets}") + log_error(f"There were unexpected changes in secrets: \n{diff_expected_secrets}") + + diff_expected_configmaps = compare_json(expected_configmaps_result,ddiff_configmaps) + if diff_expected_configmaps != {}: + log_info(f"ddiff_configmaps: \n{ddiff_configmaps}") + log_error(f"There were unexpected changes in configmaps: \n{diff_expected_configmaps}") + + log_info("Test scenario 3 completed.") + +def test_scenario_4(core_api, namespace, secret_name, configmap_name): + """ + Remove annotation and labels from origin secret and configmap, it expects replicas to be deleted. + :param core_api: CoreV1Api instance for Kubernetes API interaction. + :param namespace: The source namespace where the secret and configmap are initially created. + :param secret_name: The name of the secret to synchronize. + :param configmap_name: The name of the configmap to synchronize. + """ + log_info("Test scenario 4 running...") + + # Setup labels and annotations for synchronization + labels = {"keess.powerhrg.com/sync": None} + annotations = {"keess.powerhrg.com/namespace-label": None} + + # Get all secrets and configmaps before test + initial_all_secrets = get_all_resources_type(core_api, "secret") + initial_all_configmaps = get_all_resources_type(core_api, "configmap") + + # Remove labels and annotations to the secret and configmap + apply_labels_and_annotations(core_api, namespace, secret_name, labels, annotations, 'secret') + apply_labels_and_annotations(core_api, namespace, configmap_name, labels, annotations, 'configmap') + + log_info("Waiting for synchronization to complete...") + time.sleep(WAIT_TIME) # Adjust this delay as necessary for your environment + + # Compare all secrets and configmaps after test + all_secrets = get_all_resources_type(core_api, "secret") + all_configmaps = get_all_resources_type(core_api, "configmap") + ddiff_secrets = DeepDiff(initial_all_secrets, all_secrets).to_json() + ddiff_configmaps = DeepDiff(initial_all_configmaps, all_configmaps).to_json() + + expected_secrets_result = json.dumps({ + "dictionary_item_removed": [ + "root['test-namespace-dest-1/new-test-secret']", + "root['test-namespace-dest-2/new-test-secret']", + ], + "type_changes": { + "root['test-namespace/new-test-secret']['metadata']['annotations']": { + "old_type": "dict", + "new_type": "NoneType", + "old_value": {"keess.powerhrg.com/namespace-label": "keess.powerhrg.com/testing=yes"}, + "new_value": None, + }, + "root['test-namespace/new-test-secret']['metadata']['labels']":{ + "old_type": "dict", + "new_type": "NoneType", + "old_value": {"keess.powerhrg.com/sync": "namespace"}, + "new_value": None, + }, + }, + }) + + expected_configmaps_result = json.dumps({ + "dictionary_item_removed": [ + "root['test-namespace-dest-1/new-test-configmap']", + "root['test-namespace-dest-2/new-test-configmap']", + ], + "type_changes": { + "root['test-namespace/new-test-configmap']['metadata']['annotations']": { + "old_type": "dict", + "new_type": "NoneType", + "old_value": {"keess.powerhrg.com/namespace-label": "keess.powerhrg.com/testing=yes"}, + "new_value": None, + }, + "root['test-namespace/new-test-configmap']['metadata']['labels']":{ + "old_type": "dict", + "new_type": "NoneType", + "old_value": {"keess.powerhrg.com/sync": "namespace"}, + "new_value": None, + }, + }, + }) + + diff_expected_secrets = compare_json(expected_secrets_result,ddiff_secrets) + if diff_expected_secrets != {}: + log_info(f"ddiff_secrets: \n{ddiff_secrets}") + log_error(f"There were unexpected changes in secrets: \n{diff_expected_secrets}") + else: + log_success(f"The deletion of the origin secret '{secret_name}' in namespace '{namespace}' triggered a deletion of its copies.") + + diff_expected_configmaps = compare_json(expected_configmaps_result,ddiff_configmaps) + if diff_expected_configmaps != {}: + log_info(f"ddiff_configmaps: \n{ddiff_configmaps}") + log_error(f"There were unexpected changes in configmaps: \n{diff_expected_configmaps}") + else: + log_success(f"The deletion of the origin configmap '{configmap_name}' in namespace '{namespace}' triggered a deletion of its copies.") + + log_info("Test scenario 4 completed.") + +def test_scenario_5(source_core_api, target_core_api, source_namespace, secret_name, configmap_name, source_cluster_name, target_cluster_name): + """ + Test synchronization of a secret and configmap to a different cluster. + :param core_api: CoreV1Api instance for Kubernetes API interaction in the source cluster. + :param source_namespace: The namespace in the source cluster where the secret is created. + :param secret_name: The name of the secret to be synchronized. + :param source_cluster_name: Name of the source cluster (for verification purposes). + :param target_cluster_name: Name of the target cluster where the secret should be synchronized. + """ + log_info("Test scenario 5 running...") + + labels = {"keess.powerhrg.com/sync": "cluster"} + annotations = {"keess.powerhrg.com/clusters": target_cluster_name} + + # Get all secrets and configmaps before test + initial_source_all_secrets = get_all_resources_type(source_core_api, "secret") + initial_source_all_configmaps = get_all_resources_type(source_core_api, "configmap") + initial_target_all_secrets = get_all_resources_type(target_core_api, "secret") + initial_target_all_configmaps = get_all_resources_type(target_core_api, "configmap") + + # Apply labels and annotations to the secret for cross-cluster synchronization + apply_labels_and_annotations(source_core_api, source_namespace, secret_name, labels, annotations, 'secret') + apply_labels_and_annotations(source_core_api, source_namespace, configmap_name, labels, annotations, 'configmap') + + log_info("Labels and annotations applied for cross-cluster synchronization. Waiting for synchronization to complete...") + time.sleep(WAIT_TIME) # Adjust based on expected synchronization time + + # Verify the presence of the secret in the target cluster + verify_resource_in_namespace(source_core_api, target_core_api, source_namespace, secret_name, 'secret', source_cluster_name, source_namespace) + verify_resource_in_namespace(source_core_api, target_core_api, source_namespace, configmap_name, 'configmap', source_cluster_name, source_namespace) + + # Compare all secrets and configmaps after test + source_all_secrets = get_all_resources_type(source_core_api, "secret") + source_all_configmaps = get_all_resources_type(source_core_api, "configmap") + target_all_secrets = get_all_resources_type(target_core_api, "secret") + target_all_configmaps = get_all_resources_type(target_core_api, "configmap") + ddiff_source_secrets = DeepDiff(initial_source_all_secrets, source_all_secrets).to_json() + ddiff_source_configmaps = DeepDiff(initial_source_all_configmaps, source_all_configmaps).to_json() + ddiff_target_secrets = DeepDiff(initial_target_all_secrets, target_all_secrets).to_json() + ddiff_target_configmaps = DeepDiff(initial_target_all_configmaps, target_all_configmaps).to_json() + + expected_source_secrets_result = json.dumps({ + "type_changes": { + "root['test-namespace/new-test-secret']['metadata']['annotations']": { + "old_type": "NoneType", + "new_type": "dict", + "old_value": None, + "new_value": {"keess.powerhrg.com/clusters": "kind-destination-cluster"}, + }, + "root['test-namespace/new-test-secret']['metadata']['labels']": { + "old_type": "NoneType", + "new_type": "dict", + "old_value": None, + "new_value": {"keess.powerhrg.com/sync": "cluster"}, + }, + } + }) + expected_source_configmaps_result = json.dumps({ + "type_changes": { + "root['test-namespace/new-test-configmap']['metadata']['annotations']": { + "old_type": "NoneType", + "new_type": "dict", + "old_value": None, + "new_value": {"keess.powerhrg.com/clusters": "kind-destination-cluster"}, + }, + "root['test-namespace/new-test-configmap']['metadata']['labels']": { + "old_type": "NoneType", + "new_type": "dict", + "old_value": None, + "new_value": {"keess.powerhrg.com/sync": "cluster"}, + }, + } + }) + expected_target_secrets_result = json.dumps({"dictionary_item_added": ["root['test-namespace/new-test-secret']"]}) + expected_target_configmaps_result = json.dumps({"dictionary_item_added": ["root['test-namespace/new-test-configmap']"]}) + + diff_expected_source_secrets = compare_json(expected_source_secrets_result,ddiff_source_secrets) + diff_expected_source_configmaps = compare_json(expected_source_configmaps_result,ddiff_source_configmaps) + diff_expected_target_secrets = compare_json(expected_target_secrets_result,ddiff_target_secrets) + diff_expected_target_configmaps = compare_json(expected_target_configmaps_result,ddiff_target_configmaps) + + if diff_expected_source_secrets != {}: + log_info(f"ddiff_source_secrets: \n{ddiff_source_secrets}") + log_error(f"There were unexpected changes in secrets in cluster '{source_cluster_name}': \n{diff_expected_source_secrets}") + if diff_expected_source_configmaps != {}: + log_info(f"ddiff_source_configmaps: \n{ddiff_source_configmaps}") + log_error(f"There were unexpected changes in configmaps in cluster '{source_cluster_name}': \n{diff_expected_source_configmaps}") + if diff_expected_target_secrets != {}: + log_info(f"ddiff_target_secrets: \n{ddiff_target_secrets}") + log_error(f"There were unexpected changes in secrets in cluster '{target_cluster_name}': \n{diff_expected_target_secrets}") + if diff_expected_target_configmaps != {}: + log_info(f"ddiff_target_configmaps: \n{ddiff_target_configmaps}") + log_error(f"There were unexpected changes in configmaps in cluster '{target_cluster_name}': \n{diff_expected_target_configmaps}") + + log_info("Test scenario 5 completed.") + +def test_scenario_6(core_api): + # Scenario 6: Synchronize to all namespaces + pass + +def main(): + source_cluster_name = "kind-source-cluster" + target_cluster_name = "kind-destination-cluster" + source_namespace = "test-namespace" + secret_name = "new-test-secret" + configmap_name = "new-test-configmap" + destination_namespaces = ["test-namespace-dest-1", "test-namespace-dest-2"] + label_selector = "keess.powerhrg.com/testing=yes" + + source_core_api = get_k8s_client_for_cluster(source_cluster_name) + target_core_api = get_k8s_client_for_cluster(target_cluster_name) + + # Initial cleanup + cleanup_resources(source_core_api, target_core_api, source_namespace, destination_namespaces) + + # Setup resources for tests + setup_resources_for_test(source_core_api, target_core_api, source_namespace, secret_name, configmap_name, destination_namespaces, label_selector) + + + # Execute test scenario 1 + test_scenario_1(source_core_api, source_cluster_name, source_namespace, secret_name, configmap_name, destination_namespaces) + + # Execute test scenario 2 + test_scenario_2(source_core_api, source_namespace, secret_name, configmap_name) + + # Setup resources again + setup_resources_for_test(source_core_api, target_core_api, source_namespace, secret_name, configmap_name, destination_namespaces, label_selector) + + # Execute test scenario 3 + test_scenario_3(source_core_api, source_cluster_name, source_namespace, secret_name, configmap_name, label_selector) + + # Execute test scenario 4 + test_scenario_4(source_core_api, source_namespace, secret_name, configmap_name) + + # Cleanup + cleanup_resources(source_core_api, target_core_api, source_namespace, destination_namespaces) + # Setup resources for tests + setup_resources_for_test(source_core_api, target_core_api, source_namespace, secret_name, configmap_name, destination_namespaces, label_selector) + + # Execute test scenario 5 + test_scenario_5(source_core_api, target_core_api, source_namespace, secret_name, configmap_name, source_cluster_name, target_cluster_name) + + + # Final cleanup + cleanup_resources(source_core_api, target_core_api, source_namespace, destination_namespaces) + +if __name__ == "__main__": + main()