From a71c97ca837eb88ee8a3fee7052a4f3379110e63 Mon Sep 17 00:00:00 2001 From: apedriza Date: Mon, 10 Feb 2025 17:52:42 +0100 Subject: [PATCH] Add controlplane upgrade e2e test using CAPI framework (#845) * Add e2e using capi framework Signed-off-by: Adrian Pedriza * Remove WIP from workflow Signed-off-by: Adrian Pedriza * Remove unused ginkgo test suite for k0smotron HCP Signed-off-by: Adrian Pedriza * Remove unused code Signed-off-by: Adrian Pedriza * Fix typos Signed-off-by: Adrian Pedriza * Rename cluster-template Signed-off-by: Adrian Pedriza * Wait for cluster deploy before update Signed-off-by: Adrian Pedriza --------- Signed-off-by: Adrian Pedriza Co-authored-by: Adrian Pedriza --- .github/workflows/go.yml | 28 ++ .gitignore | 3 + .golangci.yml | 3 +- Makefile | 14 + docs/contributing/contribute-testing.md | 25 +- e2e/common.go | 29 ++ e2e/config/docker.yaml | 99 +++++++ .../cluster-template-out-of-cluster.yaml | 138 ++++++++++ e2e/data/shared/v1beta1/metadata.yaml | 9 + e2e/mothership/init.go | 109 ++++++++ e2e/suite_test.go | 216 +++++++++++++++ e2e/util/cleanup.go | 80 ++++++ e2e/util/cluster.go | 105 ++++++++ e2e/util/controlplane.go | 249 ++++++++++++++++++ e2e/util/deployments.go | 59 +++++ e2e/util/dump.go | 90 +++++++ e2e/util/interval.go | 77 ++++++ e2e/util/namespaces.go | 43 +++ e2e/workload_cluster_upgrade_test.go | 127 +++++++++ go.mod | 42 ++- go.sum | 124 +++++++-- .../controller/k0smotron.io/suite_test.go | 80 ------ inttest/basic/basic_test.go | 2 + 23 files changed, 1636 insertions(+), 115 deletions(-) create mode 100644 e2e/common.go create mode 100644 e2e/config/docker.yaml create mode 100644 e2e/data/infrastructure-docker/cluster-template-out-of-cluster.yaml create mode 100644 e2e/data/shared/v1beta1/metadata.yaml create mode 100644 e2e/mothership/init.go create mode 100644 e2e/suite_test.go create mode 100644 e2e/util/cleanup.go create mode 100644 e2e/util/cluster.go create mode 100644 e2e/util/controlplane.go create mode 100644 e2e/util/deployments.go create mode 100644 e2e/util/dump.go create mode 100644 e2e/util/interval.go create mode 100644 e2e/util/namespaces.go create mode 100644 e2e/workload_cluster_upgrade_test.go delete mode 100644 internal/controller/k0smotron.io/suite_test.go diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 6810d5f49..e0e2aad91 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -171,3 +171,31 @@ jobs: uses: ./.github/workflows/capi-smoke-tests.yml with: smoke-suite: ${{ matrix.smoke-suite }} + e2e-migration: + name: E2E + needs: build + + runs-on: ubuntu-latest + + steps: + - name: Check out code into the Go module directory + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Build e2e images + run: make docker-build + + - name: Run e2e tests + run: make e2e + + - name: Archive artifacts + if: failure() + uses: actions/upload-artifact@v4.3.2 + with: + name: e2e-artifacts + path: _artifacts + if-no-files-found: ignore \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4b965d284..aedde19e4 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ k0smotron-image-bundle.tar *.swp *.swo *~ + +# test results +_artifacts \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml index 0512e9322..61a3db11b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -16,5 +16,4 @@ linters: - depguard # Checks if package imports are in a list of acceptable packages - gofmt # Checks whether code was gofmt-ed - goheader # Checks is file headers matche a given pattern - - revive # Stricter drop-in replacement for golint - - ginkgolinter # Enforces standards of using ginkgo and gomega + - revive # Stricter drop-in replacement for golint \ No newline at end of file diff --git a/Makefile b/Makefile index 34c04c52e..1eaf5dbed 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,12 @@ CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen ENVTEST ?= $(LOCALBIN)/setup-envtest CRDOC ?= $(LOCALBIN)/crdoc +## e2e configuration +E2E_CONF_FILE ?= $(shell pwd)/e2e/config/docker.yaml +SKIP_RESOURCE_CLEANUP ?= false +# Artifacts folder generated for e2e tests +ARTIFACTS ?= $(shell pwd)/_artifacts + # Image URL to use all building/pushing image targets IMG ?= quay.io/k0sproject/k0smotron:latest # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. @@ -100,6 +106,14 @@ vet: ## Run go vet against code. test: manifests generate fmt vet envtest ## Run tests. KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $(GO_TEST_DIRS) -coverprofile cover.out +.PHONY: e2e +e2e: ## Run the end-to-end tests + go test -v ./e2e -tags e2e \ + -artifacts-folder="$(ARTIFACTS)" \ + -config="$(E2E_CONF_FILE)" \ + -skip-resource-cleanup=$(SKIP_RESOURCE_CLEANUP) \ + -timeout=30m + ##@ Build .PHONY: build diff --git a/docs/contributing/contribute-testing.md b/docs/contributing/contribute-testing.md index c7b3e87e3..acffa8a9e 100644 --- a/docs/contributing/contribute-testing.md +++ b/docs/contributing/contribute-testing.md @@ -19,4 +19,27 @@ K0smotron uses [go test](https://pkg.go.dev/testing) as the foundation for all o Following the best practices suggested in the [Cluster API documentation](https://cluster-api.sigs.k8s.io/developer/core/testing), integration tests are written using [**generic infrastructure providers**](https://cluster-api.sigs.k8s.io/developer/core/testing#generic-providers) rather than a specific provider. This ensures that tests remain agnostic and reusable across different infrastructures, fostering better maintainability and adaptability. ## E2E testing -TBD + +K0smotron's end-to-end (E2E) testing leverages the [CAPI E2E framework](https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework) to provide configurability and utilities that support various phases of E2E testing, including the creation and configuration of the management cluster, waiting for specific resources, log dumping, and more. + +To fully utilize CAPI's E2E framework, it is necessary to integrate [Ginkgo](https://onsi.github.io/ginkgo/) into the project. However, in K0smotron, we intentionally avoid using this testing framework for several reasons, primarily to maintain a unified approach to writing tests using standard Go testing conventions. As a result, certain methods from CAPI's E2E framework have been reimplemented within K0smotron to remove their direct dependency on Ginkgo. + +### Run E2E + +You can run the tests using the command: + +``` cmd +make e2e +``` + +This will perform the following actions: + +1. Deploy a local cluster using [Kind](https://github.com/kubernetes-sigs/kind) as the management cluster. +2. Install the desired providers. Basically the same achieved by executing the command `clusterctl init ...`, by including: + - Cluster API for core components. + - k0smotron as controlplane and bootstrap provider. + - Configurable infrastructure provider (currently only docker supported). + +1. Execute the E2E test suite. + +> NOTE: This command will run the tests using docker as infrastructure provider but it is intended to make use of the configurability offered by the CAPI E2E framework to add other infrastructure providers that can be used in e2e testing. \ No newline at end of file diff --git a/e2e/common.go b/e2e/common.go new file mode 100644 index 000000000..92806cb12 --- /dev/null +++ b/e2e/common.go @@ -0,0 +1,29 @@ +//go:build e2e + +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +// Test suite constants for e2e config variables. +const ( + KubernetesVersion = "KUBERNETES_VERSION" + KubernetesVersionManagement = "KUBERNETES_VERSION_MANAGEMENT" + KubernetesVersionFirstUpgradeTo = "KUBERNETES_VERSION_FIRST_UPGRADE_TO" + KubernetesVersionSecondUpgradeTo = "KUBERNETES_VERSION_SECOND_UPGRADE_TO" + ControlPlaneMachineCount = "CONTROL_PLANE_MACHINE_COUNT" + IPFamily = "IP_FAMILY" +) diff --git a/e2e/config/docker.yaml b/e2e/config/docker.yaml new file mode 100644 index 000000000..40b5ebd75 --- /dev/null +++ b/e2e/config/docker.yaml @@ -0,0 +1,99 @@ +--- +# E2E test scenario using local dev images and manifests built from the source tree for following providers: +# - cluster-api +# - bootstrap k0smotron +# - control-plane k0smotron +# - infrastructure docker +images: + - name: quay.io/k0sproject/k0smotron + loadBehavior: mustLoad + +providers: + - name: cluster-api + type: CoreProvider + versions: + - name: v1.8.5 + value: https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.8.5/core-components.yaml + type: url + contract: v1beta1 + files: + - sourcePath: "../data/shared/v1beta1/metadata.yaml" + replacements: + - old: "imagePullPolicy: Always" + new: "imagePullPolicy: IfNotPresent" + - name: docker + type: InfrastructureProvider + versions: + - name: v1.8.1 + value: https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.8.1/infrastructure-components-development.yaml + type: url + contract: v1beta1 + files: + - sourcePath: "../data/shared/v1beta1/metadata.yaml" + replacements: + - old: "imagePullPolicy: Always" + new: "imagePullPolicy: IfNotPresent" + files: + - sourcePath: "../data/infrastructure-docker/cluster-template-out-of-cluster.yaml" + - name: k0sproject-k0smotron + type: ControlPlaneProvider + versions: + - name: v1.4.1 + value: ../../config/default + contract: v1beta1 + files: + - sourcePath: "../../metadata.yaml" + replacements: + - old: "imagePullPolicy: Always" + new: "imagePullPolicy: IfNotPresent" + - old: "image: k0s/k0smotron:latest" + new: "image: quay.io/k0sproject/k0smotron:latest" + - name: k0sproject-k0smotron + type: BootstrapProvider + versions: + - name: v1.4.1 + value: ../../config/default + contract: v1beta1 + files: + - sourcePath: "../../metadata.yaml" + replacements: + - old: "imagePullPolicy: Always" + new: "imagePullPolicy: IfNotPresent" + - old: "image: k0s/k0smotron:latest" + new: "image: quay.io/k0sproject/k0smotron:latest" + +variables: + KUBERNETES_VERSION_MANAGEMENT: "v1.30.0" + KUBERNETES_VERSION: "v1.31.0" + KUBERNETES_VERSION_FIRST_UPGRADE_TO: "v1.30.2+k0s.0" + KUBERNETES_VERSION_SECOND_UPGRADE_TO: "v1.31.2+k0s.0" + IP_FAMILY: "IPv4" + KIND_IMAGE_VERSION: "v1.30.0" + # Used during clusterctl upgrade test + CAPI_CORE_VERSION: "1.8.5" + # Enabling the feature flags by setting the env variables. + CLUSTER_TOPOLOGY: "true" + EXP_MACHINE_POOL: "true" + +intervals: + # The array is defined as [timeout, polling interval] + # copied from https://github.com/kubernetes-sigs/cluster-api/blob/main/test/e2e/config/docker.yaml + default/wait-controllers: ["3m", "10s"] + default/wait-cluster: ["5m", "10s"] + default/wait-control-plane: ["10m", "10s"] + default/wait-worker-nodes: ["10m", "10s"] + default/wait-machine-pool-nodes: ["10m", "10s"] + default/wait-delete-cluster: ["3m", "10s"] + default/wait-kube-proxy-upgrade: ["30m", "10s"] + default/wait-machine-pool-upgrade: ["30m", "10s"] + default/wait-nodes-ready: ["10m", "10s"] + default/wait-machine-remediation: ["5m", "10s"] + default/wait-autoscaler: ["5m", "10s"] + bootstrap/wait-deployment-available: ["3m", "10s"] + node-drain/wait-deployment-available: ["3m", "10s"] + node-drain/wait-control-plane: ["15m", "10s"] + node-drain/wait-machine-deleted: ["2m", "10s"] + # Giving a bit more time during upgrade tests + workload-upgrade/wait-cluster: ["10m", "10s"] + workload-upgrade/wait-control-plane: ["20m", "10s"] + workload-upgrade/wait-worker-nodes: ["20m", "10s"] \ No newline at end of file diff --git a/e2e/data/infrastructure-docker/cluster-template-out-of-cluster.yaml b/e2e/data/infrastructure-docker/cluster-template-out-of-cluster.yaml new file mode 100644 index 000000000..38c59ae57 --- /dev/null +++ b/e2e/data/infrastructure-docker/cluster-template-out-of-cluster.yaml @@ -0,0 +1,138 @@ +# This cluster template is used when a cluster needs to be deployed using the Out-Of-Cluster mode, +# i.e. the control plane is running on CAPI managed Machines. +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + name: ${CLUSTER_NAME} + namespace: ${NAMESPACE} +spec: + clusterNetwork: + pods: + cidrBlocks: + - 192.168.0.0/16 + serviceDomain: cluster.local + services: + cidrBlocks: + - 10.128.0.0/12 + controlPlaneRef: + apiVersion: controlplane.cluster.x-k8s.io/v1beta1 + kind: K0sControlPlane + name: ${CLUSTER_NAME}-docker-test + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: DockerCluster + name: docker-test +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: DockerMachineTemplate +metadata: + name: docker-test-cp-template + namespace: ${NAMESPACE} +spec: + template: + spec: + customImage: kindest/node:v1.31.0 +--- +apiVersion: controlplane.cluster.x-k8s.io/v1beta1 +kind: K0sControlPlane +metadata: + name: ${CLUSTER_NAME}-docker-test + namespace: ${NAMESPACE} +spec: + replicas: ${CONTROL_PLANE_MACHINE_COUNT} + version: v1.30.1+k0s.0 + updateStrategy: Recreate + k0sConfigSpec: + args: + - --enable-worker + k0s: + apiVersion: k0s.k0sproject.io/v1beta1 + kind: ClusterConfig + metadata: + name: k0s + spec: + api: + extraArgs: + anonymous-auth: "true" + telemetry: + enabled: false + network: + controlPlaneLoadBalancing: + enabled: false + files: + - path: /tmp/test-file-secret + contentFrom: + secretRef: + name: test-file-secret + key: value + machineTemplate: + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: DockerMachineTemplate + name: docker-test-cp-template + namespace: ${NAMESPACE} +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: DockerCluster +metadata: + name: docker-test + namespace: ${NAMESPACE} +spec: +--- +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Machine +metadata: + name: ${CLUSTER_NAME}-docker-test-worker-0 + namespace: ${NAMESPACE} +spec: + version: v1.30.1 + clusterName: ${CLUSTER_NAME} + bootstrap: + configRef: + apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 + kind: K0sWorkerConfig + name: docker-test-worker-0 + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: DockerMachine + name: ${CLUSTER_NAME}-docker-test-worker-0 +--- +apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 +kind: K0sWorkerConfig +metadata: + name: docker-test-worker-0 + namespace: ${NAMESPACE} +spec: + # version is deliberately different to be able to verify we actually pick it up :) + version: v1.30.1+k0s.0 + args: + - --labels=k0sproject.io/foo=bar + preStartCommands: + - echo -n "pre-start" > /tmp/pre-start + postStartCommands: + - echo -n "post-start" > /tmp/post-start + files: + - path: /tmp/test-file + content: test-file + - path: /tmp/test-file-secret + contentFrom: + secretRef: + name: test-file-secret + key: value +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: DockerMachine +metadata: + name: ${CLUSTER_NAME}-docker-test-worker-0 + namespace: ${NAMESPACE} +spec: + customImage: kindest/node:v1.31.0 +--- +apiVersion: v1 +kind: Secret +metadata: + name: test-file-secret + namespace: ${NAMESPACE} +type: Opaque +data: + value: dGVzdA== \ No newline at end of file diff --git a/e2e/data/shared/v1beta1/metadata.yaml b/e2e/data/shared/v1beta1/metadata.yaml new file mode 100644 index 000000000..a4d7f4790 --- /dev/null +++ b/e2e/data/shared/v1beta1/metadata.yaml @@ -0,0 +1,9 @@ +# maps release series of major.minor to cluster-api contract version, +# update this file only when you update the version for cluster-api +# CoreProvider and docker InfrastructureProvider in test/e2e/config/k3s-docker.yaml +apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3 +kind: Metadata +releaseSeries: + - major: 1 + minor: 8 + contract: v1beta1 diff --git a/e2e/mothership/init.go b/e2e/mothership/init.go new file mode 100644 index 000000000..048c39b32 --- /dev/null +++ b/e2e/mothership/init.go @@ -0,0 +1,109 @@ +//go:build e2e + +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mothership + +import ( + "context" + "errors" + "fmt" + "path/filepath" + + "github.com/k0sproject/k0smotron/e2e/util" + "sigs.k8s.io/cluster-api/cmd/clusterctl/client/config" + "sigs.k8s.io/cluster-api/test/framework" + "sigs.k8s.io/cluster-api/test/framework/clusterctl" +) + +// InitAndWatchControllerLogs initializes a management using clusterctl and setup watches for controller logs. +// Important: Considering we want to support test suites using existing clusters, clusterctl init is executed only in case +// there are no provider controllers in the cluster; but controller logs watchers are created regardless of the pre-existing providers. +func InitAndWatchControllerLogs(ctx context.Context, input clusterctl.InitManagementClusterAndWatchControllerLogsInput, interval util.Interval) error { + if input.CoreProvider == "" { + input.CoreProvider = config.ClusterAPIProviderName + } + if len(input.BootstrapProviders) == 0 { + input.BootstrapProviders = []string{config.KubeadmBootstrapProviderName} + } + if len(input.ControlPlaneProviders) == 0 { + input.ControlPlaneProviders = []string{config.KubeadmControlPlaneProviderName} + } + + client := input.ClusterProxy.GetClient() + controllersDeployments := framework.GetControllerDeployments(ctx, framework.GetControllerDeploymentsInput{ + Lister: client, + }) + if len(controllersDeployments) == 0 { + initInput := clusterctl.InitInput{ + // pass reference to the management cluster hosting this test + KubeconfigPath: input.ClusterProxy.GetKubeconfigPath(), + // pass the clusterctl config file that points to the local provider repository created for this test + ClusterctlConfigPath: input.ClusterctlConfigPath, + // setup the desired list of providers for a single-tenant management cluster + CoreProvider: input.CoreProvider, + BootstrapProviders: input.BootstrapProviders, + ControlPlaneProviders: input.ControlPlaneProviders, + InfrastructureProviders: input.InfrastructureProviders, + IPAMProviders: input.IPAMProviders, + RuntimeExtensionProviders: input.RuntimeExtensionProviders, + AddonProviders: input.AddonProviders, + // setup clusterctl logs folder + LogFolder: input.LogFolder, + } + + clusterctl.Init(ctx, initInput) + } + + fmt.Println("Waiting for provider controllers to be running") + controllersDeployments = framework.GetControllerDeployments(ctx, framework.GetControllerDeploymentsInput{ + Lister: client, + }) + if len(controllersDeployments) == 0 { + return errors.New("the list of controller deployments should not be empty") + } + + for _, deployment := range controllersDeployments { + err := util.WaitForDeploymentsAvailable(ctx, framework.WaitForDeploymentsAvailableInput{ + Getter: client, + Deployment: deployment, + }, interval) + if err != nil { + return err + } + + // Start streaming logs from all controller providers + framework.WatchDeploymentLogsByName(ctx, framework.WatchDeploymentLogsByNameInput{ + GetLister: client, + Cache: input.ClusterProxy.GetCache(ctx), + ClientSet: input.ClusterProxy.GetClientSet(), + Deployment: deployment, + LogPath: filepath.Join(input.LogFolder, "logs", deployment.GetNamespace()), + }) + + if !input.DisableMetricsCollection { + framework.WatchPodMetrics(ctx, framework.WatchPodMetricsInput{ + GetLister: client, + ClientSet: input.ClusterProxy.GetClientSet(), + Deployment: deployment, + MetricsPath: filepath.Join(input.LogFolder, "metrics", deployment.GetNamespace()), + }) + } + } + + return nil +} diff --git a/e2e/suite_test.go b/e2e/suite_test.go new file mode 100644 index 000000000..2d8694b8e --- /dev/null +++ b/e2e/suite_test.go @@ -0,0 +1,216 @@ +//go:build e2e + +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "context" + "errors" + "flag" + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/onsi/gomega" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/klog/v2" + "sigs.k8s.io/yaml" + + controlplanev1beta1 "github.com/k0sproject/k0smotron/api/controlplane/v1beta1" + "github.com/k0sproject/k0smotron/e2e/mothership" + "github.com/k0sproject/k0smotron/e2e/util" + "sigs.k8s.io/cluster-api/test/framework" + capiframework "sigs.k8s.io/cluster-api/test/framework" + "sigs.k8s.io/cluster-api/test/framework/bootstrap" + "sigs.k8s.io/cluster-api/test/framework/clusterctl" + ctrl "sigs.k8s.io/controller-runtime" +) + +var ( + ctx = ctrl.SetupSignalHandler() + + // watchesCtx is used in log streaming to be able to get canceled via cancelWatches after ending the test suite. + watchesCtx, cancelWatches = context.WithCancel(ctx) + + // configPath is the path to the e2e config file. + configPath string + + // e2eConfig to be used for this test, read from configPath. + e2eConfig *clusterctl.E2EConfig + + // clusterctlConfig is the file which tests will use as a clusterctl config. + clusterctlConfig string + + // useExistingCluster instructs the test to use the current cluster instead of creating a new one (default discovery rules apply). + useExistingCluster bool + + // clusterctlConfigPath to be used for this test, created by generating a clusterctl local repository + // with the providers specified in the configPath. + clusterctlConfigPath string + + // artifactFolder is the folder to store e2e test artifacts. + artifactFolder string + + // skipCleanup prevents cleanup of test resources e.g. for debug purposes. + skipCleanup bool + + // managementClusterProvider manages provisioning of the bootstrap cluster to be used for the e2e tests. + // Please note that provisioning will be skipped if e2e.use-existing-cluster is provided. + managementClusterProvider bootstrap.ClusterProvider + + // managementClusterProxy allows to interact with the management cluster to be used for the e2e tests. + managementClusterProxy capiframework.ClusterProxy +) + +func init() { + flag.StringVar(&configPath, "config", "", "path to the e2e config file") + flag.BoolVar(&skipCleanup, "skip-resource-cleanup", false, "if true, the resource cleanup after tests will be skipped") + flag.StringVar(&artifactFolder, "artifacts-folder", "", "folder where e2e test artifact should be stored") + flag.BoolVar(&useExistingCluster, "use-existing-cluster", false, "if true, the test uses the current cluster instead of creating a new one (default discovery rules apply)") +} + +func TestMain(m *testing.M) { + ctrl.SetLogger(klog.Background()) + flag.Parse() + + // On the k0smotron side we avoid using Gomega for assertions but since we want to use the + // cluster-api framework as much as possible, the framework assertions require registering + // a fail handler beforehand. + gomega.RegisterFailHandler(func(message string, callerSkip ...int) { + panic(message) + }) + + err := setupMothership() + if err != nil { + panic(err) + } + + code := m.Run() + + if !skipCleanup { + tearDown(managementClusterProvider, managementClusterProxy) + } + + os.Exit(code) +} + +func setupMothership() error { + var err error + e2eConfig, err = loadE2EConfig(ctx, configPath) + if err != nil { + return fmt.Errorf("failed to load e2e config: %w", err) + } + + if clusterctlConfig == "" { + clusterctlConfigPath = clusterctl.CreateRepository(ctx, clusterctl.CreateRepositoryInput{ + E2EConfig: e2eConfig, + RepositoryFolder: filepath.Join(artifactFolder, "repository"), + }) + } else { + clusterctlConfigPath = clusterctlConfig + } + + scheme, err := initScheme() + if err != nil { + return err + } + + kubeconfigPath := "" + if !useExistingCluster { + managementClusterProvider = bootstrap.CreateKindBootstrapClusterAndLoadImages(ctx, bootstrap.CreateKindBootstrapClusterAndLoadImagesInput{ + Name: e2eConfig.ManagementClusterName, + Images: e2eConfig.Images, + KubernetesVersion: e2eConfig.GetVariable(KubernetesVersionManagement), + RequiresDockerSock: e2eConfig.HasDockerProvider(), + IPFamily: e2eConfig.GetVariable(IPFamily), + LogFolder: filepath.Join(artifactFolder, "kind"), + }) + if managementClusterProvider == nil { + return errors.New("failed to create a management cluster") + } + kubeconfigPath = managementClusterProvider.GetKubeconfigPath() + } else { + fmt.Println("Using an existing bootstrap cluster") + } + + managementClusterProxy = capiframework.NewClusterProxy("bootstrap", kubeconfigPath, scheme, framework.WithMachineLogCollector(framework.DockerLogCollector{})) + if managementClusterProxy == nil { + return errors.New("failed to get a management cluster proxy") + } + + err = mothership.InitAndWatchControllerLogs(watchesCtx, clusterctl.InitManagementClusterAndWatchControllerLogsInput{ + ClusterProxy: managementClusterProxy, + ClusterctlConfigPath: clusterctlConfigPath, + InfrastructureProviders: e2eConfig.InfrastructureProviders(), + DisableMetricsCollection: true, + BootstrapProviders: []string{"k0sproject-k0smotron"}, + ControlPlaneProviders: []string{"k0sproject-k0smotron"}, + LogFolder: filepath.Join(artifactFolder, "capi"), + }, util.GetInterval(e2eConfig, "bootstrap", "wait-deployment-available")) + if err != nil { + return fmt.Errorf("failed to init management cluster: %w", err) + } + + return nil +} + +func tearDown(bootstrapClusterProvider bootstrap.ClusterProvider, bootstrapClusterProxy framework.ClusterProxy) { + cancelWatches() + if bootstrapClusterProxy != nil { + bootstrapClusterProxy.Dispose(ctx) + } + if bootstrapClusterProvider != nil { + bootstrapClusterProvider.Dispose(ctx) + } +} + +func initScheme() (*runtime.Scheme, error) { + s := runtime.NewScheme() + capiframework.TryAddDefaultSchemes(s) + err := controlplanev1beta1.AddToScheme(s) + if err != nil { + return nil, err + } + return s, nil +} + +func loadE2EConfig(ctx context.Context, configPath string) (*clusterctl.E2EConfig, error) { + configData, err := os.ReadFile(configPath) + + if err != nil { + return nil, fmt.Errorf("failed to read the e2e test config file: %w", err) + } + + config := &clusterctl.E2EConfig{} + err = yaml.Unmarshal(configData, config) + if err != nil { + return nil, fmt.Errorf("failed to convert the e2e test config file to yaml: %w", err) + } + + err = config.ResolveReleases(ctx) + if err != nil { + return nil, fmt.Errorf("failed to resolve release markers in e2e test config file: %w", err) + } + + config.Defaults() + config.AbsPaths(filepath.Dir(configPath)) + + return config, nil +} diff --git a/e2e/util/cleanup.go b/e2e/util/cleanup.go new file mode 100644 index 000000000..db67d7974 --- /dev/null +++ b/e2e/util/cleanup.go @@ -0,0 +1,80 @@ +//go:build e2e + +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "context" + "fmt" + "path/filepath" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/klog/v2" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + capiframework "sigs.k8s.io/cluster-api/test/framework" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// deleteClusterAndWait deletes a cluster object and waits for it to be gone. +func deleteClusterAndWait(ctx context.Context, input capiframework.DeleteClusterAndWaitInput, interval Interval) error { + + err := input.Client.Delete(ctx, input.Cluster) + if err != nil { + return fmt.Errorf("error deleting cluster %s: %w", input.Cluster.Name, err) + } + + fmt.Printf("Waiting for the Cluster %s to be deleted", klog.KObj(input.Cluster)) + waitForClusterDeleted(ctx, capiframework.WaitForClusterDeletedInput{ + Client: input.Client, + Cluster: input.Cluster, + ArtifactFolder: input.ArtifactFolder, + }, interval) + + return nil +} + +// waitForClusterDeleted waits until the cluster object has been deleted. +func waitForClusterDeleted(ctx context.Context, input capiframework.WaitForClusterDeletedInput, interval Interval) { + fmt.Printf("Waiting for cluster %s to be deleted", klog.KObj(input.Cluster)) + + clusterName := input.Cluster.GetName() + clusterNamespace := input.Cluster.GetNamespace() + // Note: dumpArtifactsOnDeletionTimeout is passed in as a func so it gets only executed if and after the Eventually failed. + err := wait.PollUntilContextTimeout(ctx, interval.tick, interval.timeout, true, func(ctx context.Context) (done bool, err error) { + cluster := &clusterv1.Cluster{} + key := client.ObjectKey{ + Namespace: clusterNamespace, + Name: clusterName, + } + + return apierrors.IsNotFound(input.Client.Get(ctx, key, cluster)), nil + }) + if err != nil { + if input.ArtifactFolder != "" { + // Dump all Cluster API related resources to artifacts. + capiframework.DumpAllResources(ctx, capiframework.DumpAllResourcesInput{ + Lister: input.Client, + Namespace: clusterNamespace, + LogPath: filepath.Join(input.ArtifactFolder, "clusters-afterDeletionTimedOut", clusterName, "resources"), + }) + } + + fmt.Println("waiting for cluster deletion timed out") + } +} diff --git a/e2e/util/cluster.go b/e2e/util/cluster.go new file mode 100644 index 000000000..834a2077b --- /dev/null +++ b/e2e/util/cluster.go @@ -0,0 +1,105 @@ +//go:build e2e + +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "context" + "fmt" + "time" + + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/klog/v2" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "sigs.k8s.io/cluster-api/test/framework" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + retryableOperationInterval = 3 * time.Second + // retryableOperationTimeout requires a higher value especially for self-hosted upgrades. + // Short unavailability of the Kube APIServer due to joining etcd members paired with unreachable conversion webhooks due to + // failed leader election and thus controller restarts lead to longer taking retries. + // The timeout occurs when listing machines in `GetControlPlaneMachinesByCluster`. + retryableOperationTimeout = 3 * time.Minute +) + +// DiscoveryAndWaitForCluster discovers a cluster object in a namespace and waits for the cluster infrastructure to be provisioned. +func DiscoveryAndWaitForCluster(ctx context.Context, input framework.DiscoveryAndWaitForClusterInput, interval Interval) (*clusterv1.Cluster, error) { + var cluster *clusterv1.Cluster + err := wait.PollUntilContextTimeout(ctx, retryableOperationInterval, retryableOperationTimeout, true, func(ctx context.Context) (done bool, err error) { + cluster, err = GetClusterByName(ctx, framework.GetClusterByNameInput{ + Getter: input.Getter, + Name: input.Name, + Namespace: input.Namespace, + }) + if err != nil { + return false, err + } + + return cluster != nil, nil + }) + if err != nil { + return nil, fmt.Errorf("failed to get Cluster object %s: %w", klog.KRef(input.Namespace, input.Name), err) + } + + return WaitForClusterToProvision(ctx, framework.WaitForClusterToProvisionInput{ + Getter: input.Getter, + Cluster: cluster, + }, interval) +} + +// GetClusterByName returns a Cluster object given his name. +func GetClusterByName(ctx context.Context, input framework.GetClusterByNameInput) (*clusterv1.Cluster, error) { + cluster := &clusterv1.Cluster{} + key := client.ObjectKey{ + Namespace: input.Namespace, + Name: input.Name, + } + + err := wait.PollUntilContextTimeout(ctx, retryableOperationInterval, retryableOperationTimeout, true, func(ctx context.Context) (done bool, err error) { + return input.Getter.Get(ctx, key, cluster) == nil, nil + }) + if err != nil { + return nil, fmt.Errorf("failed to get Cluster object %s: %w", klog.KRef(input.Namespace, input.Name), err) + } + + return cluster, nil +} + +// WaitForClusterToProvision will wait for a cluster to have a phase status of provisioned. +func WaitForClusterToProvision(ctx context.Context, input framework.WaitForClusterToProvisionInput, interval Interval) (*clusterv1.Cluster, error) { + cluster := &clusterv1.Cluster{} + fmt.Println("Waiting for cluster to enter the provisioned phase") + + err := wait.PollUntilContextTimeout(ctx, interval.tick, interval.timeout, true, func(ctx context.Context) (done bool, err error) { + key := client.ObjectKey{ + Namespace: input.Cluster.GetNamespace(), + Name: input.Cluster.GetName(), + } + if err := input.Getter.Get(ctx, key, cluster); err != nil { + return false, err + } + return cluster.Status.Phase == string(clusterv1.ClusterPhaseProvisioned), nil + }) + if err != nil { + return nil, fmt.Errorf("timed out waiting for Cluster %s to provision: %w", klog.KObj(input.Cluster), err) + } + + return cluster, nil +} diff --git a/e2e/util/controlplane.go b/e2e/util/controlplane.go new file mode 100644 index 000000000..d35c0b24e --- /dev/null +++ b/e2e/util/controlplane.go @@ -0,0 +1,249 @@ +//go:build e2e + +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "context" + "fmt" + "strings" + "time" + + "k8s.io/klog/v2" + + cpv1beta1 "github.com/k0sproject/k0smotron/api/controlplane/v1beta1" + "github.com/pkg/errors" + appsv1 "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + capiframework "sigs.k8s.io/cluster-api/test/framework" + "sigs.k8s.io/cluster-api/util/patch" + crclient "sigs.k8s.io/controller-runtime/pkg/client" +) + +func WaitForControlPlaneToBeReady(ctx context.Context, client crclient.Client, cpObjectKey crclient.ObjectKey, interval Interval) error { + fmt.Println("Waiting for the control plane to be ready") + controlplane := &cpv1beta1.K0sControlPlane{} + err := wait.PollUntilContextTimeout(ctx, interval.tick, interval.timeout, true, func(ctx context.Context) (done bool, err error) { + if err := client.Get(ctx, cpObjectKey, controlplane); err != nil { + return false, errors.Wrapf(err, "failed to get controlplane") + } + + desiredReplicas := controlplane.Spec.Replicas + statusReplicas := controlplane.Status.Replicas + updatedReplicas := controlplane.Status.UpdatedReplicas + readyReplicas := controlplane.Status.ReadyReplicas + unavailableReplicas := controlplane.Status.UnavailableReplicas + + if statusReplicas != desiredReplicas || + updatedReplicas != desiredReplicas || + readyReplicas != desiredReplicas || + unavailableReplicas > 0 || + controlplane.Spec.Version != controlplane.Status.Version { + return false, nil + } + + return true, nil + }) + if err != nil { + return fmt.Errorf(capiframework.PrettyPrint(controlplane) + "\n") + } + + return nil +} + +// UpgradeControlPlaneAndWaitForUpgradeInput is the input type for UpgradeControlPlaneAndWaitForUpgrade. +type UpgradeControlPlaneAndWaitForUpgradeInput struct { + GetLister capiframework.GetLister + ClusterProxy capiframework.ClusterProxy + Cluster *clusterv1.Cluster + ControlPlane *cpv1beta1.K0sControlPlane + KubernetesUpgradeVersion string + WaitForKubeProxyUpgradeInterval Interval + WaitForControlPlaneReadyInterval Interval +} + +// UpgradeControlPlaneAndWaitForUpgrade upgrades a K0sControlPlane and waits for it to be upgraded. +func UpgradeControlPlaneAndWaitForReadyUpgrade(ctx context.Context, input UpgradeControlPlaneAndWaitForUpgradeInput) error { + mgmtClient := input.ClusterProxy.GetClient() + + fmt.Println("Patching the new kubernetes version to KCP") + patchHelper, err := patch.NewHelper(input.ControlPlane, mgmtClient) + if err != nil { + return err + } + + input.ControlPlane.Spec.Version = input.KubernetesUpgradeVersion + + err = wait.PollUntilContextTimeout(ctx, time.Second, time.Minute, true, func(ctx context.Context) (done bool, err error) { + return patchHelper.Patch(ctx, input.ControlPlane) == nil, nil + }) + if err != nil { + return fmt.Errorf("failed to patch the new kubernetes version to controlplane %s: %w", klog.KObj(input.ControlPlane), err) + } + + controlplaneObjectKey := crclient.ObjectKey{ + Name: input.ControlPlane.Name, + Namespace: input.ControlPlane.Namespace, + } + err = WaitForControlPlaneToBeReady(ctx, input.ClusterProxy.GetClient(), controlplaneObjectKey, input.WaitForControlPlaneReadyInterval) + if err != nil { + return err + } + + fmt.Println("Waiting for kube-proxy to have the upgraded kubernetes version") + workloadCluster := input.ClusterProxy.GetWorkloadCluster(ctx, input.Cluster.Namespace, input.Cluster.Name) + workloadClient := workloadCluster.GetClient() + return WaitForKubeProxyUpgrade(ctx, WaitForKubeProxyUpgradeInput{ + Getter: workloadClient, + KubernetesVersion: input.KubernetesUpgradeVersion, + }, input.WaitForKubeProxyUpgradeInterval) +} + +func DiscoveryAndWaitForControlPlaneInitialized(ctx context.Context, input capiframework.DiscoveryAndWaitForControlPlaneInitializedInput, interval Interval) (*cpv1beta1.K0sControlPlane, error) { + var controlPlane *cpv1beta1.K0sControlPlane + err := wait.PollUntilContextTimeout(ctx, time.Second, time.Minute, true, func(ctx context.Context) (done bool, err error) { + controlPlane, err = getK0sControlPlaneByCluster(ctx, GetK0sControlPlaneByClusterInput{ + Lister: input.Lister, + ClusterName: input.Cluster.Name, + Namespace: input.Cluster.Namespace, + }) + if err != nil { + return false, err + } + + return controlPlane != nil, nil + }) + if err != nil { + return nil, fmt.Errorf("couldn't get the control plane for the cluster %s: %w", klog.KObj(input.Cluster), err) + } + + fmt.Printf("Waiting for the first control plane machine managed by %s to be provisioned", klog.KObj(controlPlane)) + err = WaitForOneK0sControlPlaneMachineToExist(ctx, WaitForOneK0sControlPlaneMachineToExistInput{ + Lister: input.Lister, + Cluster: input.Cluster, + ControlPlane: controlPlane, + }, interval) + if err != nil { + return nil, fmt.Errorf("error waiting for the first control machine to be provisioned: %w", err) + } + + return controlPlane, nil +} + +type GetK0sControlPlaneByClusterInput struct { + Lister capiframework.Lister + ClusterName string + Namespace string +} + +func getK0sControlPlaneByCluster(ctx context.Context, input GetK0sControlPlaneByClusterInput) (*cpv1beta1.K0sControlPlane, error) { + controlPlaneList := &cpv1beta1.K0sControlPlaneList{} + err := wait.PollUntilContextTimeout(ctx, time.Second, time.Minute, true, func(ctx context.Context) (done bool, err error) { + return input.Lister.List(ctx, controlPlaneList, byClusterOptions(input.ClusterName, input.Namespace)...) == nil, nil + }) + if err != nil { + return nil, fmt.Errorf("failed to list K0sControlPlane object for Cluster %s", klog.KRef(input.Namespace, input.ClusterName)) + } + + if len(controlPlaneList.Items) > 1 { + return nil, fmt.Errorf("cluster %s should not have more than 1 K0sControlPlane object", klog.KRef(input.Namespace, input.ClusterName)) + } + + if len(controlPlaneList.Items) == 1 { + return &controlPlaneList.Items[0], nil + } + + return nil, nil +} + +// byClusterOptions returns a set of ListOptions that allows to identify all the objects belonging to a Cluster. +func byClusterOptions(name, namespace string) []crclient.ListOption { + return []crclient.ListOption{ + crclient.InNamespace(namespace), + crclient.MatchingLabels{ + clusterv1.ClusterNameLabel: name, + }, + } +} + +type WaitForOneK0sControlPlaneMachineToExistInput struct { + Lister capiframework.Lister + Cluster *clusterv1.Cluster + ControlPlane *cpv1beta1.K0sControlPlane +} + +// WaitForOneK0sControlPlaneMachineToExist will wait until all control plane machines have node refs. +func WaitForOneK0sControlPlaneMachineToExist(ctx context.Context, input WaitForOneK0sControlPlaneMachineToExistInput, interval Interval) error { + fmt.Println("Waiting for one control plane node to exist") + inClustersNamespaceListOption := crclient.InNamespace(input.Cluster.Namespace) + // ControlPlane labels + matchClusterListOption := crclient.MatchingLabels{ + clusterv1.MachineControlPlaneLabel: "true", + clusterv1.ClusterNameLabel: input.Cluster.Name, + } + + err := wait.PollUntilContextTimeout(ctx, interval.tick, interval.timeout, true, func(ctx context.Context) (done bool, err error) { + machineList := &clusterv1.MachineList{} + if err := input.Lister.List(ctx, machineList, inClustersNamespaceListOption, matchClusterListOption); err != nil { + fmt.Printf("failed to list the machines: %+v", err) + return false, err + } + count := 0 + for _, machine := range machineList.Items { + if machine.Status.NodeRef != nil { + count++ + } + } + return count > 0, nil + }) + if err != nil { + return fmt.Errorf("no Control Plane machines came into existence: %w", err) + } + + return nil +} + +type WaitForKubeProxyUpgradeInput struct { + Getter capiframework.Getter + KubernetesVersion string +} + +// WaitForKubeProxyUpgrade waits until kube-proxy version matches with the kubernetes version. +func WaitForKubeProxyUpgrade(ctx context.Context, input WaitForKubeProxyUpgradeInput, interval Interval) error { + fmt.Println("Ensuring kube-proxy has the correct image") + + // this desired version is sticky to the k0s naming on the kube-proxy image + versionPrefix := strings.Split(input.KubernetesVersion, "+")[0] + wantKubeProxyImage := fmt.Sprintf("quay.io/k0sproject/kube-proxy:%s", versionPrefix) + + return wait.PollUntilContextTimeout(ctx, interval.tick, interval.timeout, true, func(ctx context.Context) (done bool, err error) { + ds := &appsv1.DaemonSet{} + + if err := input.Getter.Get(ctx, crclient.ObjectKey{Name: "kube-proxy", Namespace: metav1.NamespaceSystem}, ds); err != nil { + return false, err + } + + if strings.HasPrefix(ds.Spec.Template.Spec.Containers[0].Image, wantKubeProxyImage) { + return true, nil + } + + return false, nil + }) +} diff --git a/e2e/util/deployments.go b/e2e/util/deployments.go new file mode 100644 index 000000000..1c6f8185b --- /dev/null +++ b/e2e/util/deployments.go @@ -0,0 +1,59 @@ +//go:build e2e + +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "context" + "errors" + "fmt" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/klog/v2" + "sigs.k8s.io/cluster-api/test/framework" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// WaitForDeploymentsAvailable waits until the Deployment has status.Available = True, that signals that +// all the desired replicas are in place. +func WaitForDeploymentsAvailable(ctx context.Context, input framework.WaitForDeploymentsAvailableInput, interval Interval) error { + fmt.Printf("Waiting for deployment %s to be available", klog.KObj(input.Deployment)) + deployment := &appsv1.Deployment{} + err := wait.PollUntilContextTimeout(ctx, interval.tick, interval.timeout, true, func(ctx context.Context) (done bool, err error) { + key := client.ObjectKey{ + Namespace: input.Deployment.GetNamespace(), + Name: input.Deployment.GetName(), + } + if err := input.Getter.Get(ctx, key, deployment); err != nil { + return false, err + } + for _, c := range deployment.Status.Conditions { + if c.Type == appsv1.DeploymentAvailable && c.Status == corev1.ConditionTrue { + return true, nil + } + } + return false, nil + }) + if err != nil { + return errors.New(framework.DescribeFailedDeployment(input, deployment)) + } + + return nil +} diff --git a/e2e/util/dump.go b/e2e/util/dump.go new file mode 100644 index 000000000..678a136d3 --- /dev/null +++ b/e2e/util/dump.go @@ -0,0 +1,90 @@ +//go:build e2e + +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "context" + "fmt" + "path/filepath" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + capiframework "sigs.k8s.io/cluster-api/test/framework" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// DumpSpecResourcesAndCleanup dumps all the resources in the spec namespace and cleans up the spec namespace. +func DumpSpecResourcesAndCleanup(ctx context.Context, specName string, clusterProxy capiframework.ClusterProxy, artifactFolder string, namespace *corev1.Namespace, cancelWatches context.CancelFunc, cluster *clusterv1.Cluster, interval Interval, skipCleanup bool) { + // Dump all the resources in the spec namespace and the workload cluster. + dumpAllResourcesAndLogs(ctx, clusterProxy, artifactFolder, namespace, cluster) + + if !skipCleanup { + err := deleteClusterAndWait(ctx, capiframework.DeleteClusterAndWaitInput{ + Client: clusterProxy.GetClient(), + Cluster: cluster, + ArtifactFolder: artifactFolder, + }, interval) + if err != nil { + fmt.Println(err.Error()) + } + + capiframework.DeleteNamespace(ctx, capiframework.DeleteNamespaceInput{ + Deleter: clusterProxy.GetClient(), + Name: namespace.Name, + }) + } + cancelWatches() +} + +// dumpAllResourcesAndLogs dumps all the resources in the spec namespace and the workload cluster. +func dumpAllResourcesAndLogs(ctx context.Context, clusterProxy capiframework.ClusterProxy, artifactFolder string, namespace *corev1.Namespace, cluster *clusterv1.Cluster) { + // Dump all the logs from the workload cluster. + clusterProxy.CollectWorkloadClusterLogs(ctx, cluster.Namespace, cluster.Name, filepath.Join(artifactFolder, "clusters", cluster.Name)) + + // Dump all Cluster API related resources to artifacts. + capiframework.DumpAllResources(ctx, capiframework.DumpAllResourcesInput{ + Lister: clusterProxy.GetClient(), + Namespace: namespace.Name, + LogPath: filepath.Join(artifactFolder, "clusters", clusterProxy.GetName(), "resources"), + }) + + // If the cluster still exists, dump pods and nodes of the workload cluster. + if err := clusterProxy.GetClient().Get(ctx, client.ObjectKeyFromObject(cluster), &clusterv1.Cluster{}); err == nil { + capiframework.DumpResourcesForCluster(ctx, capiframework.DumpResourcesForClusterInput{ + Lister: clusterProxy.GetWorkloadCluster(ctx, cluster.Namespace, cluster.Name).GetClient(), + Cluster: cluster, + LogPath: filepath.Join(artifactFolder, "clusters", cluster.Name, "resources"), + Resources: []capiframework.DumpNamespaceAndGVK{ + { + GVK: schema.GroupVersionKind{ + Version: corev1.SchemeGroupVersion.Version, + Kind: "Pod", + }, + }, + { + GVK: schema.GroupVersionKind{ + Version: corev1.SchemeGroupVersion.Version, + Kind: "Node", + }, + }, + }, + }) + } +} diff --git a/e2e/util/interval.go b/e2e/util/interval.go new file mode 100644 index 000000000..f716b256d --- /dev/null +++ b/e2e/util/interval.go @@ -0,0 +1,77 @@ +//go:build e2e + +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "fmt" + "time" + + "sigs.k8s.io/cluster-api/test/framework/clusterctl" +) + +const ( + defaultTickDuration = time.Second * 10 + defaultTimeoutDuration = time.Minute * 10 +) + +type Interval struct { + timeout time.Duration + tick time.Duration +} + +func GetInterval(c *clusterctl.E2EConfig, spec string, key string) Interval { + + specKey := fmt.Sprintf("%s/%s", spec, key) + + intervals, ok := c.Intervals[specKey] + if !ok { + specKey = fmt.Sprintf("default/%s", key) + if intervals, ok = c.Intervals[specKey]; !ok { + return defaultInterval() + } + } + + if len(intervals) != 2 { + return defaultInterval() + } + + timeoutDuration, err := time.ParseDuration(intervals[0]) + if err != nil { + fmt.Printf("Error parsing duration for spec/key '%s': %s", specKey, err.Error()) + return defaultInterval() + } + + tickDuration, err := time.ParseDuration(intervals[1]) + if err != nil { + fmt.Printf("Error parsing duration for spec/key '%s': %s", specKey, err.Error()) + return defaultInterval() + } + + return Interval{ + timeout: timeoutDuration, + tick: tickDuration, + } +} + +func defaultInterval() Interval { + return Interval{ + tick: defaultTickDuration, + timeout: defaultTimeoutDuration, + } +} diff --git a/e2e/util/namespaces.go b/e2e/util/namespaces.go new file mode 100644 index 000000000..b3ee41773 --- /dev/null +++ b/e2e/util/namespaces.go @@ -0,0 +1,43 @@ +//go:build e2e + +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "context" + "fmt" + "path/filepath" + + "github.com/k0sproject/k0smotron/internal/util" + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/cluster-api/test/framework" + capiframework "sigs.k8s.io/cluster-api/test/framework" +) + +// SetupSpecNamespace creates a namespace for the test spec and setups a watch for the namespace events. +func SetupSpecNamespace(ctx context.Context, specName string, clusterProxy capiframework.ClusterProxy, artifactFolder string) (*corev1.Namespace, context.CancelFunc) { + fmt.Printf("Creating a namespace for hosting the %q test spec", specName) + namespace, cancelWatches := framework.CreateNamespaceAndWatchEvents(ctx, framework.CreateNamespaceAndWatchEventsInput{ + Creator: clusterProxy.GetClient(), + ClientSet: clusterProxy.GetClientSet(), + Name: fmt.Sprintf("%s-%s", specName, util.RandomString(6)), + LogFolder: filepath.Join(artifactFolder, "clusters", clusterProxy.GetName()), + }) + + return namespace, cancelWatches +} diff --git a/e2e/workload_cluster_upgrade_test.go b/e2e/workload_cluster_upgrade_test.go new file mode 100644 index 000000000..649f23be5 --- /dev/null +++ b/e2e/workload_cluster_upgrade_test.go @@ -0,0 +1,127 @@ +//go:build e2e + +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "fmt" + "path/filepath" + "testing" + "time" + + "github.com/k0sproject/k0smotron/e2e/util" + "github.com/stretchr/testify/require" + "k8s.io/utils/ptr" + capiframework "sigs.k8s.io/cluster-api/test/framework" + "sigs.k8s.io/cluster-api/test/framework/clusterctl" + capiutil "sigs.k8s.io/cluster-api/util" +) + +// Validation of the correct operation of k0smotron when the +// K0sControlPlane object is updated. It simulates a typical user workflow that includes: +// +// 1. Creation of a workload cluster. +// - Ensures the cluster becomes operational. +// +// 2. Updating the control plane version. +// - Verifies the cluster status aligns with the expected state after the update. +// +// 3. Performing a subsequent control plane version update. +// - Confirms the cluster status is consistent and desired post-update. +func TestWorkloadClusterUpgrade(t *testing.T) { + + testName := "workload-upgrade" + + // Setup a Namespace where to host objects for this spec and create a watcher for the namespace events. + namespace, _ := util.SetupSpecNamespace(ctx, testName, managementClusterProxy, artifactFolder) + + clusterName := fmt.Sprintf("%s-%s", testName, capiutil.RandomString(6)) + + workloadClusterTemplate := clusterctl.ConfigCluster(ctx, clusterctl.ConfigClusterInput{ + ClusterctlConfigPath: clusterctlConfigPath, + KubeconfigPath: managementClusterProxy.GetKubeconfigPath(), + // select cluster templates + Flavor: "out-of-cluster", + + Namespace: namespace.Name, + ClusterName: clusterName, + KubernetesVersion: e2eConfig.GetVariable(KubernetesVersion), + ControlPlaneMachineCount: ptr.To[int64](3), + // TODO: make infra provider configurable + InfrastructureProvider: "docker", + LogFolder: filepath.Join(artifactFolder, "clusters", managementClusterProxy.GetName()), + ClusterctlVariables: map[string]string{ + "CLUSTER_NAME": clusterName, + "NAMESPACE": namespace.Name, + }, + }) + require.NotNil(t, workloadClusterTemplate) + + require.Eventually(t, func() bool { + return managementClusterProxy.CreateOrUpdate(ctx, workloadClusterTemplate) == nil + }, 10*time.Second, 1*time.Second, "Failed to apply the cluster template") + + cluster, err := util.DiscoveryAndWaitForCluster(ctx, capiframework.DiscoveryAndWaitForClusterInput{ + Getter: managementClusterProxy.GetClient(), + Namespace: namespace.Name, + Name: clusterName, + }, util.GetInterval(e2eConfig, testName, "wait-cluster")) + require.NoError(t, err) + + defer func() { + util.DumpSpecResourcesAndCleanup( + ctx, + testName, + managementClusterProxy, + artifactFolder, + namespace, + cancelWatches, + cluster, + util.GetInterval(e2eConfig, testName, "wait-delete-cluster"), + skipCleanup, + ) + }() + + controlPlane, err := util.DiscoveryAndWaitForControlPlaneInitialized(ctx, capiframework.DiscoveryAndWaitForControlPlaneInitializedInput{ + Lister: managementClusterProxy.GetClient(), + Cluster: cluster, + }, util.GetInterval(e2eConfig, testName, "wait-controllers")) + require.NoError(t, err) + + fmt.Println("Upgrading the Kubernetes control-plane version") + err = util.UpgradeControlPlaneAndWaitForReadyUpgrade(ctx, util.UpgradeControlPlaneAndWaitForUpgradeInput{ + ClusterProxy: managementClusterProxy, + Cluster: cluster, + ControlPlane: controlPlane, + KubernetesUpgradeVersion: e2eConfig.GetVariable(KubernetesVersionFirstUpgradeTo), + WaitForKubeProxyUpgradeInterval: util.GetInterval(e2eConfig, testName, "wait-kube-proxy-upgrade"), + WaitForControlPlaneReadyInterval: util.GetInterval(e2eConfig, testName, "wait-control-plane"), + }) + require.NoError(t, err) + + fmt.Println("Upgrading the Kubernetes control-plane version again") + err = util.UpgradeControlPlaneAndWaitForReadyUpgrade(ctx, util.UpgradeControlPlaneAndWaitForUpgradeInput{ + ClusterProxy: managementClusterProxy, + Cluster: cluster, + ControlPlane: controlPlane, + KubernetesUpgradeVersion: e2eConfig.GetVariable(KubernetesVersionSecondUpgradeTo), + WaitForKubeProxyUpgradeInterval: util.GetInterval(e2eConfig, testName, "wait-kube-proxy-upgrade"), + WaitForControlPlaneReadyInterval: util.GetInterval(e2eConfig, testName, "wait-control-plane"), + }) + require.NoError(t, err) +} diff --git a/go.mod b/go.mod index 19577f341..838c9aa09 100644 --- a/go.mod +++ b/go.mod @@ -24,6 +24,7 @@ require ( k8s.io/kubectl v0.30.3 k8s.io/kubernetes v1.30.3 k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 + sigs.k8s.io/cluster-api/test v1.8.5 sigs.k8s.io/controller-runtime v0.18.5 sigs.k8s.io/yaml v1.4.0 ) @@ -41,8 +42,8 @@ require ( github.com/containerd/containerd v1.7.0 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/docker/docker v20.10.24+incompatible // indirect - github.com/docker/go-connections v0.4.1-0.20190612165340-fd1b1942c4d5 // indirect + github.com/docker/docker v27.1.1+incompatible // indirect + github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect @@ -53,7 +54,7 @@ require ( github.com/moby/spdystream v0.2.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/sirupsen/logrus v1.9.3 github.com/weaveworks/footloose v0.0.0-20210208164054-2862489574a3 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect @@ -62,27 +63,33 @@ require ( golang.org/x/crypto v0.27.0 golang.org/x/sync v0.8.0 // indirect golang.org/x/tools v0.24.0 - gotest.tools/v3 v3.4.0 // indirect helm.sh/helm/v3 v3.11.3 // indirect k8s.io/kube-aggregator v0.27.2 // indirect ) require ( github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect + github.com/BurntSushi/toml v1.4.0 // indirect github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6 // indirect + github.com/MakeNowJust/heredoc v1.0.0 // indirect + github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect + github.com/adrg/xdg v0.5.0 // indirect github.com/alessio/shellescape v1.4.2 // indirect github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect github.com/bodgit/ntlmssp v0.0.0-20240506230425-31973bb52d9b // indirect github.com/bodgit/windows v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/cloudflare/circl v1.3.7 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/creasty/defaults v1.7.0 // indirect github.com/davidmz/go-pageant v1.0.2 // indirect github.com/distribution/reference v0.6.0 // indirect + github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46 // indirect github.com/emicklei/go-restful/v3 v3.12.1 // indirect github.com/evanphx/json-patch v5.7.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect @@ -102,13 +109,17 @@ require ( github.com/google/certificate-transparency-go v1.1.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect + github.com/google/go-github/v53 v53.2.0 // indirect + github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-version v1.6.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jcmturner/aescts/v2 v2.0.0 // indirect github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect @@ -120,36 +131,51 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 // indirect github.com/masterzen/winrm v0.0.0-20240702205601-3fad6e106085 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/prometheus/client_golang v1.18.0 // indirect github.com/prometheus/client_model v0.6.0 // indirect github.com/prometheus/common v0.45.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/shopspring/decimal v1.3.1 // 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/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.19.0 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/tidwall/transform v0.0.0-20201103190739-32f242e2dbde // indirect + github.com/valyala/fastjson v1.6.4 // indirect github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b // indirect github.com/zmap/zcrypto v0.0.0-20210511125630-18f1e0152cfc // indirect github.com/zmap/zlint/v3 v3.1.0 // indirect go.etcd.io/etcd/api/v3 v3.5.15 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect go.etcd.io/etcd/client/v3 v3.5.15 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect go.opentelemetry.io/otel v1.24.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 // indirect go.opentelemetry.io/otel/metric v1.24.0 // indirect - go.opentelemetry.io/otel/sdk v1.20.0 // indirect + go.opentelemetry.io/otel/sdk v1.22.0 // indirect go.opentelemetry.io/otel/trace v1.24.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/multierr v1.11.0 // indirect @@ -168,6 +194,7 @@ require ( google.golang.org/grpc v1.62.2 // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/apiserver v0.30.3 // indirect @@ -181,6 +208,7 @@ require ( k8s.io/kubelet v0.27.1 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.0 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/kind v0.24.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect ) diff --git a/go.sum b/go.sum index ee1c316c1..8603cc9d8 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,9 @@ -cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6 h1:w0E0fgc1YafGEh5cROhlROMWXiNoZqApk2PDN0M1+Ns= github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= @@ -11,6 +12,7 @@ github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJ github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= @@ -21,8 +23,12 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= +github.com/adrg/xdg v0.5.0 h1:dDaZvhMXatArP1NPHhnfaQUqWBLBsmx1h1HXQdMoFCY= +github.com/adrg/xdg v0.5.0/go.mod h1:dDdY4M4DF9Rjy4kHPeNL+ilVF+p2lK8IdM9/rTSGcI4= github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0= github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18= @@ -41,16 +47,20 @@ github.com/bodgit/ntlmssp v0.0.0-20240506230425-31973bb52d9b h1:baFN6AnR0SeC194X github.com/bodgit/ntlmssp v0.0.0-20240506230425-31973bb52d9b/go.mod h1:Ram6ngyPDmP+0t6+4T2rymv0w0BS9N8Ch5vvUJccw5o= github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4= github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= +github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudflare/cfssl v1.6.4 h1:NMOvfrEjFfC63K3SGXgAnFdsgkmiq4kATme5BfcqrO8= github.com/cloudflare/cfssl v1.6.4/go.mod h1:8b3CQMxfWPAeom3zBnGJ6sd+G1NkL5TXqmDXacb+1J0= -github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= -github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= +github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/containerd/containerd v1.7.0 h1:G/ZQr3gMZs6ZT0qPUZ15znx5QSdQdASW11nXTLTM2Pg= github.com/containerd/containerd v1.7.0/go.mod h1:QfR7Efgb/6X2BDpTPJRvPTYDE9rsF0FsXX9J8sIs/sc= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coredns/caddy v1.1.1 h1:2eYKZT7i6yxIfGP3qLJoJ7HAsDJqYB+X68g4NYjSrE0= github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= github.com/coredns/corefile-migration v1.0.23 h1:Fp4FETmk8sT/IRgnKX2xstC2dL7+QdcU+BL5AYIN3Jw= @@ -74,26 +84,28 @@ github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9e github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.24+incompatible h1:Ugvxm7a8+Gz6vqQYQQ2W7GYq5EUPaAiuPgIfVyI3dYE= -github.com/docker/docker v20.10.24+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= +github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-connections v0.4.1-0.20190612165340-fd1b1942c4d5 h1:2o8D0hdBky229bNnc7a8bAZkeVMpH4qsp2Rmt4g/+Zk= -github.com/docker/go-connections v0.4.1-0.20190612165340-fd1b1942c4d5/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46 h1:7QPwrLT79GlD5sizHf27aoY2RTvw62mO6x7mxkScNk0= +github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46/go.mod h1:esf2rsHFNlZlxsqsZDojNBcnNs5REqIvRrWRHqX0vEU= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= -github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +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 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= @@ -138,18 +150,26 @@ github.com/google/certificate-transparency-go v1.1.4 h1:hCyXHDbtqlr/lMXU0D4Wgbal github.com/google/certificate-transparency-go v1.1.4/go.mod h1:D6lvbfwckhNrbM9WVl1EVeMOyzC19mpIjMOI4nxBHtQ= 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.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-github/v24 v24.0.1/go.mod h1:CRqaW1Uns1TCkP0wqTpxYyRxRjxwvKU/XSS44u6X74M= +github.com/google/go-github/v53 v53.2.0 h1:wvz3FyF53v4BK+AsnvCmeNhf8AkTaeh2SoYu/XUvTtI= +github.com/google/go-github/v53 v53.2.0/go.mod h1:XhFRObz+m/l+UCm9b7KSIC3lT3NWSXGt7mOsAWEloao= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.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/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 h1:SJ+NtwL6QaZ21U+IrK7d0gGgpjGGvd2kz+FzTHVzdqI= +github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2/go.mod h1:Tv1PlzqC9t8wNnpPdctvtSUOPUUg4SHeE6vR1Ir2hmg= +github.com/google/uuid v1.1.1/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/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -175,8 +195,12 @@ github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/C github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +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/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 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= @@ -222,30 +246,44 @@ 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/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +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/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 h1:2ZKn+w/BJeL43sCxI2jhPLRv73oVVOjEKZjKkflyqxg= github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= github.com/masterzen/winrm v0.0.0-20240702205601-3fad6e106085 h1:PiQLLKX4vMYlJImDzJYtQScF2BbQ0GAjPIHCDqzHHHs= github.com/masterzen/winrm v0.0.0-20240702205601-3fad6e106085/go.mod h1:JajVhkiG2bYSNYYPYuWG7WZHr42CTjMTcCjfInRNCqc= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +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/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= 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.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= 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= @@ -262,11 +300,16 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b h1:YWuSjZCQAPM8UUBLkYUk1e+rZcvWHJmFb6i6rM44Xs8= github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= 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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +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/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= @@ -278,6 +321,11 @@ github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3c 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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +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/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -286,6 +334,11 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +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/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 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= @@ -294,12 +347,15 @@ github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3k github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 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.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= 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.1.1/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/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 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= @@ -308,12 +364,17 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tidwall/transform v0.0.0-20201103190739-32f242e2dbde h1:AMNpJRc7P+GTwVbl8DkK2I9I8BBUzNiHuH/tlxrpan0= github.com/tidwall/transform v0.0.0-20201103190739-32f242e2dbde/go.mod h1:MvrEmduDUz4ST5pGZ7CABCnOU5f3ZiOAZzT6b1A6nX8= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= +github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= +github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/weppos/publicsuffix-go v0.13.1-0.20210123135404-5fd73613514e/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE= github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b h1:FsyNrX12e5BkplJq7wKOLk0+C6LZ+KGXvuEcKUYm5ss= github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE= @@ -342,8 +403,8 @@ go.etcd.io/etcd/api/v3 v3.5.15 h1:3KpLJir1ZEBrYuV2v+Twaa/e2MdDCEZ/70H+lzEiwsk= go.etcd.io/etcd/api/v3 v3.5.15/go.mod h1:N9EhGzXq58WuMllgH9ZvnEr7SI9pS0k0+DHZezGp7jM= go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5fWlA= go.etcd.io/etcd/client/pkg/v3 v3.5.15/go.mod h1:mXDI4NAOwEiszrHCb0aqfAYNCrZP4e9hRca3d1YK8EU= -go.etcd.io/etcd/client/v2 v2.305.10 h1:MrmRktzv/XF8CvtQt+P6wLUlURaNpSDJHFZhe//2QE4= -go.etcd.io/etcd/client/v2 v2.305.10/go.mod h1:m3CKZi69HzilhVqtPDcjhSGp+kA1OmbNn0qamH80xjA= +go.etcd.io/etcd/client/v2 v2.305.12 h1:0m4ovXYo1CHaA/Mp3X/Fak5sRNIWf01wk/X1/G3sGKI= +go.etcd.io/etcd/client/v2 v2.305.12/go.mod h1:aQ/yhsxMu+Oht1FOupSr60oBvcS9cKXHrzBpDsPTf9E= go.etcd.io/etcd/client/v3 v3.5.15 h1:23M0eY4Fd/inNv1ZfU3AxrbbOdW79r9V9Rl62Nm6ip4= go.etcd.io/etcd/client/v3 v3.5.15/go.mod h1:CLSJxrYjvLtHsrPKsy7LmZEE+DK2ktfd2bN4RhBMwlU= go.etcd.io/etcd/pkg/v3 v3.5.10 h1:WPR8K0e9kWl1gAhB5A7gEa5ZBTNkT9NdNWrR8Qpo1CM= @@ -352,20 +413,22 @@ go.etcd.io/etcd/raft/v3 v3.5.10 h1:cgNAYe7xrsrn/5kXMSaH8kM/Ky8mAdMqGOxyYwpP0LA= go.etcd.io/etcd/raft/v3 v3.5.10/go.mod h1:odD6kr8XQXTy9oQnyMPBOr0TVe+gT0neQhElQ6jbGRc= go.etcd.io/etcd/server/v3 v3.5.10 h1:4NOGyOwD5sUZ22PiWYKmfxqoeh72z6EhYjNosKGLmZg= go.etcd.io/etcd/server/v3 v3.5.10/go.mod h1:gBplPHfs6YI0L+RpGkTQO7buDbHv5HJGG/Bst0/zIPo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 h1:PzIubN4/sjByhDRHLviCjJuweBXWFZWhghjg7cS28+M= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0/go.mod h1:Ct6zzQEuGK3WpJs2n4dn+wfJYzd/+hNnxMRTWjGn30M= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 h1:DeFD0VgTZ+Cj6hxravYYZE2W4GlneVH81iAOPjZkzk8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0/go.mod h1:GijYcYmNpX1KazD5JmWGsi4P7dDTTTnfv1UbGn84MnU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 h1:gvmNvqrPYovvyRmCSygkUDyL8lC5Tl845MLEwqpxhEU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0/go.mod h1:vNUq47TGFioo+ffTSnKNdob241vePmtNZnAODKapKd0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 h1:FyjCyI9jVEfqhUh2MoSkmolPjfh5fp2hnV0b0irxH4Q= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0/go.mod h1:hYwym2nDEeZfG/motx0p7L7J1N1vyzIThemQsb4g2qY= go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= -go.opentelemetry.io/otel/sdk v1.20.0 h1:5Jf6imeFZlZtKv9Qbo6qt2ZkmWtdWx/wzcCbNUlAWGM= -go.opentelemetry.io/otel/sdk v1.20.0/go.mod h1:rmkSx1cZCm/tn16iWDn1GQbLtsW/LvsdEEFzCSRM6V0= +go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= +go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= @@ -384,6 +447,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= @@ -404,6 +468,7 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 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.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= @@ -425,17 +490,20 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w 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-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.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.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= @@ -443,6 +511,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.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.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= @@ -452,7 +521,6 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= @@ -463,8 +531,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= -google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= +google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= +google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 h1:rIo7ocm2roD9DcFIX67Ym8icoGCKSARAiPljFhh5suQ= google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y= google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c h1:lfpJ/2rWPa/kJgxyyXM8PrNnfCzcmxJ265mADgwmvLI= @@ -479,6 +547,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 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/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -533,10 +603,14 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.0 h1:Tc9rS7JJoZ9sl sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.0/go.mod h1:1ewhL9l1gkPcU/IU/6rFYfikf+7Y5imWv7ARVbBOzNs= sigs.k8s.io/cluster-api v1.8.5 h1:lNA2fPN4fkXEs+oOQlnwxT/4VwRFBpv5kkSoJG8nqBA= sigs.k8s.io/cluster-api v1.8.5/go.mod h1:pXv5LqLxuIbhGIXykyNKiJh+KrLweSBajVHHitPLyoY= +sigs.k8s.io/cluster-api/test v1.8.5 h1:p2fjSv/exSFgYw+pO6iap1RVJPc0LJ+/A/PAMPl9vhU= +sigs.k8s.io/cluster-api/test v1.8.5/go.mod h1:odnzMkDndCRPCWdwl0CRofyZyY857wN34bUih1MLKIc= sigs.k8s.io/controller-runtime v0.18.5 h1:nTHio/W+Q4aBlQMgbnC5hZb4IjIidyrizMai9P6n4Rk= sigs.k8s.io/controller-runtime v0.18.5/go.mod h1:TVoGrfdpbA9VRFaRnKgk9P5/atA0pMwq+f+msb9M8Sg= 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/kind v0.24.0 h1:g4y4eu0qa+SCeKESLpESgMmVFBebL0BDa6f777OIWrg= +sigs.k8s.io/kind v0.24.0/go.mod h1:t7ueEpzPYJvHA8aeLtI52rtFftNgUYUaCwvxjk7phfw= 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.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= diff --git a/internal/controller/k0smotron.io/suite_test.go b/internal/controller/k0smotron.io/suite_test.go deleted file mode 100644 index f52b8c3dc..000000000 --- a/internal/controller/k0smotron.io/suite_test.go +++ /dev/null @@ -1,80 +0,0 @@ -/* -Copyright 2023. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package k0smotronio - -import ( - "path/filepath" - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - - k0smotronk0sprojectiov1beta1 "github.com/k0sproject/k0smotron/api/k0smotron.io/v1beta1" - //+kubebuilder:scaffold:imports -) - -// These tests use Ginkgo (BDD-style Go testing framework). Refer to -// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. - -var cfg *rest.Config -var k8sClient client.Client -var testEnv *envtest.Environment - -func TestAPIs(t *testing.T) { - RegisterFailHandler(Fail) - - RunSpecs(t, "Controller Suite") -} - -var _ = BeforeSuite(func() { - logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) - - By("bootstrapping test environment") - testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, - ErrorIfCRDPathMissing: true, - } - - var err error - // cfg is defined in this file globally. - cfg, err = testEnv.Start() - Expect(err).NotTo(HaveOccurred()) - Expect(cfg).NotTo(BeNil()) - - err = k0smotronk0sprojectiov1beta1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - - //+kubebuilder:scaffold:scheme - - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) - Expect(err).NotTo(HaveOccurred()) - Expect(k8sClient).NotTo(BeNil()) - -}) - -var _ = AfterSuite(func() { - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).NotTo(HaveOccurred()) -}) diff --git a/inttest/basic/basic_test.go b/inttest/basic/basic_test.go index 25158335c..fdc409828 100644 --- a/inttest/basic/basic_test.go +++ b/inttest/basic/basic_test.go @@ -75,6 +75,8 @@ func (s *BasicSuite) TestK0sGetsUp() { s.Require().True(strings.Contains(configMap.Data["K0SMOTRON_K0S_YAML"], "kmc-kmc-test-nodeport.kmc-test.svc.cluster.local")) s.Require().True(strings.Contains(configMap.Data["K0SMOTRON_K0S_YAML"], "externalAddress:")) + time.Sleep(time.Second * 30) + s.T().Log("updating k0smotron cluster") s.updateK0smotronCluster(s.Context(), rc)