From b56671f3ef129be5f4ca7ddfece65d24f138d4fa Mon Sep 17 00:00:00 2001 From: Dawid Rusnak Date: Fri, 15 Nov 2024 09:39:02 +0100 Subject: [PATCH 01/30] fix: merge general-use steps properly (#6031) --- .../action/containerize.go | 6 +- .../action/containerize_test.go | 61 ++++++++++++++++++ .../presets/processor_test.go | 64 +++++++++++++++++++ 3 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 pkg/testworkflows/testworkflowprocessor/action/containerize_test.go diff --git a/pkg/testworkflows/testworkflowprocessor/action/containerize.go b/pkg/testworkflows/testworkflowprocessor/action/containerize.go index fd04d8bfe4..b0d631b64d 100644 --- a/pkg/testworkflows/testworkflowprocessor/action/containerize.go +++ b/pkg/testworkflows/testworkflowprocessor/action/containerize.go @@ -39,11 +39,15 @@ func CreateContainer(groupId int, defaultContainer stage2.Container, actions []a // Find the highest priority container configuration var bestContainerConfig *actiontypes.Action var bestIsToolkit = false + var bestIsDefaultImage = true for i := range containerConfigs { if executable[containerConfigs[i].Container.Ref] { - if bestContainerConfig == nil || bestIsToolkit { + image := containerConfigs[i].Container.Config.Image + isDefaultImage := image == "" || image == constants.DefaultInitImage || image == constants.DefaultToolkitImage + if bestContainerConfig == nil || bestIsToolkit || (bestIsDefaultImage && !isDefaultImage) { bestContainerConfig = containerConfigs[i] bestIsToolkit = toolkit[bestContainerConfig.Container.Ref] + bestIsDefaultImage = isDefaultImage } } } diff --git a/pkg/testworkflows/testworkflowprocessor/action/containerize_test.go b/pkg/testworkflows/testworkflowprocessor/action/containerize_test.go new file mode 100644 index 0000000000..90e37219d5 --- /dev/null +++ b/pkg/testworkflows/testworkflowprocessor/action/containerize_test.go @@ -0,0 +1,61 @@ +package action + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + testworkflowsv1 "github.com/kubeshop/testkube-operator/api/testworkflows/v1" + "github.com/kubeshop/testkube/internal/common" + "github.com/kubeshop/testkube/pkg/testworkflows/testworkflowprocessor/action/actiontypes" + "github.com/kubeshop/testkube/pkg/testworkflows/testworkflowprocessor/constants" + "github.com/kubeshop/testkube/pkg/testworkflows/testworkflowprocessor/stage" +) + +func TestCreateContainer_MergingDefaultStepBefore(t *testing.T) { + input := actiontypes.NewActionList(). + MutateContainer("ref1", testworkflowsv1.ContainerConfig{ + Command: common.Ptr([]string{"sleep"}), + }). + Start("ref1"). + Execute("ref1", false, true). + End("ref1"). + CurrentStatus("ref1"). + MutateContainer("ref2", testworkflowsv1.ContainerConfig{ + Image: "custom-image:1.2.3", + Command: common.Ptr([]string{"my-test"}), + }). + Start("ref2"). + Execute("ref2", false, false). + End("ref2"). + End(constants.RootOperationName). + End("") + result, _, err := CreateContainer(1, stage.NewContainer(), input, false) + + assert.NoError(t, err) + assert.Equal(t, result.Image, "custom-image:1.2.3") +} + +func TestCreateContainer_MergingDefaultStepAfter(t *testing.T) { + input := actiontypes.NewActionList(). + MutateContainer("ref1", testworkflowsv1.ContainerConfig{ + Image: "custom-image:1.2.3", + Command: common.Ptr([]string{"my-test"}), + }). + Start("ref1"). + Execute("ref1", false, false). + End("ref1"). + CurrentStatus("ref1"). + MutateContainer("ref2", testworkflowsv1.ContainerConfig{ + Command: common.Ptr([]string{"sleep"}), + }). + Start("ref2"). + Execute("ref2", false, true). + End("ref2"). + End(constants.RootOperationName). + End("") + result, _, err := CreateContainer(1, stage.NewContainer(), input, false) + + assert.NoError(t, err) + assert.Equal(t, result.Image, "custom-image:1.2.3") +} diff --git a/pkg/testworkflows/testworkflowprocessor/presets/processor_test.go b/pkg/testworkflows/testworkflowprocessor/presets/processor_test.go index b7f842085d..18b503e3e7 100644 --- a/pkg/testworkflows/testworkflowprocessor/presets/processor_test.go +++ b/pkg/testworkflows/testworkflowprocessor/presets/processor_test.go @@ -1334,3 +1334,67 @@ func TestProcess_PureShellAtTheEnd(t *testing.T) { assert.NoError(t, err) assert.Equal(t, want, res.LiteActions()) } + +func TestProcess_MergingActions(t *testing.T) { + wf := &testworkflowsv1.TestWorkflow{ + Spec: testworkflowsv1.TestWorkflowSpec{ + Steps: []testworkflowsv1.Step{ + { + StepOperations: testworkflowsv1.StepOperations{Delay: "1s"}, + }, + { + StepDefaults: testworkflowsv1.StepDefaults{Container: &testworkflowsv1.ContainerConfig{ + Image: "custom-image:1.2.3", + }}, + StepOperations: testworkflowsv1.StepOperations{Shell: "test-command"}, + }, + }, + }, + } + + res, err := proc.Bundle(context.Background(), wf, testworkflowprocessor.BundleOptions{Config: testConfig}) + sig := res.Signature + + wantActions := lite.NewLiteActionGroups(). + Append(func(list lite.LiteActionList) lite.LiteActionList { + return list. + // configure + Setup(true, false, true). + Declare(constants.RootOperationName, "true"). + Declare(sig[0].Ref(), "true", constants.RootOperationName). + Declare(sig[1].Ref(), sig[0].Ref(), constants.RootOperationName). + Result(constants.RootOperationName, and(sig[0].Ref(), sig[1].Ref())). + Result("", constants.RootOperationName). + + // initialize + Start(""). + CurrentStatus("true"). + Start(constants.RootOperationName). + CurrentStatus(constants.RootOperationName) + }). + Append(func(list lite.LiteActionList) lite.LiteActionList { + return list. + // start first container + MutateContainer(lite.LiteContainerConfig{ + Command: cmd("sleep"), + Args: cmd("1"), + }). + Start(sig[0].Ref()). + Execute(sig[0].Ref(), false, true). + End(sig[0].Ref()). + CurrentStatus(and(sig[0].Ref(), constants.RootOperationName)). + MutateContainer(lite.LiteContainerConfig{ + Command: cmd("/.tktw/bin/sh"), + Args: cmdShell("test-command"), + }). + Start(sig[1].Ref()). + Execute(sig[1].Ref(), false, false). + End(sig[1].Ref()). + End(constants.RootOperationName). + End("") + }) + + assert.NoError(t, err) + assert.Equal(t, wantActions, res.LiteActions()) + assert.Equal(t, res.Job.Spec.Template.Spec.Containers[0].Image, "custom-image:1.2.3") +} From 59c91b3a7c63b4ecc1b64f90f76b3a980b12732b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 12:32:22 +0300 Subject: [PATCH 02/30] build(deps): bump anchore/sbom-action from 0.17.6 to 0.17.7 (#6010) Bumps [anchore/sbom-action](https://github.com/anchore/sbom-action) from 0.17.6 to 0.17.7. - [Release notes](https://github.com/anchore/sbom-action/releases) - [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md) - [Commits](https://github.com/anchore/sbom-action/compare/v0.17.6...v0.17.7) --- updated-dependencies: - dependency-name: anchore/sbom-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../docker-build-api-executors-tag.yaml | 22 +++++++++---------- .github/workflows/release-dev.yaml | 2 +- .github/workflows/release.yaml | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/docker-build-api-executors-tag.yaml b/.github/workflows/docker-build-api-executors-tag.yaml index fef18588e3..c416842072 100644 --- a/.github/workflows/docker-build-api-executors-tag.yaml +++ b/.github/workflows/docker-build-api-executors-tag.yaml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v4 - uses: sigstore/cosign-installer@v3.7.0 - - uses: anchore/sbom-action/download-syft@v0.17.6 + - uses: anchore/sbom-action/download-syft@v0.17.7 - name: Set up Docker Buildx id: buildx @@ -94,7 +94,7 @@ jobs: uses: actions/checkout@v4 - uses: sigstore/cosign-installer@v3.7.0 - - uses: anchore/sbom-action/download-syft@v0.17.6 + - uses: anchore/sbom-action/download-syft@v0.17.7 - name: Set up Docker Buildx id: buildx @@ -178,7 +178,7 @@ jobs: uses: actions/checkout@v4 - uses: sigstore/cosign-installer@v3.7.0 - - uses: anchore/sbom-action/download-syft@v0.17.6 + - uses: anchore/sbom-action/download-syft@v0.17.7 - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -249,7 +249,7 @@ jobs: uses: actions/checkout@v4 - uses: sigstore/cosign-installer@v3.7.0 - - uses: anchore/sbom-action/download-syft@v0.17.6 + - uses: anchore/sbom-action/download-syft@v0.17.7 - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -317,7 +317,7 @@ jobs: uses: actions/checkout@v4 - uses: sigstore/cosign-installer@v3.7.0 - - uses: anchore/sbom-action/download-syft@v0.17.6 + - uses: anchore/sbom-action/download-syft@v0.17.7 - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -442,7 +442,7 @@ jobs: uses: actions/checkout@v4 - uses: sigstore/cosign-installer@v3.7.0 - - uses: anchore/sbom-action/download-syft@v0.17.6 + - uses: anchore/sbom-action/download-syft@v0.17.7 - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -510,7 +510,7 @@ jobs: uses: actions/checkout@v4 - uses: sigstore/cosign-installer@v3.7.0 - - uses: anchore/sbom-action/download-syft@v0.17.6 + - uses: anchore/sbom-action/download-syft@v0.17.7 - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -669,7 +669,7 @@ jobs: uses: actions/checkout@v4 - uses: sigstore/cosign-installer@v3.7.0 - - uses: anchore/sbom-action/download-syft@v0.17.6 + - uses: anchore/sbom-action/download-syft@v0.17.7 - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -741,7 +741,7 @@ jobs: uses: docker/setup-qemu-action@v3 - uses: sigstore/cosign-installer@v3.7.0 - - uses: anchore/sbom-action/download-syft@v0.17.6 + - uses: anchore/sbom-action/download-syft@v0.17.7 - name: Set up Docker Buildx id: buildx @@ -790,7 +790,7 @@ jobs: uses: actions/checkout@v4 - uses: sigstore/cosign-installer@v3.7.0 - - uses: anchore/sbom-action/download-syft@v0.17.6 + - uses: anchore/sbom-action/download-syft@v0.17.7 - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -863,7 +863,7 @@ jobs: fetch-depth: 0 - uses: sigstore/cosign-installer@v3.7.0 - - uses: anchore/sbom-action/download-syft@v0.17.6 + - uses: anchore/sbom-action/download-syft@v0.17.7 - name: Set up QEMU uses: docker/setup-qemu-action@v3 diff --git a/.github/workflows/release-dev.yaml b/.github/workflows/release-dev.yaml index 8688fbbc3c..f7070c56f5 100644 --- a/.github/workflows/release-dev.yaml +++ b/.github/workflows/release-dev.yaml @@ -185,7 +185,7 @@ jobs: with: fetch-depth: 0 - uses: sigstore/cosign-installer@v3.7.0 - - uses: anchore/sbom-action/download-syft@v0.17.6 + - uses: anchore/sbom-action/download-syft@v0.17.7 - name: Download Artifacts for Linux uses: actions/download-artifact@master with: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index e50e15a883..f2a8ff1388 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -177,7 +177,7 @@ jobs: with: fetch-depth: 0 - uses: sigstore/cosign-installer@v3.7.0 - - uses: anchore/sbom-action/download-syft@v0.17.6 + - uses: anchore/sbom-action/download-syft@v0.17.7 - name: Download Artifacts for Linux uses: actions/download-artifact@master with: From b806dd3b7d3de1329c99fba9da8f83517c3f5b78 Mon Sep 17 00:00:00 2001 From: Vladislav Sukhin Date: Mon, 18 Nov 2024 13:39:08 +0300 Subject: [PATCH 03/30] fix: [TKC-2713] helm and kubectl init info (#6002) * fix: helm and kubectl debug info Signed-off-by: Vladislav Sukhin * fix: debug for dashboard Signed-off-by: Vladislav Sukhin * fix: pass set helm options Signed-off-by: Vladislav Sukhin * fix: mode debug info Signed-off-by: Vladislav Sukhin * fix: executed command Signed-off-by: Vladislav Sukhin * fix: add/modify sets Signed-off-by: Vladislav Sukhin * fix: add command context Signed-off-by: Vladislav Sukhin * fix: helm arg options Signed-off-by: Vladislav Sukhin * fix: ignore empty value Signed-off-by: Vladislav Sukhin * fix: adjust error message Signed-off-by: Vladislav Sukhin * fix: spinner Signed-off-by: Vladislav Sukhin --------- Signed-off-by: Vladislav Sukhin --- .../commands/common/errors.go | 33 ++++- .../commands/common/helper.go | 137 ++++++++++++------ cmd/kubectl-testkube/commands/dashboard.go | 7 +- cmd/kubectl-testkube/commands/init.go | 25 +++- cmd/kubectl-testkube/commands/pro/init.go | 14 +- pkg/ui/printers.go | 7 + pkg/ui/ui.go | 1 + 7 files changed, 167 insertions(+), 57 deletions(-) diff --git a/cmd/kubectl-testkube/commands/common/errors.go b/cmd/kubectl-testkube/commands/common/errors.go index 8279f2f49a..e48d0147b6 100644 --- a/cmd/kubectl-testkube/commands/common/errors.go +++ b/cmd/kubectl-testkube/commands/common/errors.go @@ -3,6 +3,7 @@ package common import ( "fmt" "os" + "strings" "github.com/spf13/cobra" @@ -52,13 +53,14 @@ const ( const helpUrl = "https://testkubeworkspace.slack.com" type CLIError struct { - Code ErrorCode - Title string - Description string - ActualError error - StackTrace string - MoreInfo string - Telemetry *ErrorTelemetry + Code ErrorCode + Title string + Description string + ActualError error + StackTrace string + MoreInfo string + ExecutedCommand string + Telemetry *ErrorTelemetry } type ErrorTelemetry struct { @@ -86,6 +88,15 @@ func (e *CLIError) Print() { pterm.DefaultSection.Println("Error Details") + cmd := "" + if e.ExecutedCommand != "" { + pterm.FgDarkGray.Printfln("Executed command: %s", e.ExecutedCommand) + params := strings.Split(e.ExecutedCommand, " ") + if len(params) > 0 { + cmd = params[0] + } + } + items := []pterm.BulletListItem{ {Level: 0, Text: pterm.Sprintf("[%s]: %s", e.Code, e.Title), TextStyle: pterm.NewStyle(pterm.FgRed)}, {Level: 0, Text: pterm.Sprintf("%s", e.Description), TextStyle: pterm.NewStyle(pterm.FgLightWhite)}, @@ -94,6 +105,9 @@ func (e *CLIError) Print() { items = append(items, pterm.BulletListItem{Level: 0, Text: pterm.Sprintf("%s", e.MoreInfo), TextStyle: pterm.NewStyle(pterm.FgGray)}) } pterm.DefaultBulletList.WithItems(items).Render() + if cmd != "" { + pterm.DefaultBox.Printfln("Error description is provided in context of binary execution %s", cmd) + } pterm.Println() pterm.Println("Let us help you!") @@ -111,6 +125,11 @@ func NewCLIError(code ErrorCode, title, moreInfoURL string, err error) *CLIError } } +func (err *CLIError) WithExecutedCommand(executedCommand string) *CLIError { + err.ExecutedCommand = executedCommand + return err +} + // HandleCLIError checks does the error exist, and if it does, prints the error and exits the program. func HandleCLIError(err *CLIError) { if err != nil { diff --git a/cmd/kubectl-testkube/commands/common/helper.go b/cmd/kubectl-testkube/commands/common/helper.go index 4e9e8e58f7..778ba132ac 100644 --- a/cmd/kubectl-testkube/commands/common/helper.go +++ b/cmd/kubectl-testkube/commands/common/helper.go @@ -28,6 +28,7 @@ type HelmOptions struct { Name, Namespace, Chart, Values string NoMinio, NoMongo, NoConfirm bool MinioReplicas, MongoReplicas int + SetOptions, ArgOptions map[string]string // On-prem LicenseKey string @@ -101,16 +102,11 @@ func HelmUpgradeOrInstallTestkubeAgent(options HelmOptions, cfg config.Data, isM } args := prepareTestkubeProHelmArgs(options, isMigration) - output, err := runHelmCommand(helmPath, args, options.DryRun) + _, err := runHelmCommand(helmPath, args, options.DryRun) if err != nil { return err } - ui.Debug("Helm command output:") - ui.Debug(helmPath, args...) - - ui.Debug("Helm install testkube output", output) - return nil } @@ -147,7 +143,7 @@ func lookupHelmPath() (string, *CLIError) { return helmPath, nil } -func updateHelmRepo(helmPath string, dryRun bool, isOnPrem bool) *CLIError { +func updateHelmRepo(helmPath string, dryRun, isOnPrem bool) *CLIError { registryURL := "https://kubeshop.github.io/helm-charts" registryName := "kubeshop" if isOnPrem { @@ -176,16 +172,18 @@ func CleanExistingCompletedMigrationJobs(namespace string) (cliErr *CLIError) { } // Clean the job only when it's found and it's state is successful - ignore pending migrations. - succeeded, _ := runKubectlCommand(kubectlPath, []string{"get", "job", "testkube-enterprise-api-migrations", "-n", namespace, "-o", "jsonpath={.status.succeeded}"}) + cmd := []string{"get", "job", "testkube-enterprise-api-migrations", "-n", namespace, "-o", "jsonpath={.status.succeeded}"} + succeeded, _ := runKubectlCommand(kubectlPath, cmd) if succeeded == "1" { - _, err := runKubectlCommand(kubectlPath, []string{"delete", "job", "testkube-enterprise-api-migrations", "--namespace", namespace}) + cmd = []string{"delete", "job", "testkube-enterprise-api-migrations", "--namespace", namespace} + _, err := runKubectlCommand(kubectlPath, cmd) if err != nil { return NewCLIError( TKErrCleanOldMigrationJobFailed, "Can't clean old migrations job", "Migration job can't be deleted from some reason, check for errors in installation namespace, check execution. As a workaround try to delete job manually and retry installation/upgrade process", err, - ) + ).WithExecutedCommand(strings.Join(cmd, " ")) } } @@ -193,82 +191,120 @@ func CleanExistingCompletedMigrationJobs(namespace string) (cliErr *CLIError) { } func runHelmCommand(helmPath string, args []string, dryRun bool) (commandOutput string, cliErr *CLIError) { + cmd := strings.Join(append([]string{helmPath}, args...), " ") + ui.DebugNL() + ui.Debug("Helm command:") + ui.Debug(cmd) + output, err := process.ExecuteWithOptions(process.Options{Command: helmPath, Args: args, DryRun: dryRun}) + ui.DebugNL() + ui.Debug("Helm output:") + ui.Debug(string(output)) if err != nil { return "", NewCLIError( TKErrHelmCommandFailed, "Helm command failed", - "Retry the command with a bigger timeout by setting --timeout 30m, if the error still persists, reach out to Testkube support", + "Retry the command with a bigger timeout by setting --helm-arg timeout=30m, if the error still persists, reach out to Testkube support", err, - ) + ).WithExecutedCommand(cmd) } return string(output), nil } +func appendHelmArgs(args []string, options HelmOptions, settings map[string]string) []string { + for key, value := range settings { + if _, ok := options.SetOptions[key]; !ok { + args = append(args, "--set", fmt.Sprintf("%s=%s", key, value)) + } + } + + for key, value := range options.SetOptions { + args = append(args, "--set", fmt.Sprintf("%s=%s", key, value)) + } + + for key, value := range options.ArgOptions { + args = append(args, fmt.Sprintf("--%s", key)) + if value != "" { + args = append(args, value) + } + } + + return args +} + func prepareTestkubeOnPremDemoArgs(options HelmOptions) []string { - return []string{ + args := []string{ "upgrade", "--install", "--create-namespace", "--namespace", options.Namespace, - "--set", "global.enterpriseLicenseKey=" + options.LicenseKey, - "--values", options.DemoValuesURL, + } + + settings := map[string]string{ + "global.enterpriseLicenseKey": options.LicenseKey, + } + + args = append(appendHelmArgs(args, options, settings), "--values", options.DemoValuesURL, "--wait", - "testkube", "testkubeenterprise/testkube-enterprise"} + "testkube", "testkubeenterprise/testkube-enterprise") + + return args } // prepareTestkubeProHelmArgs prepares Helm arguments for Testkube Pro installation. func prepareTestkubeProHelmArgs(options HelmOptions, isMigration bool) []string { - args := prepareCommonHelmArgs(options) + args, settings := prepareCommonHelmArgs(options) - args = append(args, - "--set", "testkube-api.cloud.url="+options.Master.URIs.Agent, - "--set", "testkube-api.cloud.key="+options.Master.AgentToken, - "--set", "testkube-api.cloud.uiURL="+options.Master.URIs.Ui, - "--set", "testkube-logs.pro.url="+options.Master.URIs.Logs, - "--set", "testkube-logs.pro.key="+options.Master.AgentToken, - ) + settings["testkube-api.cloud.url"] = options.Master.URIs.Agent + settings["testkube-api.cloud.key"] = options.Master.AgentToken + settings["testkube-api.cloud.uiURL"] = options.Master.URIs.Ui + settings["testkube-logs.pro.url"] = options.Master.URIs.Logs + settings["testkube-logs.pro.key"] = options.Master.AgentToken if isMigration { - args = append(args, "--set", "testkube-api.cloud.migrate=true") + settings["testkube-api.cloud.migrate"] = "true" } if options.Master.EnvId != "" { - args = append(args, "--set", fmt.Sprintf("testkube-api.cloud.envId=%s", options.Master.EnvId)) - args = append(args, "--set", fmt.Sprintf("testkube-logs.pro.envId=%s", options.Master.EnvId)) + settings["testkube-api.cloud.envId"] = options.Master.EnvId + settings["testkube-logs.pro.envId"] = options.Master.EnvId } if options.Master.OrgId != "" { - args = append(args, "--set", fmt.Sprintf("testkube-api.cloud.orgId=%s", options.Master.OrgId)) - args = append(args, "--set", fmt.Sprintf("testkube-logs.pro.orgId=%s", options.Master.OrgId)) + settings["testkube-api.cloud.orgId"] = options.Master.OrgId + settings["testkube-logs.pro.orgId"] = options.Master.OrgId } - return args + return appendHelmArgs(args, options, settings) } // prepareTestkubeHelmArgs prepares Helm arguments for Testkube OS installation. func prepareTestkubeHelmArgs(options HelmOptions) []string { - args := prepareCommonHelmArgs(options) + args, settings := prepareCommonHelmArgs(options) if options.NoMinio { - args = append(args, "--set", "testkube-api.logs.storage=mongo") + settings["testkube-api.logs.storage"] = "mongo" } else { - args = append(args, "--set", "testkube-api.logs.storage=minio") + settings["testkube-api.logs.storage"] = "minio" } - return args + return appendHelmArgs(args, options, settings) } // prepareCommonHelmArgs prepares common Helm arguments for both OS and Pro installation. -func prepareCommonHelmArgs(options HelmOptions) []string { +func prepareCommonHelmArgs(options HelmOptions) ([]string, map[string]string) { args := []string{ "upgrade", "--install", "--create-namespace", "--namespace", options.Namespace, - "--set", fmt.Sprintf("global.features.logsV2=%v", options.Master.Features.LogsV2), - "--set", fmt.Sprintf("testkube-api.multinamespace.enabled=%t", options.MultiNamespace), - "--set", fmt.Sprintf("testkube-api.minio.enabled=%t", !options.NoMinio), - "--set", fmt.Sprintf("testkube-api.minio.replicas=%d", options.MinioReplicas), "--set", fmt.Sprintf("testkube-operator.enabled=%t", !options.NoOperator), - "--set", fmt.Sprintf("mongodb.enabled=%t", !options.NoMongo), - "--set", fmt.Sprintf("mongodb.replicas=%d", options.MongoReplicas), + } + + settings := map[string]string{ + "global.features.logsV2": fmt.Sprintf("%v", options.Master.Features.LogsV2), + "testkube-api.multinamespace.enabled": fmt.Sprintf("%t", options.MultiNamespace), + "testkube-api.minio.enabled": fmt.Sprintf("%t", !options.NoMinio), + "testkube-api.minio.replicas": fmt.Sprintf("%d", options.MinioReplicas), + "testkube-operator.enabled": fmt.Sprintf("%t", !options.NoOperator), + "mongodb.enabled": fmt.Sprintf("%t", !options.NoMongo), + "mongodb.replicas": fmt.Sprintf("%d", options.MongoReplicas), } if options.Values != "" { @@ -277,12 +313,12 @@ func prepareCommonHelmArgs(options HelmOptions) []string { // if embedded nats is enabled disable nats chart if options.EmbeddedNATS { - args = append(args, "--set", "testkube-api.nats.enabled=false") - args = append(args, "--set", "testkube-api.nats.embedded=true") + settings["testkube-api.nats.enabled"] = "false" + settings["testkube-api.nats.embedded"] = "true" } args = append(args, options.Name, options.Chart) - return args + return args, settings } func PopulateHelmFlags(cmd *cobra.Command, options *HelmOptions) { @@ -463,6 +499,7 @@ func LoginUser(authUri string, customConnector bool) (string, string, error) { connectorID = ui.Select("Choose your login method", []string{github, gitlab}) } + ui.Debug("Logging into cloud with parameters", authUri, connectorID) authUrl, tokenChan, err := cloudlogin.CloudLogin(context.Background(), authUri, strings.ToLower(connectorID)) if err != nil { return "", "", fmt.Errorf("cloud login: %w", err) @@ -477,6 +514,7 @@ func LoginUser(authUri string, customConnector bool) (string, string, error) { return "", "", fmt.Errorf("login cancelled") } + ui.Debug("Opening login page in browser to get a token", authUrl) // open browser with login page and redirect to localhost open.Run(authUrl) @@ -732,14 +770,21 @@ func lookupKubectlPath() (string, *CLIError) { } func runKubectlCommand(kubectlPath string, args []string) (output string, cliErr *CLIError) { + cmd := strings.Join(append([]string{kubectlPath}, args...), " ") + ui.DebugNL() + ui.Debug("Kubectl command:") + ui.Debug(cmd) out, err := process.Execute(kubectlPath, args...) + ui.DebugNL() + ui.Debug("Kubectl output:") + ui.Debug(string(out)) if err != nil { return "", NewCLIError( TKErrKubectlCommandFailed, "Kubectl command failed", "Check does the kubeconfig file (~/.kube/config) exist and has correct permissions and is the Kubernetes cluster reachable and has Ready nodes by running 'kubectl get nodes' ", err, - ) + ).WithExecutedCommand(cmd) } return string(out), nil } @@ -766,7 +811,7 @@ func RunDockerCommand(args []string) (output string, cliErr *CLIError) { "Docker command failed", "Check is the Docker service installed and running on your computer by executing 'docker info' ", err, - ) + ).WithExecutedCommand(strings.Join(append([]string{"docker"}, args...), " ")) } return string(out), nil } diff --git a/cmd/kubectl-testkube/commands/dashboard.go b/cmd/kubectl-testkube/commands/dashboard.go index 3aec322e43..b54bc8f556 100644 --- a/cmd/kubectl-testkube/commands/dashboard.go +++ b/cmd/kubectl-testkube/commands/dashboard.go @@ -71,22 +71,26 @@ func openOnPremDashboard(cmd *cobra.Command, cfg config.Data, verbose, skipBrows uri := fmt.Sprintf("http://localhost:%d", uiLocalPort) ctx, cancel := context.WithCancel(context.Background()) + + ui.Debug("Port forwarding for api", config.EnterpriseApiName) err = k8sclient.PortForward(ctx, cfg.Namespace, config.EnterpriseApiName, config.EnterpriseApiPort, config.EnterpriseApiForwardingPort, verbose) if err != nil { sendErrTelemetry(cmd, cfg, "port_forward", license, "port forwarding api", err) } ui.ExitOnError("port forwarding api", err) + ui.Debug("Port forwarding for ui", config.EnterpriseUiName) err = k8sclient.PortForward(ctx, cfg.Namespace, config.EnterpriseUiName, config.EnterpriseUiPort, uiLocalPort, verbose) if err != nil { sendErrTelemetry(cmd, cfg, "port_forward", license, "port forwarding ui", err) } ui.ExitOnError("port forwarding ui", err) + ui.Debug("Port forwarding for dex", config.EnterpriseDexName) err = k8sclient.PortForward(ctx, cfg.Namespace, config.EnterpriseDexName, config.EnterpriseDexPort, config.EnterpriseDexForwardingPort, verbose) if err != nil { sendErrTelemetry(cmd, cfg, "port_forward", license, "port forwarding dex", err) } ui.ExitOnError("port forwarding dex", err) - + ui.Debug("Port forwarding for minio", config.EnterpriseMinioName) err = k8sclient.PortForward(ctx, cfg.Namespace, config.EnterpriseMinioName, config.EnterpriseMinioPort, config.EnterpriseMinioPortFrwardingPort, verbose) if err != nil { sendTelemetry(cmd, cfg, license, "port forwarding minio") @@ -94,6 +98,7 @@ func openOnPremDashboard(cmd *cobra.Command, cfg config.Data, verbose, skipBrows ui.ExitOnError("port forwarding minio", err) if !skipBrowser { + ui.Debug("Opening dashboard in browser", uri) err = open.Run(uri) if err != nil { sendErrTelemetry(cmd, cfg, "open_dashboard", license, "opening dashboard", err) diff --git a/cmd/kubectl-testkube/commands/init.go b/cmd/kubectl-testkube/commands/init.go index aa434d03be..4fe0ff53ee 100644 --- a/cmd/kubectl-testkube/commands/init.go +++ b/cmd/kubectl-testkube/commands/init.go @@ -61,6 +61,7 @@ func NewInitCmd() *cobra.Command { func NewInitCmdStandalone() *cobra.Command { var export bool var options common.HelmOptions + var setOptions, argOptions map[string]string cmd := &cobra.Command{ Use: standaloneAgentProfile, @@ -81,7 +82,11 @@ func NewInitCmdStandalone() *cobra.Command { } common.ProcessMasterFlags(cmd, &options, nil) - + options.SetOptions = setOptions + options.ArgOptions = argOptions + ui.NL() + ui.H2("Running Helm command...") + ui.NL() common.HandleCLIError(common.HelmUpgradeOrInstallTestkube(options)) ui.Info(`To help improve the quality of Testkube, we collect anonymous basic telemetry data. Head out to https://docs.testkube.io/articles/telemetry to read our policy or feel free to:`) @@ -97,6 +102,8 @@ func NewInitCmdStandalone() *cobra.Command { } cmd.Flags().BoolVarP(&export, "export", "", false, "Export the values.yaml") + cmd.Flags().StringToStringVarP(&setOptions, "helm-set", "", nil, "helm set option in form of key=value") + cmd.Flags().StringToStringVarP(&argOptions, "helm-arg", "", nil, "helm arg option in form of key=value") common.PopulateHelmFlags(cmd, &options) common.PopulateMasterFlags(cmd, &options, false) @@ -106,6 +113,7 @@ func NewInitCmdStandalone() *cobra.Command { func NewInitCmdDemo() *cobra.Command { var noConfirm, dryRun, export bool var license, namespace string + var setOptions, argOptions map[string]string cmd := &cobra.Command{ Use: demoProfile, @@ -132,6 +140,9 @@ func NewInitCmdDemo() *cobra.Command { sendTelemetry(cmd, cfg, license, "installation launched") + ui.NL() + ui.H2("Running Kubectl command...") + ui.NL() kubecontext, cliErr := common.GetCurrentKubernetesContext() if cliErr != nil { if cfg.TelemetryEnabled { @@ -206,7 +217,7 @@ func NewInitCmdDemo() *cobra.Command { } } - spinner := ui.NewSpinner("Installing Testkube On-Prem Demo...") + spinner := ui.NewSpinner("Running Kubectl command...") sendTelemetry(cmd, cfg, license, "installing started") options := common.HelmOptions{ Namespace: namespace, @@ -225,6 +236,10 @@ func NewInitCmdDemo() *cobra.Command { common.HandleCLIError(cliErr) } + spinner.Success() + spinner = ui.NewSpinner("Running Helm command...") + options.SetOptions = setOptions + options.ArgOptions = argOptions cliErr = common.HelmUpgradeOrInstallTestkubeOnPremDemo(options) if cliErr != nil { spinner.Fail("Failed to install Testkube On-Prem Demo") @@ -264,6 +279,10 @@ func NewInitCmdDemo() *cobra.Command { sendTelemetry(cmd, cfg, license, "opening dashboard") cfg, err = config.Load() ui.ExitOnError("Cannot open dashboard", err) + + ui.NL() + ui.H2("Launching web browser...") + ui.NL() openOnPremDashboard(cmd, cfg, false, false, license) }, } @@ -273,6 +292,8 @@ func NewInitCmdDemo() *cobra.Command { cmd.Flags().StringVarP(&license, "license", "l", "", "License key") cmd.Flags().BoolVarP(&dryRun, "dry-run", "", false, "Dry run") cmd.Flags().StringVarP(&namespace, "namespace", "n", "", "Namespace to install "+demoInstallationName) + cmd.Flags().StringToStringVarP(&setOptions, "helm-set", "", nil, "helm set option in form of key=value") + cmd.Flags().StringToStringVarP(&argOptions, "helm-arg", "", nil, "helm arg option in form of key=value") return cmd } diff --git a/cmd/kubectl-testkube/commands/pro/init.go b/cmd/kubectl-testkube/commands/pro/init.go index 7f54702a57..3270480f60 100644 --- a/cmd/kubectl-testkube/commands/pro/init.go +++ b/cmd/kubectl-testkube/commands/pro/init.go @@ -16,6 +16,8 @@ import ( func NewInitCmd() *cobra.Command { var export bool var noLogin bool // ignore ask for login + var setOptions, argOptions map[string]string + options := common.HelmOptions{ NoMinio: true, NoMongo: true, @@ -56,6 +58,9 @@ func NewInitCmd() *cobra.Command { ui.Warn("Please be sure you're on valid kubectl context before continuing!") ui.NL() + ui.NL() + ui.H2("Running Kubectl command...") + ui.NL() currentContext, cliErr := common.GetCurrentKubernetesContext() if cliErr != nil { sendErrTelemetry(cmd, cfg, "k8s_context", err) @@ -72,7 +77,9 @@ func NewInitCmd() *cobra.Command { } } - spinner := ui.NewSpinner("Installing Testkube") + spinner := ui.NewSpinner("Running Helm command...") + options.SetOptions = setOptions + options.ArgOptions = argOptions if cliErr := common.HelmUpgradeOrInstallTestkubeAgent(options, cfg, false); cliErr != nil { spinner.Fail() sendErrTelemetry(cmd, cfg, "helm_install", cliErr) @@ -98,6 +105,9 @@ func NewInitCmd() *cobra.Command { ui.H2("Saving Testkube CLI Pro context") var token, refreshToken string if !common.IsUserLoggedIn(cfg, options) { + ui.NL() + ui.H2("Launching web browser...") + ui.NL() token, refreshToken, err = common.LoginUser(options.Master.URIs.Auth, options.Master.CustomAuth) sendErrTelemetry(cmd, cfg, "login", err) ui.ExitOnError("user login", err) @@ -119,6 +129,8 @@ func NewInitCmd() *cobra.Command { cmd.Flags().BoolVarP(&export, "export", "", false, "Export the values.yaml") cmd.Flags().BoolVar(&options.MultiNamespace, "multi-namespace", false, "multi namespace mode") cmd.Flags().BoolVar(&options.NoOperator, "no-operator", false, "should operator be installed (for more instances in multi namespace mode it should be set to true)") + cmd.Flags().StringToStringVarP(&setOptions, "helm-set", "", nil, "helm set option in form of key=value") + cmd.Flags().StringToStringVarP(&argOptions, "helm-arg", "", nil, "helm arg option in form of key=value") return cmd } diff --git a/pkg/ui/printers.go b/pkg/ui/printers.go index 0d567cb05e..362c2a80c2 100644 --- a/pkg/ui/printers.go +++ b/pkg/ui/printers.go @@ -37,6 +37,13 @@ func (ui *UI) NL(amount ...int) { fmt.Fprintln(ui.Writer) } +func (ui *UI) DebugNL(amount ...int) { + if !ui.Verbose { + return + } + ui.NL(amount...) +} + // Success shows success in terminal func (ui *UI) Success(message string, subMessages ...string) { fmt.Fprintf(ui.Writer, "%s", LightYellow(message)) diff --git a/pkg/ui/ui.go b/pkg/ui/ui.go index 82563b7c39..3425926fa9 100644 --- a/pkg/ui/ui.go +++ b/pkg/ui/ui.go @@ -61,6 +61,7 @@ func WarnOnErrorAndOutputPretty(item string, outputPretty bool, errors ...error) func Logo() { ui.Logo() } func LogoNoColor() { ui.LogoNoColor() } func NL(amount ...int) { ui.NL(amount...) } +func DebugNL(amount ...int) { ui.DebugNL(amount...) } func H1(message string) { ui.H1(message) } func H2(message string) { ui.H2(message) } func Paragraph(message string) { ui.Paragraph(message) } From 481c968ef081545bc32191828a1f471c86313f97 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 13:58:16 +0300 Subject: [PATCH 04/30] build(deps): bump codecov/codecov-action from 4 to 5 (#6033) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4 to 5. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v4...v5) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 437aa02fbd..472a1e9ebb 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -53,7 +53,7 @@ jobs: - name: Send coverage report to Codecov if: always() - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: file: ./coverage.out flags: unittests @@ -181,7 +181,7 @@ jobs: - name: Send coverage report to Codecov if: always() - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: file: ./coverage.out flags: integrationtests From f9205066e73bd6741271dd2868bd78a7f5c07b07 Mon Sep 17 00:00:00 2001 From: Vladislav Sukhin Date: Mon, 18 Nov 2024 18:47:06 +0300 Subject: [PATCH 05/30] feat: [TKC-2765] run test workflows by selector (#6016) * feat: run test workflows by selector Signed-off-by: Vladislav Sukhin * fix: log typo Signed-off-by: Vladislav Sukhin * feat: selector for execute op Signed-off-by: Vladislav Sukhin * fix: dep update Signed-off-by: Vladislav Sukhin * feat: execute method with selector Signed-off-by: Vladislav Sukhin * fix: check if name is not empty Signed-off-by: Vladislav Sukhin * fix: exit on error code Signed-off-by: Vladislav Sukhin * fix: update help Signed-off-by: Vladislav Sukhin * fix: switch to error merhods Signed-off-by: Vladislav Sukhin * fix: error text Signed-off-by: Vladislav Sukhin * fix: join errors Signed-off-by: Vladislav Sukhin * fix: use existing naming in cli Signed-off-by: Vladislav Sukhin * fix: move common part Signed-off-by: Vladislav Sukhin * fix: help Signed-off-by: Vladislav Sukhin * feat: add execute test workflows api method spec Signed-off-by: Vladislav Sukhin * Update pkg/api/v1/client/interface.go Co-authored-by: Dawid Rusnak * fix: remove concurrency level Signed-off-by: Vladislav Sukhin * fix: use Label Selector Signed-off-by: Vladislav Sukhin * fix: check for empty name and selector Signed-off-by: Vladislav Sukhin * fix: dep update Signed-off-by: Vladislav Sukhin --------- Signed-off-by: Vladislav Sukhin Co-authored-by: Dawid Rusnak --- api/v1/testkube.yaml | 54 ++++++++++++ .../commands/testworkflows/run.go | 85 +++++++++++++------ .../testworkflow-toolkit/commands/execute.go | 73 +++++++++++----- go.mod | 4 +- go.sum | 4 +- internal/app/api/v1/server.go | 1 + internal/app/api/v1/testworkflows.go | 84 ++++++++++++------ pkg/api/v1/client/interface.go | 1 + pkg/api/v1/client/testworkflow.go | 17 ++++ ...workflow_step_execute_test_workflow_ref.go | 3 +- pkg/mapper/testworkflows/kube_openapi.go | 16 ++++ pkg/mapper/testworkflows/openapi_kube.go | 16 ++++ 12 files changed, 281 insertions(+), 77 deletions(-) diff --git a/api/v1/testkube.yaml b/api/v1/testkube.yaml index 1a7068d0e5..26fafe0525 100644 --- a/api/v1/testkube.yaml +++ b/api/v1/testkube.yaml @@ -4065,6 +4065,57 @@ paths: items: $ref: "#/components/schemas/Problem" /test-workflow-executions: + post: + parameters: + - $ref: "#/components/parameters/Selector" + - $ref: "#/components/parameters/ConcurrencyLevel" + tags: + - api + - test-workflows + - pro + summary: "Execute test workflows" + description: "Execute test workflows in the kubernetes cluster" + operationId: executeTestWorkflows + requestBody: + description: test workflow execution request + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/TestWorkflowExecutionRequest" + responses: + 200: + description: successful execution + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TestWorkflowExecution" + 400: + description: "problem with body parsing - probably some bad input occurs" + content: + application/problem+json: + schema: + type: array + items: + $ref: "#/components/schemas/Problem" + 402: + description: "missing Pro subscription for a commercial feature" + content: + application/problem+json: + schema: + type: array + items: + $ref: "#/components/schemas/Problem" + 502: + description: problem communicating with kubernetes cluster + content: + application/problem+json: + schema: + type: array + items: + $ref: "#/components/schemas/Problem" get: tags: - test-workflows @@ -8990,6 +9041,9 @@ components: $ref: "#/components/schemas/TestWorkflowTarballRequest" config: $ref: "#/components/schemas/TestWorkflowConfigValue" + selector: + $ref: "#/components/schemas/LabelSelector" + description: label selector for test workflow TestWorkflowStepExecuteTestRef: type: object diff --git a/cmd/kubectl-testkube/commands/testworkflows/run.go b/cmd/kubectl-testkube/commands/testworkflows/run.go index d0f10c51ba..5c96c690d3 100644 --- a/cmd/kubectl-testkube/commands/testworkflows/run.go +++ b/cmd/kubectl-testkube/commands/testworkflows/run.go @@ -3,6 +3,7 @@ package testworkflows import ( "bufio" "bytes" + "context" "errors" "fmt" "os" @@ -45,12 +46,12 @@ func NewRunTestWorkflowCmd() *cobra.Command { format string masks []string tags map[string]string + selectors []string ) cmd := &cobra.Command{ Use: "testworkflow [name]", Aliases: []string{"testworkflows", "tw"}, - Args: cobra.ExactArgs(1), Short: "Starts test workflow execution", Run: func(cmd *cobra.Command, args []string) { @@ -65,7 +66,6 @@ func NewRunTestWorkflowCmd() *cobra.Command { client, _, err := common.GetClient(cmd) ui.ExitOnError("getting client", err) - name := args[0] runContext := telemetry.GetCliRunContext() interfaceType := testkube.CICD_TestWorkflowRunningContextInterfaceType if runContext == "others|local" { @@ -83,13 +83,29 @@ func NewRunTestWorkflowCmd() *cobra.Command { runningContext = tclcmd.GetRunningContext(runContext, cfg.CloudContext.ApiKey, interfaceType) } - execution, err := client.ExecuteTestWorkflow(name, testkube.TestWorkflowExecutionRequest{ + request := testkube.TestWorkflowExecutionRequest{ Name: executionName, Config: config, DisableWebhooks: disableWebhooks, Tags: tags, RunningContext: runningContext, - }) + } + + var executions []testkube.TestWorkflowExecution + switch { + case len(args) > 0: + name := args[0] + + var execution testkube.TestWorkflowExecution + execution, err = client.ExecuteTestWorkflow(name, request) + executions = append(executions, execution) + case len(selectors) != 0: + selector := strings.Join(selectors, ",") + executions, err = client.ExecuteTestWorkflows(selector, request) + default: + ui.Failf("Pass Test workflow name or labels to run by labels ") + } + if err != nil { // User friendly Open Source operation error errMessage := err.Error() @@ -108,33 +124,49 @@ func NewRunTestWorkflowCmd() *cobra.Command { } } - ui.ExitOnError("execute test workflow "+name+" from namespace "+namespace, err) - err = renderer.PrintTestWorkflowExecution(cmd, os.Stdout, execution) - ui.ExitOnError("render test workflow execution", err) - - var exitCode = 0 - if outputPretty { - ui.NL() - if !execution.FailedToInitialize() { - if watchEnabled { - exitCode = uiWatch(execution, client) - ui.NL() - if downloadArtifactsEnabled { - tests.DownloadTestWorkflowArtifacts(execution.Id, downloadDir, format, masks, client, outputPretty) + if len(args) > 0 { + ui.ExitOnError("execute test workflow "+args[0]+" from namespace "+namespace, err) + } else { + ui.ExitOnError("execute test workflows "+strings.Join(selectors, ",")+" from namespace "+namespace, err) + } + + go func() { + <-cmd.Context().Done() + if errors.Is(cmd.Context().Err(), context.Canceled) { + os.Exit(0) + } + }() + + for _, execution := range executions { + err = renderer.PrintTestWorkflowExecution(cmd, os.Stdout, execution) + ui.ExitOnError("render test workflow execution", err) + + var exitCode = 0 + if outputPretty { + ui.NL() + if !execution.FailedToInitialize() { + if watchEnabled && len(args) > 0 { + exitCode = uiWatch(execution, client) + ui.NL() + if downloadArtifactsEnabled { + tests.DownloadTestWorkflowArtifacts(execution.Id, downloadDir, format, masks, client, outputPretty) + } + } else { + uiShellWatchExecution(execution.Id) } - } else { - uiShellWatchExecution(execution.Id) } - } - execution, err = client.GetTestWorkflowExecution(execution.Id) - ui.ExitOnError("get execution failed", err) + execution, err = client.GetTestWorkflowExecution(execution.Id) + ui.ExitOnError("get execution failed", err) - render.PrintTestWorkflowExecutionURIs(&execution) - uiShellGetExecution(execution.Id) - } + render.PrintTestWorkflowExecutionURIs(&execution) + uiShellGetExecution(execution.Id) + } - os.Exit(exitCode) + if exitCode != 0 { + os.Exit(exitCode) + } + } }, } @@ -148,6 +180,7 @@ func NewRunTestWorkflowCmd() *cobra.Command { cmd.Flags().StringVar(&format, "format", "folder", "data format for storing files, one of folder|archive") cmd.Flags().StringArrayVarP(&masks, "mask", "", []string{}, "regexp to filter downloaded files, single or comma separated, like report/.* or .*\\.json,.*\\.js$") cmd.Flags().StringToStringVarP(&tags, "tag", "", map[string]string{}, "execution tags in a form of name1=val1 passed to executor") + cmd.Flags().StringSliceVarP(&selectors, "label", "l", nil, "label key value pair: --label key1=value1 or label expression") return cmd } diff --git a/cmd/tcl/testworkflow-toolkit/commands/execute.go b/cmd/tcl/testworkflow-toolkit/commands/execute.go index 2079b69e20..ce47f57f83 100644 --- a/cmd/tcl/testworkflow-toolkit/commands/execute.go +++ b/cmd/tcl/testworkflow-toolkit/commands/execute.go @@ -18,6 +18,7 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" testworkflowsv1 "github.com/kubeshop/testkube-operator/api/testworkflows/v1" commontcl "github.com/kubeshop/testkube/cmd/tcl/testworkflow-toolkit/common" @@ -368,6 +369,8 @@ func NewExecuteCmd() *cobra.Command { operations = append(operations, fn) } } + + c := env.Testkube() for _, s := range workflows { var w testworkflowsv1.StepExecuteWorkflow err := json.Unmarshal([]byte(s), &w) @@ -375,35 +378,65 @@ func NewExecuteCmd() *cobra.Command { ui.Fail(errors.Wrap(err, "unmarshal workflow definition")) } - // Resolve the params - params, err := commontcl.GetParamsSpec(w.Matrix, w.Shards, w.Count, w.MaxCount, baseMachine) - if err != nil { - ui.Fail(errors.Wrap(err, "matrix and sharding")) + if w.Name == "" && w.Selector == nil { + ui.Fail(errors.New("either workflow name or selector should be specified")) } - fmt.Printf("%s: %s\n", commontcl.ServiceLabel(w.Name), params.Humanize()) - // Create operations for each expected execution - for i := int64(0); i < params.Count; i++ { - // Clone the spec - spec := w.DeepCopy() + var testWorkflowNames []string + if w.Name != "" { + testWorkflowNames = []string{w.Name} + } - // Build files for transfer - tarballMachine, err := registerTransfer(transferSrv, spec.Tarball, baseMachine, params.MachineAt(i)) + if w.Selector != nil { + selector, err := metav1.LabelSelectorAsSelector(w.Selector) if err != nil { - ui.Fail(errors.Wrapf(err, "'%s' workflow", spec.Name)) + ui.Fail(errors.Wrap(err, "error creating selector from test workflow selector")) } - spec.Tarball = nil - // Prepare the operation to run - err = expressions.Finalize(&spec, baseMachine, tarballMachine, params.MachineAt(i)) + stringifiedSelector := selector.String() + testWorkflowsList, err := c.ListTestWorkflows(stringifiedSelector) if err != nil { - ui.Fail(errors.Wrapf(err, "'%s' workflow: computing execution", spec.Name)) + ui.Fail(errors.Wrap(err, "error listing test workflows using selector")) } - fn, err := buildWorkflowExecution(*spec, async) - if err != nil { - ui.Fail(err) + + for _, item := range testWorkflowsList { + testWorkflowNames = append(testWorkflowNames, item.Name) + } + } + + // Resolve the params + params, err := commontcl.GetParamsSpec(w.Matrix, w.Shards, w.Count, w.MaxCount, baseMachine) + if err != nil { + ui.Fail(errors.Wrap(err, "matrix and sharding")) + } + + for _, testWorkflowName := range testWorkflowNames { + fmt.Printf("%s: %s\n", commontcl.ServiceLabel(testWorkflowName), params.Humanize()) + + // Create operations for each expected execution + for i := int64(0); i < params.Count; i++ { + // Clone the spec + spec := w.DeepCopy() + spec.Name = testWorkflowName + + // Build files for transfer + tarballMachine, err := registerTransfer(transferSrv, spec.Tarball, baseMachine, params.MachineAt(i)) + if err != nil { + ui.Fail(errors.Wrapf(err, "'%s' workflow", spec.Name)) + } + spec.Tarball = nil + + // Prepare the operation to run + err = expressions.Finalize(&spec, baseMachine, tarballMachine, params.MachineAt(i)) + if err != nil { + ui.Fail(errors.Wrapf(err, "'%s' workflow: computing execution", spec.Name)) + } + fn, err := buildWorkflowExecution(*spec, async) + if err != nil { + ui.Fail(err) + } + operations = append(operations, fn) } - operations = append(operations, fn) } } diff --git a/go.mod b/go.mod index 9cee87d6da..2d1b1bf494 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,7 @@ require ( github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/kelseyhightower/envconfig v1.4.0 github.com/kubepug/kubepug v1.7.1 - github.com/kubeshop/testkube-operator v1.17.55-0.20241030092155-2a57f6e797e9 + github.com/kubeshop/testkube-operator v1.17.55-0.20241118133003-70462ac10f4a github.com/minio/minio-go/v7 v7.0.47 github.com/montanaflynn/stats v0.6.6 github.com/moogar0880/problems v0.1.1 @@ -55,6 +55,7 @@ require ( github.com/prometheus/client_golang v1.18.0 github.com/pterm/pterm v0.12.79 github.com/robfig/cron v1.2.0 + github.com/savioxavier/termlink v1.4.1 github.com/segmentio/analytics-go/v3 v3.2.1 github.com/shirou/gopsutil/v3 v3.24.3 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 @@ -185,7 +186,6 @@ require ( github.com/rs/xid v1.4.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 // indirect - github.com/savioxavier/termlink v1.4.1 // indirect github.com/savsgio/gotils v0.0.0-20211223103454-d0aaa54c5899 // indirect github.com/segmentio/backo-go v1.0.0 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect diff --git a/go.sum b/go.sum index cd0cd7ac9e..a16cd51848 100644 --- a/go.sum +++ b/go.sum @@ -402,8 +402,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kubepug/kubepug v1.7.1 h1:LKhfSxS8Y5mXs50v+3Lpyec+cogErDLcV7CMUuiaisw= github.com/kubepug/kubepug v1.7.1/go.mod h1:lv+HxD0oTFL7ZWjj0u6HKhMbbTIId3eG7aWIW0gyF8g= -github.com/kubeshop/testkube-operator v1.17.55-0.20241030092155-2a57f6e797e9 h1:0v4W4kPfuDBJxvfkgKhDFA71AgmV0B5Jdb8dR7n4bV4= -github.com/kubeshop/testkube-operator v1.17.55-0.20241030092155-2a57f6e797e9/go.mod h1:P47tw1nKQFufdsZndyq2HG2MSa0zK/lU0XpRfZtEmIk= +github.com/kubeshop/testkube-operator v1.17.55-0.20241118133003-70462ac10f4a h1:xget2cwwqOL+K2Op9FPbMgfzj9lSVJAzZ9p48yxuFrE= +github.com/kubeshop/testkube-operator v1.17.55-0.20241118133003-70462ac10f4a/go.mod h1:P47tw1nKQFufdsZndyq2HG2MSa0zK/lU0XpRfZtEmIk= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= diff --git a/internal/app/api/v1/server.go b/internal/app/api/v1/server.go index e555f095ec..615f1e52c7 100644 --- a/internal/app/api/v1/server.go +++ b/internal/app/api/v1/server.go @@ -147,6 +147,7 @@ func (s *TestkubeAPI) Init(server server.HTTPServer) { testWorkflowExecutions := root.Group("/test-workflow-executions") testWorkflowExecutions.Get("/", s.ListTestWorkflowExecutionsHandler()) + testWorkflowExecutions.Post("/", s.ExecuteTestWorkflowHandler()) testWorkflowExecutions.Get("/:executionID", s.GetTestWorkflowExecutionHandler()) testWorkflowExecutions.Get("/:executionID/notifications", s.StreamTestWorkflowExecutionNotificationsHandler()) testWorkflowExecutions.Get("/:executionID/notifications/stream", s.StreamTestWorkflowExecutionNotificationsWebSocketHandler()) diff --git a/internal/app/api/v1/testworkflows.go b/internal/app/api/v1/testworkflows.go index 4602eb555a..4a741e24b3 100644 --- a/internal/app/api/v1/testworkflows.go +++ b/internal/app/api/v1/testworkflows.go @@ -2,13 +2,13 @@ package v1 import ( "context" + "errors" "fmt" "net/http" "strconv" "strings" "github.com/gofiber/fiber/v2" - "github.com/pkg/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" testworkflowsv1 "github.com/kubeshop/testkube-operator/api/testworkflows/v1" @@ -348,10 +348,27 @@ func (s *TestkubeAPI) ExecuteTestWorkflowHandler() fiber.Handler { return func(c *fiber.Ctx) (err error) { ctx := c.Context() name := c.Params("id") - errPrefix := fmt.Sprintf("failed to execute test workflow '%s'", name) - workflow, err := s.TestWorkflowsClient.Get(name) - if err != nil { - return s.ClientError(c, errPrefix, err) + selector := c.Query("selector") + s.Log.Debugw("getting test workflow", "name", name, "selector", selector) + + errPrefix := "failed to execute test workflow" + + var testWorkflows []testworkflowsv1.TestWorkflow + if name != "" { + errPrefix = errPrefix + " " + name + testWorkflow, err := s.TestWorkflowsClient.Get(name) + if err != nil { + return s.ClientError(c, errPrefix, err) + } + + testWorkflows = append(testWorkflows, *testWorkflow) + } else { + testWorkflowList, err := s.TestWorkflowsClient.List(selector) + if err != nil { + return s.ClientError(c, errPrefix, err) + } + + testWorkflows = append(testWorkflows, testWorkflowList.Items...) } // Load the execution request @@ -371,34 +388,32 @@ func (s *TestkubeAPI) ExecuteTestWorkflowHandler() fiber.Handler { var results []testkube.TestWorkflowExecution var errs []error - request.TestWorkflowExecutionName = strings.Clone(c.Query("testWorkflowExecutionName")) - concurrencyLevel := scheduler.DefaultConcurrencyLevel - workerpoolService := workerpool.New[testworkflowsv1.TestWorkflow, testkube.TestWorkflowExecutionRequest, - testkube.TestWorkflowExecution](concurrencyLevel) - requests := []workerpool.Request[testworkflowsv1.TestWorkflow, testkube.TestWorkflowExecutionRequest, testkube.TestWorkflowExecution]{ - { - Object: *workflow, - Options: request, - ExecFn: s.TestWorkflowExecutor.Execute, - }, - } - - go workerpoolService.SendRequests(requests) - go workerpoolService.Run(ctx) - - for r := range workerpoolService.GetResponses() { - results = append(results, r.Result) - if r.Err != nil { - errs = append(errs, r.Err) + if len(testWorkflows) != 0 { + request.TestWorkflowExecutionName = strings.Clone(c.Query("testWorkflowExecutionName")) + workerpoolService := workerpool.New[testworkflowsv1.TestWorkflow, testkube.TestWorkflowExecutionRequest, testkube.TestWorkflowExecution](scheduler.DefaultConcurrencyLevel) + + go workerpoolService.SendRequests(s.prepareTestWorkflowRequests(testWorkflows, request)) + go workerpoolService.Run(ctx) + + for r := range workerpoolService.GetResponses() { + results = append(results, r.Result) + if r.Err != nil { + errs = append(errs, r.Err) + } } } if len(errs) != 0 { - return s.InternalError(c, errPrefix, "execution error", errs[0]) + return s.InternalError(c, errPrefix, "execution error", errors.Join(errs...)) } + s.Log.Debugw("executing test workflow", "name", name, "selector", selector) if len(results) != 0 { - return c.JSON(results[0]) + if name != "" { + return c.JSON(results[0]) + } + + return c.JSON(results) } return s.InternalError(c, errPrefix, "error", errors.New("no execution results")) @@ -423,3 +438,20 @@ func (s *TestkubeAPI) getFilteredTestWorkflowList(c *fiber.Ctx) (*testworkflowsv return crWorkflows, nil } + +func (s *TestkubeAPI) prepareTestWorkflowRequests(work []testworkflowsv1.TestWorkflow, request testkube.TestWorkflowExecutionRequest) []workerpool.Request[ + testworkflowsv1.TestWorkflow, + testkube.TestWorkflowExecutionRequest, + testkube.TestWorkflowExecution, +] { + requests := make([]workerpool.Request[testworkflowsv1.TestWorkflow, testkube.TestWorkflowExecutionRequest, testkube.TestWorkflowExecution], len(work)) + for i := range work { + requests[i] = workerpool.Request[testworkflowsv1.TestWorkflow, testkube.TestWorkflowExecutionRequest, testkube.TestWorkflowExecution]{ + Object: work[i], + Options: request, + ExecFn: s.TestWorkflowExecutor.Execute, + } + } + + return requests +} diff --git a/pkg/api/v1/client/interface.go b/pkg/api/v1/client/interface.go index 554a3faaf6..82f1357377 100644 --- a/pkg/api/v1/client/interface.go +++ b/pkg/api/v1/client/interface.go @@ -151,6 +151,7 @@ type TestWorkflowAPI interface { UpdateTestWorkflow(workflow testkube.TestWorkflow) (testkube.TestWorkflow, error) DeleteTestWorkflow(name string) error ExecuteTestWorkflow(name string, request testkube.TestWorkflowExecutionRequest) (testkube.TestWorkflowExecution, error) + ExecuteTestWorkflows(selector string, request testkube.TestWorkflowExecutionRequest) ([]testkube.TestWorkflowExecution, error) GetTestWorkflowExecutionNotifications(id string) (chan testkube.TestWorkflowExecutionNotification, error) GetTestWorkflowExecutionLogs(id string) ([]byte, error) } diff --git a/pkg/api/v1/client/testworkflow.go b/pkg/api/v1/client/testworkflow.go index 3c929be660..d878f93565 100644 --- a/pkg/api/v1/client/testworkflow.go +++ b/pkg/api/v1/client/testworkflow.go @@ -121,6 +121,23 @@ func (c TestWorkflowClient) ExecuteTestWorkflow(name string, request testkube.Te return c.testWorkflowExecutionTransport.Execute(http.MethodPost, uri, body, nil) } +// ExecuteTestWorkflows starts new external test workflow executions, reads data and returns IDs +// Executions are started asynchronously client can check later for results +func (c TestWorkflowClient) ExecuteTestWorkflows(selector string, request testkube.TestWorkflowExecutionRequest) (executions []testkube.TestWorkflowExecution, err error) { + uri := c.testWorkflowExecutionTransport.GetURI("/test-workflow-executions") + + body, err := json.Marshal(request) + if err != nil { + return executions, err + } + + params := map[string]string{ + "selector": selector, + } + + return c.testWorkflowExecutionTransport.ExecuteMultiple(http.MethodPost, uri, body, params) +} + // GetTestWorkflowExecutionNotifications returns events stream from job pods, based on job pods logs func (c TestWorkflowClient) GetTestWorkflowExecutionNotifications(id string) (notifications chan testkube.TestWorkflowExecutionNotification, err error) { notifications = make(chan testkube.TestWorkflowExecutionNotification) diff --git a/pkg/api/v1/testkube/model_test_workflow_step_execute_test_workflow_ref.go b/pkg/api/v1/testkube/model_test_workflow_step_execute_test_workflow_ref.go index c2bcd5b1d1..be1e4454f7 100644 --- a/pkg/api/v1/testkube/model_test_workflow_step_execute_test_workflow_ref.go +++ b/pkg/api/v1/testkube/model_test_workflow_step_execute_test_workflow_ref.go @@ -23,5 +23,6 @@ type TestWorkflowStepExecuteTestWorkflowRef struct { // matrix of parameters to spawn instances Matrix map[string]interface{} `json:"matrix,omitempty"` // parameters that should be distributed across sharded instances - Shards map[string]interface{} `json:"shards,omitempty"` + Shards map[string]interface{} `json:"shards,omitempty"` + Selector *LabelSelector `json:"selector,omitempty"` } diff --git a/pkg/mapper/testworkflows/kube_openapi.go b/pkg/mapper/testworkflows/kube_openapi.go index c0664f9d93..9076451da8 100644 --- a/pkg/mapper/testworkflows/kube_openapi.go +++ b/pkg/mapper/testworkflows/kube_openapi.go @@ -806,6 +806,21 @@ func MapStepExecuteTestKubeToAPI(v testworkflowsv1.StepExecuteTest) testkube.Tes } } +func MapLabelSelectorRequirementToAPI(v metav1.LabelSelectorRequirement) testkube.LabelSelectorRequirement { + return testkube.LabelSelectorRequirement{ + Key: v.Key, + Operator: string(v.Operator), + Values: v.Values, + } +} + +func MapSelectorToAPI(v metav1.LabelSelector) testkube.LabelSelector { + return testkube.LabelSelector{ + MatchLabels: v.MatchLabels, + MatchExpressions: common.MapSlice(v.MatchExpressions, MapLabelSelectorRequirementToAPI), + } +} + func MapStepExecuteTestWorkflowKubeToAPI(v testworkflowsv1.StepExecuteWorkflow) testkube.TestWorkflowStepExecuteTestWorkflowRef { return testkube.TestWorkflowStepExecuteTestWorkflowRef{ Name: v.Name, @@ -817,6 +832,7 @@ func MapStepExecuteTestWorkflowKubeToAPI(v testworkflowsv1.StepExecuteWorkflow) MaxCount: MapIntOrStringToBoxedString(v.MaxCount), Matrix: MapDynamicListMapKubeToAPI(v.Matrix), Shards: MapDynamicListMapKubeToAPI(v.Shards), + Selector: common.MapPtr(v.Selector, MapSelectorToAPI), } } diff --git a/pkg/mapper/testworkflows/openapi_kube.go b/pkg/mapper/testworkflows/openapi_kube.go index 844e6da049..86b9bf7ede 100644 --- a/pkg/mapper/testworkflows/openapi_kube.go +++ b/pkg/mapper/testworkflows/openapi_kube.go @@ -846,6 +846,21 @@ func MapStepExecuteTestAPIToKube(v testkube.TestWorkflowStepExecuteTestRef) test } } +func MapLabelSelectorRequirementToCRD(v testkube.LabelSelectorRequirement) metav1.LabelSelectorRequirement { + return metav1.LabelSelectorRequirement{ + Key: v.Key, + Operator: metav1.LabelSelectorOperator(v.Operator), + Values: v.Values, + } +} + +func MapSelectorToCRD(v testkube.LabelSelector) metav1.LabelSelector { + return metav1.LabelSelector{ + MatchLabels: v.MatchLabels, + MatchExpressions: common.MapSlice(v.MatchExpressions, MapLabelSelectorRequirementToCRD), + } +} + func MapStepExecuteTestWorkflowAPIToKube(v testkube.TestWorkflowStepExecuteTestWorkflowRef) testworkflowsv1.StepExecuteWorkflow { return testworkflowsv1.StepExecuteWorkflow{ Name: v.Name, @@ -859,6 +874,7 @@ func MapStepExecuteTestWorkflowAPIToKube(v testkube.TestWorkflowStepExecuteTestW Matrix: MapDynamicListMapAPIToKube(v.Matrix), Shards: MapDynamicListMapAPIToKube(v.Shards), }, + Selector: common.MapPtr(v.Selector, MapSelectorToCRD), } } From 71b0482a674181d413f46834a3d2e9cb6be8ed9a Mon Sep 17 00:00:00 2001 From: ypoplavs <45286051+ypoplavs@users.noreply.github.com> Date: Tue, 19 Nov 2024 14:02:30 +0200 Subject: [PATCH 06/30] add dispatch to release ci (#6037) --- .../docker-build-api-executors-tag.yaml | 29 +++++-------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/.github/workflows/docker-build-api-executors-tag.yaml b/.github/workflows/docker-build-api-executors-tag.yaml index c416842072..5042d860de 100644 --- a/.github/workflows/docker-build-api-executors-tag.yaml +++ b/.github/workflows/docker-build-api-executors-tag.yaml @@ -942,25 +942,10 @@ jobs: with: strip_v: false - - name: Editing helm-release repo with version based on a Tag pushed. - run: | - - # Setting up Git: - git clone https://kubeshop-bot:$GH_TOKEN@github.com/kubeshop/helm-charts - cd ./helm-charts - git config user.name "kubeshop-bot" - git config user.email "kubeshop-bot@kubeshop.io" - - export GH_TOKEN - export RELEASE_VERSION - export SERVICE - - git fetch origin main - git checkout main - cd ./scripts - ./chart_releaser.sh --helm-chart-folder testkube-$SERVICE - - env: - SERVICE: api - GH_TOKEN: ${{ secrets.CI_BOT_TOKEN }} - RELEASE_VERSION: ${{ steps.tag.outputs.tag }} + - name: Repository Dispatch + uses: peter-evans/repository-dispatch@v3 + with: + token: ${{ secrets.CI_BOT_TOKEN }} + repository: kubeshop/helm-charts + event-type: release-testkube-api-charts + client-payload: '{"RELEASE_VERSION": "${{ steps.tag.outputs.tag }}", "SERVICE": "api"}' From 54a3f3d51ecce88b54d2eeb0c7662160ba682a5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 13:48:56 +0100 Subject: [PATCH 07/30] build(deps): bump github.com/cli/cli/v2 from 2.20.2 to 2.62.0 (#6030) Bumps [github.com/cli/cli/v2](https://github.com/cli/cli) from 2.20.2 to 2.62.0. - [Release notes](https://github.com/cli/cli/releases) - [Changelog](https://github.com/cli/cli/blob/trunk/.goreleaser.yml) - [Commits](https://github.com/cli/cli/compare/v2.20.2...v2.62.0) --- updated-dependencies: - dependency-name: github.com/cli/cli/v2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 85 ++++----- go.sum | 557 ++++++++++++--------------------------------------------- 2 files changed, 156 insertions(+), 486 deletions(-) diff --git a/go.mod b/go.mod index 2d1b1bf494..531fe38009 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/avast/retry-go/v4 v4.6.0 github.com/bmatcuk/doublestar/v4 v4.6.1 github.com/cdevents/sdk-go v0.3.0 - github.com/cli/cli/v2 v2.20.2 + github.com/cli/cli/v2 v2.62.0 github.com/cloudevents/sdk-go/v2 v2.15.2 github.com/coreos/go-oidc v2.2.1+incompatible github.com/creasty/defaults v1.7.0 @@ -19,22 +19,22 @@ require ( github.com/dustinkirkland/golang-petname v0.0.0-20191129215211-8e5a1ed0cff0 github.com/fasthttp/websocket v1.5.0 github.com/fluxcd/pkg/apis/event v0.2.0 - github.com/fsnotify/fsnotify v1.6.0 - github.com/gabriel-vasile/mimetype v1.4.1 + github.com/fsnotify/fsnotify v1.7.0 + github.com/gabriel-vasile/mimetype v1.4.6 github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 github.com/gofiber/adaptor/v2 v2.1.29 github.com/gofiber/fiber/v2 v2.52.5 github.com/gofiber/websocket/v2 v2.1.1 github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.6.0 - github.com/google/go-containerregistry v0.20.1 + github.com/google/go-containerregistry v0.20.2 github.com/google/uuid v1.6.0 github.com/gookit/color v1.5.4 - github.com/gorilla/websocket v1.5.0 + github.com/gorilla/websocket v1.5.3 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/h2non/filetype v1.1.3 github.com/hashicorp/golang-lru/v2 v2.0.1 - github.com/itchyny/gojq v0.12.14 + github.com/itchyny/gojq v0.12.15 github.com/joshdk/go-junit v1.0.0 github.com/json-iterator/go v1.1.12 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 @@ -60,15 +60,15 @@ require ( github.com/shirou/gopsutil/v3 v3.24.3 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/slack-go/slack v0.12.2 - github.com/spf13/afero v1.10.0 - github.com/spf13/cobra v1.7.0 + github.com/spf13/afero v1.11.0 + github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.9.0 github.com/valyala/fasthttp v1.51.0 github.com/vektah/gqlparser/v2 v2.5.2-0.20230422221642-25e09f9d292d github.com/wI2L/jsondiff v0.6.0 - go.mongodb.org/mongo-driver v1.11.3 - go.uber.org/zap v1.26.0 - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 + go.mongodb.org/mongo-driver v1.14.0 + go.uber.org/zap v1.27.0 + golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 golang.org/x/oauth2 v0.22.0 golang.org/x/sync v0.8.0 golang.org/x/text v0.19.0 @@ -89,29 +89,32 @@ require ( atomicgo.dev/cursor v0.2.0 // indirect atomicgo.dev/keyboard v0.2.9 // indirect atomicgo.dev/schedule v0.1.0 // indirect - github.com/AlecAivazis/survey/v2 v2.3.6 // indirect + github.com/AlecAivazis/survey/v2 v2.3.7 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect - github.com/alecthomas/chroma v0.10.0 // indirect + github.com/alecthomas/chroma/v2 v2.8.0 // indirect github.com/andybalholm/brotli v1.0.5 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect github.com/briandowns/spinner v1.18.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/charmbracelet/glamour v0.5.1-0.20220727184942-e70ff2d969da // indirect - github.com/cli/browser v1.1.0 // indirect - github.com/cli/go-gh v0.1.3-0.20221102170023-e3ec45fb1d1b // indirect - github.com/cli/safeexec v1.0.0 // indirect - github.com/cli/shurcooL-graphql v0.0.2 // indirect + github.com/charmbracelet/glamour v0.7.0 // indirect + github.com/charmbracelet/lipgloss v0.10.1-0.20240413172830-d0be07ea6b9c // indirect + github.com/charmbracelet/x/exp/term v0.0.0-20240425164147-ba2a9512b05f // indirect + github.com/cli/browser v1.3.0 // indirect + github.com/cli/go-gh/v2 v2.11.0 // indirect + github.com/cli/safeexec v1.0.1 // indirect + github.com/cli/shurcooL-graphql v0.0.4 // indirect github.com/containerd/console v1.0.3 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/distribution/reference v0.6.0 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect - github.com/docker/cli v24.0.0+incompatible // indirect + github.com/docker/cli v27.1.1+incompatible // indirect github.com/docker/distribution v2.8.2+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/docker/go-connections v0.4.0 // indirect @@ -119,15 +122,15 @@ require ( github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.7.0 // indirect - github.com/fatih/color v1.15.0 // indirect + github.com/fatih/color v1.16.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-errors/errors v1.5.1 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect - github.com/go-openapi/jsonpointer v0.20.0 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-playground/validator/v10 v10.11.1 // indirect @@ -140,7 +143,7 @@ require ( github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gorilla/css v1.0.0 // indirect - github.com/henvic/httpretty v0.0.6 // indirect + github.com/henvic/httpretty v0.1.4 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/itchyny/timefmt-go v0.1.5 // indirect @@ -157,7 +160,7 @@ require ( github.com/mattn/go-runewidth v0.0.15 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect - github.com/microcosm-cc/bluemonday v1.0.20 // indirect + github.com/microcosm-cc/bluemonday v1.0.26 // indirect github.com/minio/highwayhash v1.0.2 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/sha256-simd v1.0.0 // indirect @@ -168,29 +171,29 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/muesli/reflow v0.3.0 // indirect - github.com/muesli/termenv v0.12.0 // indirect + github.com/muesli/termenv v0.15.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nats-io/jwt/v2 v2.5.7 // indirect github.com/nats-io/nkeys v0.4.7 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc3 // indirect + github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/package-url/packageurl-go v0.1.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/pquerna/cachecontrol v0.2.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.45.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect - github.com/rivo/uniseg v0.4.4 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/rs/xid v1.4.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 // indirect github.com/savsgio/gotils v0.0.0-20211223103454-d0aaa54c5899 // indirect github.com/segmentio/backo-go v1.0.0 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect - github.com/shurcooL/githubv4 v0.0.0-20220520033151-0b4e3294ff00 // indirect - github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29 // indirect + github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc // indirect + github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e // indirect @@ -205,13 +208,13 @@ require ( github.com/valyala/tcplisten v1.0.0 // indirect github.com/vbatts/tar-split v0.11.3 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect - github.com/xdg-go/scram v1.1.1 // indirect - github.com/xdg-go/stringprep v1.0.3 // indirect + github.com/xdg-go/scram v1.1.2 // indirect + github.com/xdg-go/stringprep v1.0.4 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect - github.com/yuin/goldmark v1.4.13 // indirect - github.com/yuin/goldmark-emoji v1.0.1 // indirect + github.com/yuin/goldmark v1.5.4 // indirect + github.com/yuin/goldmark-emoji v1.0.2 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect go.opentelemetry.io/otel v1.31.0 // indirect @@ -221,20 +224,20 @@ require ( go.opentelemetry.io/otel/trace v1.31.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.28.0 // indirect - golang.org/x/mod v0.17.0 // indirect + golang.org/x/mod v0.21.0 // indirect golang.org/x/net v0.30.0 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/term v0.25.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/tools v0.26.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.66.6 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect k8s.io/apiextensions-apiserver v0.28.3 // indirect k8s.io/component-base v0.28.3 // indirect - k8s.io/klog/v2 v2.100.1 // indirect + k8s.io/klog/v2 v2.120.1 // indirect k8s.io/kube-openapi v0.0.0-20230918164632-68afd615200d // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect diff --git a/go.sum b/go.sum index a16cd51848..cd93994055 100644 --- a/go.sum +++ b/go.sum @@ -7,52 +7,14 @@ atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtE atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs= atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/99designs/gqlgen v0.17.27 h1:XPsaZiWY1lL2qqVYtBt37GzkyX7bBiVvda7k1buC/Ao= github.com/99designs/gqlgen v0.17.27/go.mod h1:i4rEatMrzzu6RXaHydq1nmEPZkb3bKQsnxNRHS4DQB4= -github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw= -github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI= +github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= +github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= 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/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= @@ -75,8 +37,14 @@ github.com/adhocore/gronx v1.6.3/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiR github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= -github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= -github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= +github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= +github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/chroma/v2 v2.8.0 h1:w9WJUjFFmHHB2e8mRpL9jjy3alYDlU0QLDezj1xE264= +github.com/alecthomas/chroma/v2 v2.8.0/go.mod h1:yrkMI9807G1ROx13fhe1v6PN2DDeaR73L3d+1nmYQtw= +github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= +github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0= +github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= @@ -89,6 +57,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinRJA= github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -107,27 +77,29 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/charmbracelet/glamour v0.5.1-0.20220727184942-e70ff2d969da h1:FGz53GWQRiKQ/5xUsoCCkewSQIC7u81Scaxx2nUy3nM= -github.com/charmbracelet/glamour v0.5.1-0.20220727184942-e70ff2d969da/go.mod h1:HXz79SMFnF9arKxqeoHWxmo1BhplAH7wehlRhKQIL94= +github.com/charmbracelet/glamour v0.7.0 h1:2BtKGZ4iVJCDfMF229EzbeR1QRKLWztO9dMtjmqZSng= +github.com/charmbracelet/glamour v0.7.0/go.mod h1:jUMh5MeihljJPQbJ/wf4ldw2+yBP59+ctV36jASy7ps= +github.com/charmbracelet/lipgloss v0.10.1-0.20240413172830-d0be07ea6b9c h1:0FwZb0wTiyalb8QQlILWyIuh3nF5wok6j9D9oUQwfQY= +github.com/charmbracelet/lipgloss v0.10.1-0.20240413172830-d0be07ea6b9c/go.mod h1:EPP2QJ0ectp3zo6gx9f8oJGq8keirqPJ3XpYEI8wrrs= +github.com/charmbracelet/x/exp/term v0.0.0-20240425164147-ba2a9512b05f h1:1BXkZqDueTOBECyDoFGRi0xMYgjJ6vvoPIkWyKOwzTc= +github.com/charmbracelet/x/exp/term v0.0.0-20240425164147-ba2a9512b05f/go.mod h1:yQqGHmheaQfkqiJWjklPHVAq1dKbk8uGbcoS/lcKCJ0= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cli/browser v1.1.0 h1:xOZBfkfY9L9vMBgqb1YwRirGu6QFaQ5dP/vXt5ENSOY= -github.com/cli/browser v1.1.0/go.mod h1:HKMQAt9t12kov91Mn7RfZxyJQQgWgyS/3SZswlZ5iTI= -github.com/cli/cli/v2 v2.20.2 h1:w2dntZE09NvvH/IETHh95aKLatRtxRSomipL/kIqQOg= -github.com/cli/cli/v2 v2.20.2/go.mod h1:wZMsxgLyu4e2ja4bfX+Sxi44b0zSiakuc2qpC7DAdZQ= -github.com/cli/go-gh v0.1.3-0.20221102170023-e3ec45fb1d1b h1:W17Cf1UmOvLPbrHcFs9InoY3VPxC0TJWx3QwnjnD4TY= -github.com/cli/go-gh v0.1.3-0.20221102170023-e3ec45fb1d1b/go.mod h1:bqxLdCoTZ73BuiPEJx4olcO/XKhVZaFDchFagYRBweE= -github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI= -github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= -github.com/cli/shurcooL-graphql v0.0.2 h1:rwP5/qQQ2fM0TzkUTwtt6E2LbIYf6R+39cUXTa04NYk= -github.com/cli/shurcooL-graphql v0.0.2/go.mod h1:tlrLmw/n5Q/+4qSvosT+9/W5zc8ZMjnJeYBxSdb4nWA= +github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo= +github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk= +github.com/cli/cli/v2 v2.62.0 h1:djXx4/6kJvS8/KSAYzkXQvAmUby5agYt8u9PkInkmd4= +github.com/cli/cli/v2 v2.62.0/go.mod h1:lFlAu9/wJZJFfPbpKYB1YK8L3HmaXF87NNk2mLRRFvs= +github.com/cli/go-gh/v2 v2.11.0 h1:TERLYMMWderKBO3lBff/JIu2+eSly2oFRgN2WvO+3eA= +github.com/cli/go-gh/v2 v2.11.0/go.mod h1:MeRoKzXff3ygHu7zP+NVTT+imcHW6p3tpuxHAzRM2xE= +github.com/cli/safeexec v1.0.1 h1:e/C79PbXF4yYTN/wauC4tviMxEV13BwljGj0N9j+N00= +github.com/cli/safeexec v1.0.1/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= +github.com/cli/shurcooL-graphql v0.0.4 h1:6MogPnQJLjKkaXPyGqPRXOI2qCsQdqNfUY1QSJu2GuY= +github.com/cli/shurcooL-graphql v0.0.4/go.mod h1:3waN4u02FiZivIV+p1y4d0Jo1jc6BViMA73C+sZo2fk= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudevents/sdk-go/v2 v2.15.2 h1:54+I5xQEnI73RBhWHxbI1XJcqOFOVJN85vb41+8mHUc= github.com/cloudevents/sdk-go/v2 v2.15.2/go.mod h1:lL7kSWAE/V8VI4Wh0jbL2v/jvqsm6tjmaQBSvxcv4uE= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= @@ -136,17 +108,22 @@ github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSk github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/creasty/defaults v1.7.0 h1:eNdqZvc5B509z18lD8yc212CAqJNvfT1Jq6L8WowdBA= github.com/creasty/defaults v1.7.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= +github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8GQs= +github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ= github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= @@ -155,8 +132,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/docker/cli v24.0.0+incompatible h1:0+1VshNwBQzQAx9lOl+OYCTCEAD8fKs/qeXMx3O0wqM= -github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE= +github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI= @@ -176,8 +153,6 @@ github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRr github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -186,24 +161,20 @@ github.com/evanphx/json-patch/v5 v5.7.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq github.com/fasthttp/websocket v1.5.0 h1:B4zbe3xXyvIdnqjOZrafVFklCUq5ZLo/TqCt5JA1wLE= github.com/fasthttp/websocket v1.5.0/go.mod h1:n0BlOQvJdPbTuBkZT0O5+jk/sp/1/VCzquR1BehI2F4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fluxcd/pkg/apis/event v0.2.0 h1:cmAtkZfoEaNVYegI4SFM8XstdRAil3O9AoP+8fpbR34= github.com/fluxcd/pkg/apis/event v0.2.0/go.mod h1:OyzKqs90J+MK7rQaEOFMMCkALpPkfmxlkabgyY2wSFQ= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/gabriel-vasile/mimetype v1.4.1 h1:TRWk7se+TOjCYgRth7+1/OYLNiRNIotknkFtf/dnN7Q= -github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M= +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/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc= +github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc= github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -213,14 +184,12 @@ github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= -github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= -github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= @@ -234,6 +203,8 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofiber/adaptor/v2 v2.1.29 h1:JnYd6fbqVM9D4zPchk+kg89PfxyuKqZKhBWGQDHfKH4= github.com/gofiber/adaptor/v2 v2.1.29/go.mod h1:z4mAV9mMsUgIEVGGS5Ii6ZMTJq4VdV1KWL1JAbsZdUA= github.com/gofiber/fiber/v2 v2.39.0/go.mod h1:Cmuu+elPYGqlvQvdKyjtYsjGMi69PDp8a1AY2I5B2gM= @@ -244,89 +215,41 @@ github.com/gofiber/websocket/v2 v2.1.1/go.mod h1:F0ES7DhlFrNyHtC2UGey2KYI+zdqIUR github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/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.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.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-containerregistry v0.20.1 h1:eTgx9QNYugV4DN5mz4U8hiAGTi1ybXn0TPi4Smd8du0= -github.com/google/go-containerregistry v0.20.1/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI= +github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo= +github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8= 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/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= @@ -334,8 +257,8 @@ github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/Q github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= @@ -345,22 +268,21 @@ github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4= github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/henvic/httpretty v0.0.6 h1:JdzGzKZBajBfnvlMALXXMVQWxWMF/ofTy8C3/OSUTxs= -github.com/henvic/httpretty v0.0.6/go.mod h1:X38wLjWXHkXT7r2+uK8LjCMne9rsuNaBLJ+5cU2/Pmo= +github.com/henvic/httpretty v0.1.4 h1:Jo7uwIRWVFxkqOnErcoYfH90o3ddQyVrSANeS4cxYmU= +github.com/henvic/httpretty v0.1.4/go.mod h1:Dn60sQTZfbt2dYsdUSNsCljyF4AfdqnuJFDLJA1I4AM= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/itchyny/gojq v0.12.14 h1:6k8vVtsrhQSYgSGg827AD+PVVaB1NLXEdX+dda2oZCc= -github.com/itchyny/gojq v0.12.14/go.mod h1:y1G7oO7XkcR1LPZO59KyoCRy08T3j9vDYRV0GgYSS+s= +github.com/itchyny/gojq v0.12.15 h1:WC1Nxbx4Ifw5U2oQWACYz32JK8G9qxNtHzrvW4KEcqI= +github.com/itchyny/gojq v0.12.15/go.mod h1:uWAHCbCIla1jiNxmeT5/B5mOjSdfkCq6p8vxWg+BM10= github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE= github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -369,15 +291,12 @@ github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.14.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= @@ -390,7 +309,6 @@ github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuOb github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= @@ -418,11 +336,9 @@ github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= @@ -434,9 +350,8 @@ github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQth github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/microcosm-cc/bluemonday v1.0.19/go.mod h1:QNzV2UbLK2/53oIIwTOyLUSABMkjZ4tqiyC1g/DyqxE= -github.com/microcosm-cc/bluemonday v1.0.20 h1:flpzsq4KU3QIYAYGV/szUat7H+GPOXR0B2JU5A1Wp8Y= -github.com/microcosm-cc/bluemonday v1.0.20/go.mod h1:yfBmMi8mxvaZut3Yytv+jTXRY8mxyjJ0/kQBTElld50= +github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58= +github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= @@ -462,7 +377,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.6.6 h1:Duep6KMIDpY4Yo11iFsvyqJDyfzLF9+sndUKT+v64GQ= github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/moogar0880/problems v0.1.1 h1:bktLhq8NDG/czU2ZziYNigBFksx13RaYe5AVdNmHDT4= @@ -471,9 +385,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= -github.com/muesli/termenv v0.11.0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= -github.com/muesli/termenv v0.12.0 h1:KuQRUE3PgxRFWhq4gHvZtPSLCGDqM5q/cYr1pZ39ytc= -github.com/muesli/termenv v0.12.0/go.mod h1:WCCv32tusQ/EEZ5S8oUIIrC/nIuBcxCVqlN4Xfkv+7A= +github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/nats-io/jwt/v2 v2.5.7 h1:j5lH1fUXCnJnY8SsQeB/a/z9Azgu2bYIDvtPVNdxe2c= @@ -490,7 +403,6 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/olekukonko/tablewriter v0.0.6-0.20230925090304-df64c4bbad77 h1:3bMMZ1f+GPXFQ1uNaYbO/uECWvSfqEA+ZEXn1rFAT88= github.com/olekukonko/tablewriter v0.0.6-0.20230925090304-df64c4bbad77/go.mod h1:8Hf+pH6thup1sPZPD+NLg7d6vbpsdilu9CPIeikvgMQ= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= @@ -501,8 +413,8 @@ github.com/onsi/gomega v1.31.0 h1:54UJxxj6cPInHS3a35wm6BK/F9nHYueZ1NVujHDrnXE= github.com/onsi/gomega v1.31.0/go.mod h1:DW9aCi7U6Yi40wNVAvT6kzFnEVEI5n3DloYBiKiT6zk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= -github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/otiai10/copy v1.11.0 h1:OKBD80J/mLBrwnzXqGtFCzprFSGioo30JcmR4APsNwc= github.com/otiai10/copy v1.11.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= @@ -514,9 +426,9 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/pquerna/cachecontrol v0.2.0 h1:vBXSNuE5MYP9IJ5kjsdo8uq+w41jSPgvba2DEnkRx9k= @@ -541,15 +453,14 @@ github.com/pterm/pterm v0.12.79 h1:lH3yrYMhdpeqX9y5Ep1u7DejyHy7NSQg9qrBjF9dFT4= github.com/pterm/pterm v0.12.79/go.mod h1:1v/gzOF1N0FsjbgTHZ1wVycRkKiatFvJSJC4IGaQAAo= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -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/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -573,10 +484,10 @@ github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFt github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= -github.com/shurcooL/githubv4 v0.0.0-20220520033151-0b4e3294ff00 h1:fiFvD4lT0aWjuuAb64LlZ/67v87m+Kc9Qsu5cMFNK0w= -github.com/shurcooL/githubv4 v0.0.0-20220520033151-0b4e3294ff00/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo= -github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29 h1:B1PEwpArrNp4dkQrfxh/abbBAOZBVp0ds+fBEOUOqOc= -github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg= +github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc h1:vH0NQbIDk+mJLvBliNGfcQgUmhlniWBDXC79oRxfZA0= +github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8= +github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0= +github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= @@ -585,10 +496,10 @@ github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EE github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ= github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= -github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= -github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= 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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -599,7 +510,6 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -615,7 +525,6 @@ github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= @@ -644,10 +553,10 @@ github.com/wI2L/jsondiff v0.6.0 h1:zrsH3FbfVa3JO9llxrcDy/XLkYPLgoMX6Mz3T2PP2AI= github.com/wI2L/jsondiff v0.6.0/go.mod h1:D6aQ5gKgPF9g17j+E9N7aasmU1O+XvfmWm1y8UMmNpw= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= -github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= -github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= -github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= @@ -657,26 +566,21 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRT github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.4/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg= -github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= +github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os= -github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= +github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU= +github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s= +github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.mongodb.org/mongo-driver v1.11.3 h1:Ql6K6qYHEzB6xvu4+AU0BoRoqf9vFPcc4o7MUIdPW8Y= -go.mongodb.org/mongo-driver v1.11.3/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +github.com/zalando/go-keyring v0.2.5 h1:Bc2HHpjALryKD62ppdEzaFG6VxL6Bc+5v0LYpN8Lba8= +github.com/zalando/go-keyring v0.2.5/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk= +go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= +go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= @@ -695,131 +599,62 @@ go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeX go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -829,65 +664,32 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/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-20220704084225-05e143d24a9e/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.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -897,189 +699,62 @@ golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 h1:9NWlQfY2ePejTmfwUH1OWwmznFa+0kKcHGPDvcPza9M= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg= google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M= google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc= google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= @@ -1094,8 +769,8 @@ gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= 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.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI= -gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +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/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -1112,12 +787,7 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY= k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0= k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08= @@ -1128,15 +798,12 @@ k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY= k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4= k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI= k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8= -k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= -k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20230918164632-68afd615200d h1:/CFeJBjBrZvHX09rObS2+2iEEDevMWYc1v3aIYAjIYI= k8s.io/kube-openapi v0.0.0-20230918164632-68afd615200d/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4= sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= From 15ddfbe832387b71f6e05b8b5eb5c191cfafa7e2 Mon Sep 17 00:00:00 2001 From: Jacek Wysocki Date: Tue, 19 Nov 2024 22:41:33 +0100 Subject: [PATCH 08/30] fix: conflicting subscription name with control plane (#6042) * fix: conflicting subscription name with control plane * fix: events to agentevents refactor in whole codebase --- internal/graphql/services/executors.go | 2 +- internal/graphql/services/executors_test.go | 2 +- pkg/api/v1/testkube/model_event_extended.go | 10 +++++----- pkg/api/v1/testkube/model_event_extended_test.go | 6 +++--- pkg/event/bus/nats.go | 2 +- pkg/event/emitter.go | 2 +- pkg/logs/client/interface.go | 4 ++-- pkg/logs/events.go | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/internal/graphql/services/executors.go b/internal/graphql/services/executors.go index e2254cddf2..a515b42851 100644 --- a/internal/graphql/services/executors.go +++ b/internal/graphql/services/executors.go @@ -32,7 +32,7 @@ func (s *executorsService) List(selector string) ([]testkube.ExecutorDetails, er } func (s *executorsService) SubscribeList(ctx context.Context, selector string) (<-chan []testkube.ExecutorDetails, error) { - return HandleSubscription(ctx, "events.executor.>", s, func() ([]testkube.ExecutorDetails, error) { + return HandleSubscription(ctx, "agentevents.executor.>", s, func() ([]testkube.ExecutorDetails, error) { return s.List(selector) }) } diff --git a/internal/graphql/services/executors_test.go b/internal/graphql/services/executors_test.go index aae22813a4..df45d7f7ea 100644 --- a/internal/graphql/services/executors_test.go +++ b/internal/graphql/services/executors_test.go @@ -169,7 +169,7 @@ func TestExecutorsService_SubscribeList(t *testing.T) { assert.NoError(t, err) <-ch client.Client = getMockExecutorClient(k8sObjects2).Client - assert.NoError(t, srvMock.BusMock().PublishTopic("events.executor.create", testkube.Event{ + assert.NoError(t, srvMock.BusMock().PublishTopic("agentevents.executor.create", testkube.Event{ Type_: testkube.EventCreated, Resource: testkube.EventResourceExecutor, })) diff --git a/pkg/api/v1/testkube/model_event_extended.go b/pkg/api/v1/testkube/model_event_extended.go index e9415a386e..af7a0d2e3c 100644 --- a/pkg/api/v1/testkube/model_event_extended.go +++ b/pkg/api/v1/testkube/model_event_extended.go @@ -8,8 +8,8 @@ import ( ) const ( - TestStartSubject = "events.test.start" - TestStopSubject = "events.test.stop" + TestStartSubject = "agentevents.test.start" + TestStopSubject = "agentevents.test.stop" ) // check if Event implements model generic event type @@ -270,14 +270,14 @@ func (e Event) Topic() string { } if e.Resource == nil { - return "events.all" + return "agentevents.all" } if e.ResourceId == "" { - return "events." + string(*e.Resource) + return "agentevents." + string(*e.Resource) } - return "events." + string(*e.Resource) + "." + e.ResourceId + return "agentevents." + string(*e.Resource) + "." + e.ResourceId } // GetResourceId implmenents generic event trigger diff --git a/pkg/api/v1/testkube/model_event_extended_test.go b/pkg/api/v1/testkube/model_event_extended_test.go index cf0da3bb5c..bb6845a63b 100644 --- a/pkg/api/v1/testkube/model_event_extended_test.go +++ b/pkg/api/v1/testkube/model_event_extended_test.go @@ -339,20 +339,20 @@ func TestEvent_Topic(t *testing.T) { t.Parallel() evt := Event{Type_: EventStartTest, Resource: nil} - assert.Equal(t, "events.all", evt.Topic()) + assert.Equal(t, "agentevents.all", evt.Topic()) }) t.Run("should return event topic with resource name and id if set", func(t *testing.T) { t.Parallel() evt := Event{Type_: EventStartTest, Resource: EventResourceExecutor, ResourceId: "a12"} - assert.Equal(t, "events.executor.a12", evt.Topic()) + assert.Equal(t, "agentevents.executor.a12", evt.Topic()) }) t.Run("should return event topic with resource name when id not set", func(t *testing.T) { t.Parallel() evt := Event{Type_: EventStartTest, Resource: EventResourceExecutor} - assert.Equal(t, "events.executor", evt.Topic()) + assert.Equal(t, "agentevents.executor", evt.Topic()) }) } diff --git a/pkg/event/bus/nats.go b/pkg/event/bus/nats.go index f652882cbb..e10fc563e2 100644 --- a/pkg/event/bus/nats.go +++ b/pkg/event/bus/nats.go @@ -23,7 +23,7 @@ var ( const ( SubscribeBuffer = 1 - SubscriptionName = "events" + SubscriptionName = "agentevents" InternalPublishTopic = "internal.all" InternalSubscribeTopic = "internal.>" ) diff --git a/pkg/event/emitter.go b/pkg/event/emitter.go index b814b0f22a..232537c983 100644 --- a/pkg/event/emitter.go +++ b/pkg/event/emitter.go @@ -155,7 +155,7 @@ func (e *Emitter) Listen(ctx context.Context) { } func (e *Emitter) startListener(l common.Listener) { - err := e.Bus.SubscribeTopic("events.>", l.Name(), e.notifyHandler(l)) + err := e.Bus.SubscribeTopic("agentevents.>", l.Name(), e.notifyHandler(l)) if err != nil { e.Log.Errorw("error while starting listener", "error", err) } diff --git a/pkg/logs/client/interface.go b/pkg/logs/client/interface.go index d942682f6c..d07c902df9 100644 --- a/pkg/logs/client/interface.go +++ b/pkg/logs/client/interface.go @@ -9,8 +9,8 @@ import ( const ( StreamPrefix = "log" - StartSubject = "events.logs.start" - StopSubject = "events.logs.stop" + StartSubject = "agentevents.logs.start" + StopSubject = "agentevents.logs.stop" ) //go:generate mockgen -destination=./mock_stream.go -package=client "github.com/kubeshop/testkube/pkg/logs/client" Stream diff --git a/pkg/logs/events.go b/pkg/logs/events.go index 712a5df1a5..7a2bfb3d56 100644 --- a/pkg/logs/events.go +++ b/pkg/logs/events.go @@ -27,8 +27,8 @@ const ( StartQueue = "logsstart" StopQueue = "logsstop" - LogStartSubject = "events.logs.start" - LogStopSubject = "events.logs.stop" + LogStartSubject = "agentevents.logs.start" + LogStopSubject = "agentevents.logs.stop" ) var ( From eb85115bda1ac283b3824ee221238602f40e8852 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 09:27:10 +0300 Subject: [PATCH 09/30] build(deps): bump github/codeql-action from 3.27.0 to 3.27.4 (#6034) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.27.0 to 3.27.4. - [Release notes](https://github.com/github/codeql-action/releases) - [Commits](https://github.com/github/codeql-action/compare/v3.27.0...v3.27.4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 75ba52f79a..91b651ca85 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -52,7 +52,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3.27.0 + uses: github/codeql-action/init@v3.27.4 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -82,4 +82,4 @@ jobs: test-${{ runner.os }}-go- - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3.27.0 + uses: github/codeql-action/analyze@v3.27.4 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index ea2c85e3af..97803f73ec 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -67,6 +67,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@b7cdb7fd39e52d1018799033ccd11eeb108aed85 # v2.25.3 + uses: github/codeql-action/upload-sarif@a1695c562bbfa68dc5ab58c9b5e9f616b52bf5be # v2.25.3 with: sarif_file: results.sarif From 6b5a0877da3a3a86c0c022c83674482505f98abf Mon Sep 17 00:00:00 2001 From: Dawid Rusnak Date: Wed, 20 Nov 2024 11:23:50 +0100 Subject: [PATCH 10/30] feat: add Prometheus/Grafana to Development Tool (#6044) --- cmd/tcl/kubectl-testkube/devbox/command.go | 37 +++++ .../devbox/devutils/grafana.go | 130 ++++++++++++++++++ .../devbox/devutils/prometheus.go | 98 +++++++++++++ 3 files changed, 265 insertions(+) create mode 100644 cmd/tcl/kubectl-testkube/devbox/devutils/grafana.go create mode 100644 cmd/tcl/kubectl-testkube/devbox/devutils/prometheus.go diff --git a/cmd/tcl/kubectl-testkube/devbox/command.go b/cmd/tcl/kubectl-testkube/devbox/command.go index 80c19e06e1..d7448aabf3 100644 --- a/cmd/tcl/kubectl-testkube/devbox/command.go +++ b/cmd/tcl/kubectl-testkube/devbox/command.go @@ -122,6 +122,8 @@ func NewDevBoxCommand() *cobra.Command { binaryStoragePod := namespace.Pod("devbox-binary") mongoPod := namespace.Pod("devbox-mongodb") minioPod := namespace.Pod("devbox-minio") + prometheusPod := namespace.Pod("devbox-prometheus") + grafanaPod := namespace.Pod("devbox-grafana") // Initialize binaries interceptorBin := devutils.NewBinary(InterceptorMainPath, cluster.OperatingSystem(), cluster.Architecture()) @@ -143,6 +145,8 @@ func NewDevBoxCommand() *cobra.Command { binaryStorage := devutils.NewBinaryStorage(binaryStoragePod, binaryStorageBin) mongo := devutils.NewMongo(mongoPod) minio := devutils.NewMinio(minioPod) + prometheus := devutils.NewPrometheus(prometheusPod) + grafana := devutils.NewGrafana(grafanaPod) var env *client.Environment // Cleanup @@ -236,6 +240,34 @@ func NewDevBoxCommand() *cobra.Command { return nil }) + // Deploying Prometheus + g.Go(func() error { + fmt.Println("[Prometheus] Deploying...") + if err = prometheus.Create(ctx); err != nil { + fail(errors.Wrap(err, "failed to create prometheus instance")) + } + fmt.Println("[Prometheus] Waiting for readiness...") + if err = prometheus.WaitForReady(ctx); err != nil { + fail(errors.Wrap(err, "failed to create prometheus instance")) + } + fmt.Println("[Prometheus] Ready") + return nil + }) + + // Deploying Grafana + g.Go(func() error { + fmt.Println("[Grafana] Deploying...") + if err = grafana.Create(ctx); err != nil { + fail(errors.Wrap(err, "failed to create grafana instance")) + } + fmt.Println("[Grafana] Waiting for readiness...") + if err = grafana.WaitForReady(ctx); err != nil { + fail(errors.Wrap(err, "failed to create grafana instance")) + } + fmt.Printf("[Grafana] Ready at %s\n", grafana.LocalAddress()) + return nil + }) + // Deploying binary storage g.Go(func() error { fmt.Println("[Binary Storage] Building...") @@ -615,6 +647,11 @@ func NewDevBoxCommand() *cobra.Command { color.Green.Println("Development box is ready. Took", time.Since(startTs).Truncate(time.Millisecond)) fmt.Println("Namespace:", namespace.Name()) + if termlink.SupportsHyperlinks() { + fmt.Println("Grafana:", termlink.Link(grafana.LocalAddress(), grafana.LocalAddress())) + } else { + fmt.Println("Grafana:", grafana.LocalAddress()) + } if !oss { if termlink.SupportsHyperlinks() { fmt.Println("Dashboard:", termlink.Link(cloud.DashboardUrl(env.Slug, "dashboard/test-workflows"), cloud.DashboardUrl(env.Slug, "dashboard/test-workflows"))) diff --git a/cmd/tcl/kubectl-testkube/devbox/devutils/grafana.go b/cmd/tcl/kubectl-testkube/devbox/devutils/grafana.go new file mode 100644 index 0000000000..547101287f --- /dev/null +++ b/cmd/tcl/kubectl-testkube/devbox/devutils/grafana.go @@ -0,0 +1,130 @@ +// Copyright 2024 Testkube. +// +// Licensed as a Testkube Pro file under the Testkube Community +// License (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// https://github.com/kubeshop/testkube/blob/main/licenses/TCL.txt + +package devutils + +import ( + "context" + "fmt" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/kubeshop/testkube/internal/common" +) + +type Grafana struct { + pod *PodObject + localPort int +} + +func NewGrafana(pod *PodObject) *Grafana { + return &Grafana{ + pod: pod, + } +} + +const ( + grafanaProvisioningPrometheusDataSource = ` +apiVersion: 1 + +deleteDatasources: + - name: Prometheus + orgId: 1 + +datasources: + - name: Prometheus + type: prometheus + access: proxy + orgId: 1 + url: http://devbox-prometheus:9090` +) + +func (r *Grafana) Create(ctx context.Context) error { + _, err := r.pod.ClientSet().CoreV1().ConfigMaps(r.pod.Namespace()).Create(ctx, &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: "devbox-grafana-provisioning-datasources"}, + Data: map[string]string{ + "prometheus.yml": grafanaProvisioningPrometheusDataSource, + }, + }, metav1.CreateOptions{}) + if err != nil { + return err + } + + err = r.pod.Create(ctx, &corev1.Pod{ + Spec: corev1.PodSpec{ + TerminationGracePeriodSeconds: common.Ptr(int64(1)), + Volumes: []corev1.Volume{ + {Name: "data", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}}, + {Name: "provisioning-datasources", VolumeSource: corev1.VolumeSource{ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{Name: "devbox-grafana-provisioning-datasources"}, + }}}, + }, + Containers: []corev1.Container{ + { + Name: "prometheus", + Image: "grafana/grafana-oss:11.3.1", + ImagePullPolicy: corev1.PullIfNotPresent, + Env: []corev1.EnvVar{ + {Name: "GF_USERS_ALLOW_SIGN_UP", Value: "false"}, + {Name: "GF_AUTH_ANONYMOUS_ENABLED", Value: "true"}, + {Name: "GF_AUTH_ANONYMOUS_ORG_ROLE", Value: "Admin"}, + {Name: "GF_AUTH_DISABLE_LOGIN_FORM", Value: "true"}, + }, + VolumeMounts: []corev1.VolumeMount{ + {Name: "data", MountPath: "/var/lib/grafana"}, + {Name: "provisioning-datasources", MountPath: "/etc/grafana/provisioning/datasources"}, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.FromInt32(3000), + }, + }, + PeriodSeconds: 1, + }, + }, + }, + }, + }) + if err != nil { + return err + } + + err = r.pod.CreateService(ctx, corev1.ServicePort{ + Name: "web", + Protocol: "TCP", + Port: 3000, + TargetPort: intstr.FromInt32(3000), + }) + if err != nil { + return err + } + + err = r.pod.WaitForReady(ctx) + if err != nil { + return err + } + + r.localPort = GetFreePort() + err = r.pod.Forward(ctx, 3000, r.localPort, true) + if err != nil { + return err + } + + return nil +} + +func (r *Grafana) LocalAddress() string { + return fmt.Sprintf("http://localhost:%d", r.localPort) +} + +func (r *Grafana) WaitForReady(ctx context.Context) error { + return r.pod.WaitForReady(ctx) +} diff --git a/cmd/tcl/kubectl-testkube/devbox/devutils/prometheus.go b/cmd/tcl/kubectl-testkube/devbox/devutils/prometheus.go new file mode 100644 index 0000000000..4ba4fe56b2 --- /dev/null +++ b/cmd/tcl/kubectl-testkube/devbox/devutils/prometheus.go @@ -0,0 +1,98 @@ +// Copyright 2024 Testkube. +// +// Licensed as a Testkube Pro file under the Testkube Community +// License (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// https://github.com/kubeshop/testkube/blob/main/licenses/TCL.txt + +package devutils + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/kubeshop/testkube/internal/common" +) + +type Prometheus struct { + pod *PodObject +} + +func NewPrometheus(pod *PodObject) *Prometheus { + return &Prometheus{ + pod: pod, + } +} + +const ( + prometheusConfig = ` +global: + scrape_interval: 1s + scrape_timeout: 500ms + evaluation_interval: 1s + +scrape_configs: +- job_name: 'Agent' + honor_labels: true + metrics_path: /metrics + static_configs: + - targets: [ 'devbox-agent:8088' ]` +) + +func (r *Prometheus) Create(ctx context.Context) error { + _, err := r.pod.ClientSet().CoreV1().ConfigMaps(r.pod.Namespace()).Create(ctx, &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: "devbox-prometheus-config"}, + Data: map[string]string{"prometheus.yml": prometheusConfig}, + }, metav1.CreateOptions{}) + if err != nil { + return err + } + + err = r.pod.Create(ctx, &corev1.Pod{ + Spec: corev1.PodSpec{ + TerminationGracePeriodSeconds: common.Ptr(int64(1)), + Volumes: []corev1.Volume{ + {Name: "data", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}}, + {Name: "config", VolumeSource: corev1.VolumeSource{ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{Name: "devbox-prometheus-config"}, + }}}, + }, + Containers: []corev1.Container{ + { + Name: "prometheus", + Image: "prom/prometheus:v2.53.3", + ImagePullPolicy: corev1.PullIfNotPresent, + VolumeMounts: []corev1.VolumeMount{ + {Name: "data", MountPath: "/prometheus"}, + {Name: "config", MountPath: "/etc/prometheus/prometheus.yml", SubPath: "prometheus.yml"}, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.FromInt32(9090), + }, + }, + PeriodSeconds: 1, + }, + }, + }, + }, + }) + if err != nil { + return err + } + return r.pod.CreateService(ctx, corev1.ServicePort{ + Name: "api", + Protocol: "TCP", + Port: 9090, + TargetPort: intstr.FromInt32(9090), + }) +} + +func (r *Prometheus) WaitForReady(ctx context.Context) error { + return r.pod.WaitForReady(ctx) +} From 6276c1c32b7848513bae43c143ddb1479c004fb2 Mon Sep 17 00:00:00 2001 From: Tomasz Konieczny Date: Wed, 20 Nov 2024 11:26:31 +0100 Subject: [PATCH 11/30] feat: k6 perf example (#6039) * k6 example perf test * k6 example perf test * k6 perf test - additinal requests * k6 perf test - additinal requests removed * k6 perf - workflows, values (prometheus + grafana) * branch updated * k6 workflow with prometheus - native histogram, K6_PROMETHEUS_RW_TREND_STATS * k6 test updated * k6 perf - prometheus values - native-histograms enabled --- test/examples/k6-perf/k6-crd.yaml | 135 ++ .../prometheus-grafana/values-grafana.yaml | 1386 +++++++++++++++++ .../prometheus-grafana/values-prometheus.yaml | 1308 ++++++++++++++++ test/k6/executor-tests/k6-perf-test.js | 11 + 4 files changed, 2840 insertions(+) create mode 100644 test/examples/k6-perf/k6-crd.yaml create mode 100644 test/examples/k6-perf/prometheus-grafana/values-grafana.yaml create mode 100644 test/examples/k6-perf/prometheus-grafana/values-prometheus.yaml create mode 100644 test/k6/executor-tests/k6-perf-test.js diff --git a/test/examples/k6-perf/k6-crd.yaml b/test/examples/k6-perf/k6-crd.yaml new file mode 100644 index 0000000000..ac72c31f1e --- /dev/null +++ b/test/examples/k6-perf/k6-crd.yaml @@ -0,0 +1,135 @@ +# Simple +apiVersion: testworkflows.testkube.io/v1 +kind: TestWorkflow +metadata: + name: k6-perf-test +spec: + content: + git: + uri: https://github.com/kubeshop/testkube + revision: main + paths: + - test/k6/executor-tests/k6-perf-test.js + container: + # resources: + # requests: + # cpu: 128m + # memory: 128Mi + workingDir: /data/repo/test/k6/executor-tests + config: + vus: {type: integer, default: 20} + duration: {type: string, default: '1m'} + steps: + - name: Run test + container: + image: grafana/k6:0.49.0 + steps: + - run: + shell: mkdir /data/artifacts && k6 run k6-perf-test.js --vus {{ config.vus }} --duration {{ config.duration }} + env: + - name: K6_WEB_DASHBOARD + value: "true" + - name: K6_WEB_DASHBOARD_EXPORT + value: "/data/artifacts/k6-test-report.html" + steps: + - name: Saving artifacts + workingDir: /data/artifacts + artifacts: + paths: + - '*' +--- +apiVersion: testworkflows.testkube.io/v1 +kind: TestWorkflow +metadata: + name: k6-perf-test-workers +spec: + config: + vus: {type: integer, default: 20} + duration: {type: string, default: '1m'} + workers: {type: integer, default: 3} + content: + git: + uri: https://github.com/kubeshop/testkube + revision: main + paths: + - test/k6/executor-tests/k6-perf-test.js + steps: + - name: Run test + parallel: + count: 'config.workers' + transfer: + - from: /data/repo + fetch: + - from: /data/artifacts + use: + - name: distribute/evenly + # container: + # resources: + # requests: + # cpu: 128m + # memory: 128Mi + paused: true # synchronise running all workers + run: + image: grafana/k6:0.49.0 + workingDir: /data/repo/test/k6/executor-tests + shell: mkdir /data/artifacts && k6 run k6-perf-test.js --vus {{ config.vus }} --duration {{ config.duration }} --execution-segment '{{ index }}/{{ count }}:{{ index + 1 }}/{{ count }}' + env: + - name: K6_WEB_DASHBOARD + value: "true" + - name: K6_WEB_DASHBOARD_EXPORT + value: "/data/artifacts/k6-test-report-worker-{{ index + 1}}.html" + artifacts: + workingDir: /data/artifacts + paths: + - '*.html' +--- +apiVersion: testworkflows.testkube.io/v1 +kind: TestWorkflow +metadata: + name: k6-perf-test-workers-prometheus +spec: + config: + vus: {type: integer, default: 20} + duration: {type: string, default: '1m'} + workers: {type: integer, default: 3} + content: + git: + uri: https://github.com/kubeshop/testkube + revision: main + paths: + - test/k6/executor-tests/k6-perf-test.js + steps: + - name: Run test + parallel: + count: 'config.workers' + transfer: + - from: /data/repo + fetch: + - from: /data/artifacts + use: + - name: distribute/evenly + # container: + # resources: + # requests: + # cpu: 128m + # memory: 128Mi + paused: true # synchronise running all workers + run: + image: grafana/k6:0.49.0 + workingDir: /data/repo/test/k6/executor-tests + shell: mkdir /data/artifacts && k6 run k6-perf-test.js -o experimental-prometheus-rw --vus {{ config.vus }} --duration {{ config.duration }} --execution-segment '{{ index }}/{{ count }}:{{ index + 1 }}/{{ count }}' --tag testid=worker-{{ index + 1}} + env: + - name: K6_WEB_DASHBOARD + value: "true" + - name: K6_WEB_DASHBOARD_EXPORT + value: "/data/artifacts/k6-test-report-worker-{{ index + 1}}.html" + - name: K6_PROMETHEUS_RW_SERVER_URL + value: 'http://prometheus-server.prometheus-grafana.svc.cluster.local:80/api/v1/write' + - name: K6_PROMETHEUS_RW_TREND_STATS + value: 'p(95),p(99),min,max' + - name: K6_PROMETHEUS_RW_TREND_AS_NATIVE_HISTOGRAM + value: "true" + artifacts: + workingDir: /data/artifacts + paths: + - '*.html' diff --git a/test/examples/k6-perf/prometheus-grafana/values-grafana.yaml b/test/examples/k6-perf/prometheus-grafana/values-grafana.yaml new file mode 100644 index 0000000000..b35402054a --- /dev/null +++ b/test/examples/k6-perf/prometheus-grafana/values-grafana.yaml @@ -0,0 +1,1386 @@ +global: + # -- Overrides the Docker registry globally for all images + imageRegistry: null + + # To help compatibility with other charts which use global.imagePullSecrets. + # Allow either an array of {name: pullSecret} maps (k8s-style), or an array of strings (more common helm-style). + # Can be templated. + # global: + # imagePullSecrets: + # - name: pullSecret1 + # - name: pullSecret2 + # or + # global: + # imagePullSecrets: + # - pullSecret1 + # - pullSecret2 + imagePullSecrets: [] + +rbac: + create: true + ## Use an existing ClusterRole/Role (depending on rbac.namespaced false/true) + # useExistingRole: name-of-some-role + # useExistingClusterRole: name-of-some-clusterRole + pspEnabled: false + pspUseAppArmor: false + namespaced: false + extraRoleRules: [] + # - apiGroups: [] + # resources: [] + # verbs: [] + extraClusterRoleRules: [] + # - apiGroups: [] + # resources: [] + # verbs: [] +serviceAccount: + create: true + name: + nameTest: + ## ServiceAccount labels. + labels: {} + ## Service account annotations. Can be templated. + # annotations: + # eks.amazonaws.com/role-arn: arn:aws:iam::123456789000:role/iam-role-name-here + + ## autoMount is deprecated in favor of automountServiceAccountToken + # autoMount: false + automountServiceAccountToken: false + +replicas: 1 + +## Create a headless service for the deployment +headlessService: false + +## Should the service account be auto mounted on the pod +automountServiceAccountToken: true + +## Create HorizontalPodAutoscaler object for deployment type +# +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 5 + targetCPU: "60" + targetMemory: "" + behavior: {} + +## See `kubectl explain poddisruptionbudget.spec` for more +## ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/ +podDisruptionBudget: {} +# apiVersion: "" +# minAvailable: 1 +# maxUnavailable: 1 + +## See `kubectl explain deployment.spec.strategy` for more +## ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy +deploymentStrategy: + type: RollingUpdate + +readinessProbe: + httpGet: + path: /api/health + port: 3000 + +livenessProbe: + httpGet: + path: /api/health + port: 3000 + initialDelaySeconds: 60 + timeoutSeconds: 30 + failureThreshold: 10 + +## Use an alternate scheduler, e.g. "stork". +## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ +## +# schedulerName: "default-scheduler" + +image: + # -- The Docker registry + registry: docker.io + # -- Docker image repository + repository: grafana/grafana + # Overrides the Grafana image tag whose default is the chart appVersion + tag: "" + sha: "" + pullPolicy: IfNotPresent + + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## Can be templated. + ## + pullSecrets: [] + # - myRegistrKeySecretName + +testFramework: + enabled: true + image: + # -- The Docker registry + registry: docker.io + repository: bats/bats + tag: "v1.4.1" + imagePullPolicy: IfNotPresent + securityContext: {} + resources: {} + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +# dns configuration for pod +dnsPolicy: ~ +dnsConfig: {} + # nameservers: + # - 8.8.8.8 + # options: + # - name: ndots + # value: "2" + # - name: edns0 + +securityContext: + runAsNonRoot: true + runAsUser: 472 + runAsGroup: 472 + fsGroup: 472 + +containerSecurityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault + +# Enable creating the grafana configmap +createConfigmap: true + +# Extra configmaps to mount in grafana pods +# Values are templated. +extraConfigmapMounts: [] + # - name: certs-configmap + # mountPath: /etc/grafana/ssl/ + # subPath: certificates.crt # (optional) + # configMap: certs-configmap + # readOnly: true + # optional: false + + +extraEmptyDirMounts: [] + # - name: provisioning-notifiers + # mountPath: /etc/grafana/provisioning/notifiers + + +# Apply extra labels to common labels. +extraLabels: {} + +## Assign a PriorityClassName to pods if set +# priorityClassName: + +downloadDashboardsImage: + # -- The Docker registry + registry: docker.io + repository: curlimages/curl + tag: 7.85.0 + sha: "" + pullPolicy: IfNotPresent + +downloadDashboards: + env: {} + envFromSecret: "" + resources: {} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault + envValueFrom: {} + # ENV_NAME: + # configMapKeyRef: + # name: configmap-name + # key: value_key + +## Pod Annotations +# podAnnotations: {} + +## ConfigMap Annotations +# configMapAnnotations: {} + # argocd.argoproj.io/sync-options: Replace=true + +## Pod Labels +# podLabels: {} + +podPortName: grafana +gossipPortName: gossip +## Deployment annotations +# annotations: {} + +## Expose the grafana service to be accessed from outside the cluster (LoadBalancer service). +## or access it from within the cluster (ClusterIP service). Set the service type and the port to serve it. +## ref: http://kubernetes.io/docs/user-guide/services/ +## +service: + enabled: true + type: ClusterIP + # Set the ip family policy to configure dual-stack see [Configure dual-stack](https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services) + ipFamilyPolicy: "" + # Sets the families that should be supported and the order in which they should be applied to ClusterIP as well. Can be IPv4 and/or IPv6. + ipFamilies: [] + loadBalancerIP: "" + loadBalancerClass: "" + loadBalancerSourceRanges: [] + port: 80 + targetPort: 3000 + # targetPort: 4181 To be used with a proxy extraContainer + ## Service annotations. Can be templated. + annotations: {} + labels: {} + portName: service + # Adds the appProtocol field to the service. This allows to work with istio protocol selection. Ex: "http" or "tcp" + appProtocol: "" + +serviceMonitor: + ## If true, a ServiceMonitor CR is created for a prometheus operator + ## https://github.com/coreos/prometheus-operator + ## + enabled: false + path: /metrics + # namespace: monitoring (defaults to use the namespace this chart is deployed to) + labels: {} + interval: 30s + scheme: http + tlsConfig: {} + scrapeTimeout: 30s + relabelings: [] + metricRelabelings: [] + targetLabels: [] + +extraExposePorts: [] + # - name: keycloak + # port: 8080 + # targetPort: 8080 + +# overrides pod.spec.hostAliases in the grafana deployment's pods +hostAliases: [] + # - ip: "1.2.3.4" + # hostnames: + # - "my.host.com" + +ingress: + enabled: false + # For Kubernetes >= 1.18 you should specify the ingress-controller via the field ingressClassName + # See https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/#specifying-the-class-of-an-ingress + # ingressClassName: nginx + # Values can be templated + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + labels: {} + path: / + + # pathType is only for k8s >= 1.1= + pathType: Prefix + + hosts: + - chart-example.local + ## Extra paths to prepend to every host configuration. This is useful when working with annotation based services. + extraPaths: [] + # - path: /* + # backend: + # serviceName: ssl-redirect + # servicePort: use-annotation + ## Or for k8s > 1.19 + # - path: /* + # pathType: Prefix + # backend: + # service: + # name: ssl-redirect + # port: + # name: use-annotation + + + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} +# limits: +# cpu: 100m +# memory: 128Mi +# requests: +# cpu: 100m +# memory: 128Mi + +## Node labels for pod assignment +## ref: https://kubernetes.io/docs/user-guide/node-selection/ +# +nodeSelector: {} + +## Tolerations for pod assignment +## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +## +tolerations: [] + +## Affinity for pod assignment (evaluated as template) +## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity +## +affinity: {} + +## Topology Spread Constraints +## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ +## +topologySpreadConstraints: [] + +## Additional init containers (evaluated as template) +## ref: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ +## +extraInitContainers: [] + +## Enable an Specify container in extraContainers. This is meant to allow adding an authentication proxy to a grafana pod +extraContainers: "" +# extraContainers: | +# - name: proxy +# image: quay.io/gambol99/keycloak-proxy:latest +# args: +# - -provider=github +# - -client-id= +# - -client-secret= +# - -github-org= +# - -email-domain=* +# - -cookie-secret= +# - -http-address=http://0.0.0.0:4181 +# - -upstream-url=http://127.0.0.1:3000 +# ports: +# - name: proxy-web +# containerPort: 4181 + +## Volumes that can be used in init containers that will not be mounted to deployment pods +extraContainerVolumes: [] +# - name: volume-from-secret +# secret: +# secretName: secret-to-mount +# - name: empty-dir-volume +# emptyDir: {} + +## Enable persistence using Persistent Volume Claims +## ref: https://kubernetes.io/docs/user-guide/persistent-volumes/ +## +persistence: + type: pvc + enabled: true + # storageClassName: default + accessModes: + - ReadWriteOnce + size: 10Gi + # annotations: {} + finalizers: + - kubernetes.io/pvc-protection + # selectorLabels: {} + ## Sub-directory of the PV to mount. Can be templated. + # subPath: "" + ## Name of an existing PVC. Can be templated. + # existingClaim: + ## Extra labels to apply to a PVC. + extraPvcLabels: {} + disableWarning: false + + ## If persistence is not enabled, this allows to mount the + ## local storage in-memory to improve performance + ## + inMemory: + enabled: false + ## The maximum usage on memory medium EmptyDir would be + ## the minimum value between the SizeLimit specified + ## here and the sum of memory limits of all containers in a pod + ## + # sizeLimit: 300Mi + + ## If 'lookupVolumeName' is set to true, Helm will attempt to retrieve + ## the current value of 'spec.volumeName' and incorporate it into the template. + lookupVolumeName: true + +initChownData: + ## If false, data ownership will not be reset at startup + ## This allows the grafana-server to be run with an arbitrary user + ## + enabled: true + + ## initChownData container image + ## + image: + # -- The Docker registry + registry: docker.io + repository: library/busybox + tag: "1.31.1" + sha: "" + pullPolicy: IfNotPresent + + ## initChownData resource requests and limits + ## Ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## + resources: {} + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + securityContext: + runAsNonRoot: false + runAsUser: 0 + seccompProfile: + type: RuntimeDefault + capabilities: + add: + - CHOWN + +# Administrator credentials when not using an existing secret (see below) +adminUser: admin +# adminPassword: strongpassword + +# Use an existing secret for the admin user. +admin: + ## Name of the secret. Can be templated. + existingSecret: "" + userKey: admin-user + passwordKey: admin-password + +## Define command to be executed at startup by grafana container +## Needed if using `vault-env` to manage secrets (ref: https://banzaicloud.com/blog/inject-secrets-into-pods-vault/) +## Default is "run.sh" as defined in grafana's Dockerfile +# command: +# - "sh" +# - "/run.sh" + +## Optionally define args if command is used +## Needed if using `hashicorp/envconsul` to manage secrets +## By default no arguments are set +# args: +# - "-secret" +# - "secret/grafana" +# - "./grafana" + +## Extra environment variables that will be pass onto deployment pods +## +## to provide grafana with access to CloudWatch on AWS EKS: +## 1. create an iam role of type "Web identity" with provider oidc.eks.* (note the provider for later) +## 2. edit the "Trust relationships" of the role, add a line inside the StringEquals clause using the +## same oidc eks provider as noted before (same as the existing line) +## also, replace NAMESPACE and prometheus-operator-grafana with the service account namespace and name +## +## "oidc.eks.us-east-1.amazonaws.com/id/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:sub": "system:serviceaccount:NAMESPACE:prometheus-operator-grafana", +## +## 3. attach a policy to the role, you can use a built in policy called CloudWatchReadOnlyAccess +## 4. use the following env: (replace 123456789000 and iam-role-name-here with your aws account number and role name) +## +## env: +## AWS_ROLE_ARN: arn:aws:iam::123456789000:role/iam-role-name-here +## AWS_WEB_IDENTITY_TOKEN_FILE: /var/run/secrets/eks.amazonaws.com/serviceaccount/token +## AWS_REGION: us-east-1 +## +## 5. uncomment the EKS section in extraSecretMounts: below +## 6. uncomment the annotation section in the serviceAccount: above +## make sure to replace arn:aws:iam::123456789000:role/iam-role-name-here with your role arn + +env: {} + +## "valueFrom" environment variable references that will be added to deployment pods. Name is templated. +## ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvarsource-v1-core +## Renders in container spec as: +## env: +## ... +## - name: +## valueFrom: +## +envValueFrom: {} + # ENV_NAME: + # configMapKeyRef: + # name: configmap-name + # key: value_key + +## The name of a secret in the same kubernetes namespace which contain values to be added to the environment +## This can be useful for auth tokens, etc. Value is templated. +envFromSecret: "" + +## Sensible environment variables that will be rendered as new secret object +## This can be useful for auth tokens, etc. +## If the secret values contains "{{", they'll need to be properly escaped so that they are not interpreted by Helm +## ref: https://helm.sh/docs/howto/charts_tips_and_tricks/#using-the-tpl-function +envRenderSecret: {} + +## The names of secrets in the same kubernetes namespace which contain values to be added to the environment +## Each entry should contain a name key, and can optionally specify whether the secret must be defined with an optional key. +## Name is templated. +envFromSecrets: [] +## - name: secret-name +## prefix: prefix +## optional: true + +## The names of conifgmaps in the same kubernetes namespace which contain values to be added to the environment +## Each entry should contain a name key, and can optionally specify whether the configmap must be defined with an optional key. +## Name is templated. +## ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#configmapenvsource-v1-core +envFromConfigMaps: [] +## - name: configmap-name +## prefix: prefix +## optional: true + +# Inject Kubernetes services as environment variables. +# See https://kubernetes.io/docs/concepts/services-networking/connect-applications-service/#environment-variables +enableServiceLinks: true + +## Additional grafana server secret mounts +# Defines additional mounts with secrets. Secrets must be manually created in the namespace. +extraSecretMounts: [] + # - name: secret-files + # mountPath: /etc/secrets + # secretName: grafana-secret-files + # readOnly: true + # optional: false + # subPath: "" + # + # for AWS EKS (cloudwatch) use the following (see also instruction in env: above) + # - name: aws-iam-token + # mountPath: /var/run/secrets/eks.amazonaws.com/serviceaccount + # readOnly: true + # projected: + # defaultMode: 420 + # sources: + # - serviceAccountToken: + # audience: sts.amazonaws.com + # expirationSeconds: 86400 + # path: token + # + # for CSI e.g. Azure Key Vault use the following + # - name: secrets-store-inline + # mountPath: /run/secrets + # readOnly: true + # csi: + # driver: secrets-store.csi.k8s.io + # readOnly: true + # volumeAttributes: + # secretProviderClass: "akv-grafana-spc" + # nodePublishSecretRef: # Only required when using service principal mode + # name: grafana-akv-creds # Only required when using service principal mode + +## Additional grafana server volume mounts +# Defines additional volume mounts. +extraVolumeMounts: [] + # - name: extra-volume-0 + # mountPath: /mnt/volume0 + # readOnly: true + # - name: extra-volume-1 + # mountPath: /mnt/volume1 + # readOnly: true + # - name: grafana-secrets + # mountPath: /mnt/volume2 + +## Additional Grafana server volumes +extraVolumes: [] + # - name: extra-volume-0 + # existingClaim: volume-claim + # - name: extra-volume-1 + # hostPath: + # path: /usr/shared/ + # type: "" + # - name: grafana-secrets + # csi: + # driver: secrets-store.csi.k8s.io + # readOnly: true + # volumeAttributes: + # secretProviderClass: "grafana-env-spc" + +## Container Lifecycle Hooks. Execute a specific bash command or make an HTTP request +lifecycleHooks: {} + # postStart: + # exec: + # command: [] + +## Pass the plugins you want installed as a list. +## +plugins: [] + # - digrich-bubblechart-panel + # - grafana-clock-panel + ## You can also use other plugin download URL, as long as they are valid zip files, + ## and specify the name of the plugin after the semicolon. Like this: + # - https://grafana.com/api/plugins/marcusolsson-json-datasource/versions/1.3.2/download;marcusolsson-json-datasource + +## Configure grafana datasources +## ref: http://docs.grafana.org/administration/provisioning/#datasources +## +datasources: {} +# datasources.yaml: +# apiVersion: 1 +# datasources: +# - name: Prometheus +# type: prometheus +# url: http://prometheus-prometheus-server +# access: proxy +# isDefault: true +# - name: CloudWatch +# type: cloudwatch +# access: proxy +# uid: cloudwatch +# editable: false +# jsonData: +# authType: default +# defaultRegion: us-east-1 +# deleteDatasources: [] +# - name: Prometheus + +## Configure grafana alerting (can be templated) +## ref: http://docs.grafana.org/administration/provisioning/#alerting +## +alerting: {} + # rules.yaml: + # apiVersion: 1 + # groups: + # - orgId: 1 + # name: '{{ .Chart.Name }}_my_rule_group' + # folder: my_first_folder + # interval: 60s + # rules: + # - uid: my_id_1 + # title: my_first_rule + # condition: A + # data: + # - refId: A + # datasourceUid: '-100' + # model: + # conditions: + # - evaluator: + # params: + # - 3 + # type: gt + # operator: + # type: and + # query: + # params: + # - A + # reducer: + # type: last + # type: query + # datasource: + # type: __expr__ + # uid: '-100' + # expression: 1==0 + # intervalMs: 1000 + # maxDataPoints: 43200 + # refId: A + # type: math + # dashboardUid: my_dashboard + # panelId: 123 + # noDataState: Alerting + # for: 60s + # annotations: + # some_key: some_value + # labels: + # team: sre_team_1 + # contactpoints.yaml: + # secret: + # apiVersion: 1 + # contactPoints: + # - orgId: 1 + # name: cp_1 + # receivers: + # - uid: first_uid + # type: pagerduty + # settings: + # integrationKey: XXX + # severity: critical + # class: ping failure + # component: Grafana + # group: app-stack + # summary: | + # {{ `{{ include "default.message" . }}` }} + +## Configure notifiers +## ref: http://docs.grafana.org/administration/provisioning/#alert-notification-channels +## +notifiers: {} +# notifiers.yaml: +# notifiers: +# - name: email-notifier +# type: email +# uid: email1 +# # either: +# org_id: 1 +# # or +# org_name: Main Org. +# is_default: true +# settings: +# addresses: an_email_address@example.com +# delete_notifiers: + +## Configure grafana dashboard providers +## ref: http://docs.grafana.org/administration/provisioning/#dashboards +## +## `path` must be /var/lib/grafana/dashboards/ +## +dashboardProviders: {} +# dashboardproviders.yaml: +# apiVersion: 1 +# providers: +# - name: 'default' +# orgId: 1 +# folder: '' +# type: file +# disableDeletion: false +# editable: true +# options: +# path: /var/lib/grafana/dashboards/default + +## Configure grafana dashboard to import +## NOTE: To use dashboards you must also enable/configure dashboardProviders +## ref: https://grafana.com/dashboards +## +## dashboards per provider, use provider name as key. +## +dashboards: {} + # default: + # some-dashboard: + # json: | + # $RAW_JSON + # custom-dashboard: + # file: dashboards/custom-dashboard.json + # prometheus-stats: + # gnetId: 2 + # revision: 2 + # datasource: Prometheus + # local-dashboard: + # url: https://example.com/repository/test.json + # token: '' + # local-dashboard-base64: + # url: https://example.com/repository/test-b64.json + # token: '' + # b64content: true + # local-dashboard-gitlab: + # url: https://example.com/repository/test-gitlab.json + # gitlabToken: '' + # local-dashboard-bitbucket: + # url: https://example.com/repository/test-bitbucket.json + # bearerToken: '' + # local-dashboard-azure: + # url: https://example.com/repository/test-azure.json + # basic: '' + # acceptHeader: '*/*' + +## Reference to external ConfigMap per provider. Use provider name as key and ConfigMap name as value. +## A provider dashboards must be defined either by external ConfigMaps or in values.yaml, not in both. +## ConfigMap data example: +## +## data: +## example-dashboard.json: | +## RAW_JSON +## +dashboardsConfigMaps: {} +# default: "" + +## Grafana's primary configuration +## NOTE: values in map will be converted to ini format +## ref: http://docs.grafana.org/installation/configuration/ +## +grafana.ini: + paths: + data: /var/lib/grafana/ + logs: /var/log/grafana + plugins: /var/lib/grafana/plugins + provisioning: /etc/grafana/provisioning + analytics: + check_for_updates: true + log: + mode: console + grafana_net: + url: https://grafana.net + server: + domain: "{{ if (and .Values.ingress.enabled .Values.ingress.hosts) }}{{ tpl (.Values.ingress.hosts | first) . }}{{ else }}''{{ end }}" +## grafana Authentication can be enabled with the following values on grafana.ini + # server: + # The full public facing url you use in browser, used for redirects and emails + # root_url: + # https://grafana.com/docs/grafana/latest/auth/github/#enable-github-in-grafana + # auth.github: + # enabled: false + # allow_sign_up: false + # scopes: user:email,read:org + # auth_url: https://github.com/login/oauth/authorize + # token_url: https://github.com/login/oauth/access_token + # api_url: https://api.github.com/user + # team_ids: + # allowed_organizations: + # client_id: + # client_secret: +## LDAP Authentication can be enabled with the following values on grafana.ini +## NOTE: Grafana will fail to start if the value for ldap.toml is invalid + # auth.ldap: + # enabled: true + # allow_sign_up: true + # config_file: /etc/grafana/ldap.toml + +## Grafana's LDAP configuration +## Templated by the template in _helpers.tpl +## NOTE: To enable the grafana.ini must be configured with auth.ldap.enabled +## ref: http://docs.grafana.org/installation/configuration/#auth-ldap +## ref: http://docs.grafana.org/installation/ldap/#configuration +ldap: + enabled: false + # `existingSecret` is a reference to an existing secret containing the ldap configuration + # for Grafana in a key `ldap-toml`. + existingSecret: "" + # `config` is the content of `ldap.toml` that will be stored in the created secret + config: "" + # config: |- + # verbose_logging = true + + # [[servers]] + # host = "my-ldap-server" + # port = 636 + # use_ssl = true + # start_tls = false + # ssl_skip_verify = false + # bind_dn = "uid=%s,ou=users,dc=myorg,dc=com" + +## Grafana's SMTP configuration +## NOTE: To enable, grafana.ini must be configured with smtp.enabled +## ref: http://docs.grafana.org/installation/configuration/#smtp +smtp: + # `existingSecret` is a reference to an existing secret containing the smtp configuration + # for Grafana. + existingSecret: "" + userKey: "user" + passwordKey: "password" + +## Sidecars that collect the configmaps with specified label and stores the included files them into the respective folders +## Requires at least Grafana 5 to work and can't be used together with parameters dashboardProviders, datasources and dashboards +sidecar: + image: + # -- The Docker registry + registry: quay.io + repository: kiwigrid/k8s-sidecar + tag: 1.27.4 + sha: "" + imagePullPolicy: IfNotPresent + resources: {} +# limits: +# cpu: 100m +# memory: 100Mi +# requests: +# cpu: 50m +# memory: 50Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault + # skipTlsVerify Set to true to skip tls verification for kube api calls + # skipTlsVerify: true + enableUniqueFilenames: false + readinessProbe: {} + livenessProbe: {} + # Log level default for all sidecars. Can be one of: DEBUG, INFO, WARN, ERROR, CRITICAL. Defaults to INFO + # logLevel: INFO + alerts: + enabled: false + # Additional environment variables for the alerts sidecar + env: {} + # Do not reprocess already processed unchanged resources on k8s API reconnect. + # ignoreAlreadyProcessed: true + # label that the configmaps with alert are marked with + label: grafana_alert + # value of label that the configmaps with alert are set to + labelValue: "" + # Log level. Can be one of: DEBUG, INFO, WARN, ERROR, CRITICAL. + # logLevel: INFO + # If specified, the sidecar will search for alert config-maps inside this namespace. + # Otherwise the namespace in which the sidecar is running will be used. + # It's also possible to specify ALL to search in all namespaces + searchNamespace: null + # Method to use to detect ConfigMap changes. With WATCH the sidecar will do a WATCH requests, with SLEEP it will list all ConfigMaps, then sleep for 60 seconds. + watchMethod: WATCH + # search in configmap, secret or both + resource: both + # watchServerTimeout: request to the server, asking it to cleanly close the connection after that. + # defaults to 60sec; much higher values like 3600 seconds (1h) are feasible for non-Azure K8S + # watchServerTimeout: 3600 + # + # watchClientTimeout: is a client-side timeout, configuring your local socket. + # If you have a network outage dropping all packets with no RST/FIN, + # this is how long your client waits before realizing & dropping the connection. + # defaults to 66sec (sic!) + # watchClientTimeout: 60 + # + # Endpoint to send request to reload alerts + reloadURL: "http://localhost:3000/api/admin/provisioning/alerting/reload" + # Absolute path to shell script to execute after a alert got reloaded + script: null + skipReload: false + # This is needed if skipReload is true, to load any alerts defined at startup time. + # Deploy the alert sidecar as an initContainer. + initAlerts: false + # Additional alert sidecar volume mounts + extraMounts: [] + # Sets the size limit of the alert sidecar emptyDir volume + sizeLimit: {} + dashboards: + enabled: false + # Additional environment variables for the dashboards sidecar + env: {} + ## "valueFrom" environment variable references that will be added to deployment pods. Name is templated. + ## ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvarsource-v1-core + ## Renders in container spec as: + ## env: + ## ... + ## - name: + ## valueFrom: + ## + envValueFrom: {} + # ENV_NAME: + # configMapKeyRef: + # name: configmap-name + # key: value_key + # Do not reprocess already processed unchanged resources on k8s API reconnect. + # ignoreAlreadyProcessed: true + SCProvider: true + # label that the configmaps with dashboards are marked with + label: grafana_dashboard + # value of label that the configmaps with dashboards are set to + labelValue: "" + # Log level. Can be one of: DEBUG, INFO, WARN, ERROR, CRITICAL. + # logLevel: INFO + # folder in the pod that should hold the collected dashboards (unless `defaultFolderName` is set) + folder: /tmp/dashboards + # The default folder name, it will create a subfolder under the `folder` and put dashboards in there instead + defaultFolderName: null + # Namespaces list. If specified, the sidecar will search for config-maps/secrets inside these namespaces. + # Otherwise the namespace in which the sidecar is running will be used. + # It's also possible to specify ALL to search in all namespaces. + searchNamespace: null + # Method to use to detect ConfigMap changes. With WATCH the sidecar will do a WATCH requests, with SLEEP it will list all ConfigMaps, then sleep for 60 seconds. + watchMethod: WATCH + # search in configmap, secret or both + resource: both + # If specified, the sidecar will look for annotation with this name to create folder and put graph here. + # You can use this parameter together with `provider.foldersFromFilesStructure`to annotate configmaps and create folder structure. + folderAnnotation: null + # Endpoint to send request to reload alerts + reloadURL: "http://localhost:3000/api/admin/provisioning/dashboards/reload" + # Absolute path to shell script to execute after a configmap got reloaded + script: null + skipReload: false + # watchServerTimeout: request to the server, asking it to cleanly close the connection after that. + # defaults to 60sec; much higher values like 3600 seconds (1h) are feasible for non-Azure K8S + # watchServerTimeout: 3600 + # + # watchClientTimeout: is a client-side timeout, configuring your local socket. + # If you have a network outage dropping all packets with no RST/FIN, + # this is how long your client waits before realizing & dropping the connection. + # defaults to 66sec (sic!) + # watchClientTimeout: 60 + # + # provider configuration that lets grafana manage the dashboards + provider: + # name of the provider, should be unique + name: sidecarProvider + # orgid as configured in grafana + orgid: 1 + # folder in which the dashboards should be imported in grafana + folder: '' + # folder UID. will be automatically generated if not specified + folderUid: '' + # type of the provider + type: file + # disableDelete to activate a import-only behaviour + disableDelete: false + # allow updating provisioned dashboards from the UI + allowUiUpdates: false + # allow Grafana to replicate dashboard structure from filesystem + foldersFromFilesStructure: false + # Additional dashboard sidecar volume mounts + extraMounts: [] + # Sets the size limit of the dashboard sidecar emptyDir volume + sizeLimit: {} + datasources: + enabled: false + # Additional environment variables for the datasourcessidecar + env: {} + ## "valueFrom" environment variable references that will be added to deployment pods. Name is templated. + ## ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvarsource-v1-core + ## Renders in container spec as: + ## env: + ## ... + ## - name: + ## valueFrom: + ## + envValueFrom: {} + # ENV_NAME: + # configMapKeyRef: + # name: configmap-name + # key: value_key + # Do not reprocess already processed unchanged resources on k8s API reconnect. + # ignoreAlreadyProcessed: true + # label that the configmaps with datasources are marked with + label: grafana_datasource + # value of label that the configmaps with datasources are set to + labelValue: "" + # Log level. Can be one of: DEBUG, INFO, WARN, ERROR, CRITICAL. + # logLevel: INFO + # If specified, the sidecar will search for datasource config-maps inside this namespace. + # Otherwise the namespace in which the sidecar is running will be used. + # It's also possible to specify ALL to search in all namespaces + searchNamespace: null + # Method to use to detect ConfigMap changes. With WATCH the sidecar will do a WATCH requests, with SLEEP it will list all ConfigMaps, then sleep for 60 seconds. + watchMethod: WATCH + # search in configmap, secret or both + resource: both + # watchServerTimeout: request to the server, asking it to cleanly close the connection after that. + # defaults to 60sec; much higher values like 3600 seconds (1h) are feasible for non-Azure K8S + # watchServerTimeout: 3600 + # + # watchClientTimeout: is a client-side timeout, configuring your local socket. + # If you have a network outage dropping all packets with no RST/FIN, + # this is how long your client waits before realizing & dropping the connection. + # defaults to 66sec (sic!) + # watchClientTimeout: 60 + # + # Endpoint to send request to reload datasources + reloadURL: "http://localhost:3000/api/admin/provisioning/datasources/reload" + # Absolute path to shell script to execute after a datasource got reloaded + script: null + skipReload: false + # This is needed if skipReload is true, to load any datasources defined at startup time. + # Deploy the datasources sidecar as an initContainer. + initDatasources: false + # Sets the size limit of the datasource sidecar emptyDir volume + sizeLimit: {} + plugins: + enabled: false + # Additional environment variables for the plugins sidecar + env: {} + # Do not reprocess already processed unchanged resources on k8s API reconnect. + # ignoreAlreadyProcessed: true + # label that the configmaps with plugins are marked with + label: grafana_plugin + # value of label that the configmaps with plugins are set to + labelValue: "" + # Log level. Can be one of: DEBUG, INFO, WARN, ERROR, CRITICAL. + # logLevel: INFO + # If specified, the sidecar will search for plugin config-maps inside this namespace. + # Otherwise the namespace in which the sidecar is running will be used. + # It's also possible to specify ALL to search in all namespaces + searchNamespace: null + # Method to use to detect ConfigMap changes. With WATCH the sidecar will do a WATCH requests, with SLEEP it will list all ConfigMaps, then sleep for 60 seconds. + watchMethod: WATCH + # search in configmap, secret or both + resource: both + # watchServerTimeout: request to the server, asking it to cleanly close the connection after that. + # defaults to 60sec; much higher values like 3600 seconds (1h) are feasible for non-Azure K8S + # watchServerTimeout: 3600 + # + # watchClientTimeout: is a client-side timeout, configuring your local socket. + # If you have a network outage dropping all packets with no RST/FIN, + # this is how long your client waits before realizing & dropping the connection. + # defaults to 66sec (sic!) + # watchClientTimeout: 60 + # + # Endpoint to send request to reload plugins + reloadURL: "http://localhost:3000/api/admin/provisioning/plugins/reload" + # Absolute path to shell script to execute after a plugin got reloaded + script: null + skipReload: false + # Deploy the datasource sidecar as an initContainer in addition to a container. + # This is needed if skipReload is true, to load any plugins defined at startup time. + initPlugins: false + # Sets the size limit of the plugin sidecar emptyDir volume + sizeLimit: {} + notifiers: + enabled: false + # Additional environment variables for the notifierssidecar + env: {} + # Do not reprocess already processed unchanged resources on k8s API reconnect. + # ignoreAlreadyProcessed: true + # label that the configmaps with notifiers are marked with + label: grafana_notifier + # value of label that the configmaps with notifiers are set to + labelValue: "" + # Log level. Can be one of: DEBUG, INFO, WARN, ERROR, CRITICAL. + # logLevel: INFO + # If specified, the sidecar will search for notifier config-maps inside this namespace. + # Otherwise the namespace in which the sidecar is running will be used. + # It's also possible to specify ALL to search in all namespaces + searchNamespace: null + # Method to use to detect ConfigMap changes. With WATCH the sidecar will do a WATCH requests, with SLEEP it will list all ConfigMaps, then sleep for 60 seconds. + watchMethod: WATCH + # search in configmap, secret or both + resource: both + # watchServerTimeout: request to the server, asking it to cleanly close the connection after that. + # defaults to 60sec; much higher values like 3600 seconds (1h) are feasible for non-Azure K8S + # watchServerTimeout: 3600 + # + # watchClientTimeout: is a client-side timeout, configuring your local socket. + # If you have a network outage dropping all packets with no RST/FIN, + # this is how long your client waits before realizing & dropping the connection. + # defaults to 66sec (sic!) + # watchClientTimeout: 60 + # + # Endpoint to send request to reload notifiers + reloadURL: "http://localhost:3000/api/admin/provisioning/notifications/reload" + # Absolute path to shell script to execute after a notifier got reloaded + script: null + skipReload: false + # Deploy the notifier sidecar as an initContainer in addition to a container. + # This is needed if skipReload is true, to load any notifiers defined at startup time. + initNotifiers: false + # Sets the size limit of the notifier sidecar emptyDir volume + sizeLimit: {} + +## Override the deployment namespace +## +namespaceOverride: "" + +## Number of old ReplicaSets to retain +## +revisionHistoryLimit: 10 + +## Add a seperate remote image renderer deployment/service +imageRenderer: + deploymentStrategy: {} + # Enable the image-renderer deployment & service + enabled: false + replicas: 1 + autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 5 + targetCPU: "60" + targetMemory: "" + behavior: {} + # The url of remote image renderer if it is not in the same namespace with the grafana instance + serverURL: "" + # The callback url of grafana instances if it is not in the same namespace with the remote image renderer + renderingCallbackURL: "" + image: + # -- The Docker registry + registry: docker.io + # image-renderer Image repository + repository: grafana/grafana-image-renderer + # image-renderer Image tag + tag: latest + # image-renderer Image sha (optional) + sha: "" + # image-renderer ImagePullPolicy + pullPolicy: Always + # extra environment variables + env: + HTTP_HOST: "0.0.0.0" + # RENDERING_ARGS: --no-sandbox,--disable-gpu,--window-size=1280x758 + # RENDERING_MODE: clustered + # IGNORE_HTTPS_ERRORS: true + + ## "valueFrom" environment variable references that will be added to deployment pods. Name is templated. + ## ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvarsource-v1-core + ## Renders in container spec as: + ## env: + ## ... + ## - name: + ## valueFrom: + ## + envValueFrom: {} + # ENV_NAME: + # configMapKeyRef: + # name: configmap-name + # key: value_key + + # image-renderer deployment serviceAccount + serviceAccountName: "" + # image-renderer deployment securityContext + securityContext: {} + # image-renderer deployment container securityContext + containerSecurityContext: + seccompProfile: + type: RuntimeDefault + capabilities: + drop: ['ALL'] + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + ## image-renderer pod annotation + podAnnotations: {} + # image-renderer deployment Host Aliases + hostAliases: [] + # image-renderer deployment priority class + priorityClassName: '' + service: + # Enable the image-renderer service + enabled: true + # image-renderer service port name + portName: 'http' + # image-renderer service port used by both service and deployment + port: 8081 + targetPort: 8081 + # Adds the appProtocol field to the image-renderer service. This allows to work with istio protocol selection. Ex: "http" or "tcp" + appProtocol: "" + serviceMonitor: + ## If true, a ServiceMonitor CRD is created for a prometheus operator + ## https://github.com/coreos/prometheus-operator + ## + enabled: false + path: /metrics + # namespace: monitoring (defaults to use the namespace this chart is deployed to) + labels: {} + interval: 1m + scheme: http + tlsConfig: {} + scrapeTimeout: 30s + relabelings: [] + # See: https://doc.crds.dev/github.com/prometheus-operator/kube-prometheus/monitoring.coreos.com/ServiceMonitor/v1@v0.11.0#spec-targetLabels + targetLabels: [] + # - targetLabel1 + # - targetLabel2 + # If https is enabled in Grafana, this needs to be set as 'https' to correctly configure the callback used in Grafana + grafanaProtocol: http + # In case a sub_path is used this needs to be added to the image renderer callback + grafanaSubPath: "" + # name of the image-renderer port on the pod + podPortName: http + # number of image-renderer replica sets to keep + revisionHistoryLimit: 10 + networkPolicy: + # Enable a NetworkPolicy to limit inbound traffic to only the created grafana pods + limitIngress: true + # Enable a NetworkPolicy to limit outbound traffic to only the created grafana pods + limitEgress: false + # Allow additional services to access image-renderer (eg. Prometheus operator when ServiceMonitor is enabled) + extraIngressSelectors: [] + resources: {} +# limits: +# cpu: 100m +# memory: 100Mi +# requests: +# cpu: 50m +# memory: 50Mi + ## Node labels for pod assignment + ## ref: https://kubernetes.io/docs/user-guide/node-selection/ + # + nodeSelector: {} + + ## Tolerations for pod assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + + ## Affinity for pod assignment (evaluated as template) + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## + affinity: {} + + ## Use an alternate scheduler, e.g. "stork". + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + # schedulerName: "default-scheduler" + + # Extra configmaps to mount in image-renderer pods + extraConfigmapMounts: [] + + # Extra secrets to mount in image-renderer pods + extraSecretMounts: [] + + # Extra volumes to mount in image-renderer pods + extraVolumeMounts: [] + + # Extra volumes for image-renderer pods + extraVolumes: [] + +networkPolicy: + ## @param networkPolicy.enabled Enable creation of NetworkPolicy resources. Only Ingress traffic is filtered for now. + ## + enabled: false + ## @param networkPolicy.allowExternal Don't require client label for connections + ## The Policy model to apply. When set to false, only pods with the correct + ## client label will have network access to grafana port defined. + ## When true, grafana will accept connections from any source + ## (with the correct destination port). + ## + ingress: true + ## @param networkPolicy.ingress When true enables the creation + ## an ingress network policy + ## + allowExternal: true + ## @param networkPolicy.explicitNamespacesSelector A Kubernetes LabelSelector to explicitly select namespaces from which traffic could be allowed + ## If explicitNamespacesSelector is missing or set to {}, only client Pods that are in the networkPolicy's namespace + ## and that match other criteria, the ones that have the good label, can reach the grafana. + ## But sometimes, we want the grafana to be accessible to clients from other namespaces, in this case, we can use this + ## LabelSelector to select these namespaces, note that the networkPolicy's namespace should also be explicitly added. + ## + ## Example: + ## explicitNamespacesSelector: + ## matchLabels: + ## role: frontend + ## matchExpressions: + ## - {key: role, operator: In, values: [frontend]} + ## + explicitNamespacesSelector: {} + ## + ## + ## + ## + ## + ## + egress: + ## @param networkPolicy.egress.enabled When enabled, an egress network policy will be + ## created allowing grafana to connect to external data sources from kubernetes cluster. + enabled: false + ## + ## @param networkPolicy.egress.blockDNSResolution When enabled, DNS resolution will be blocked + ## for all pods in the grafana namespace. + blockDNSResolution: false + ## + ## @param networkPolicy.egress.ports Add individual ports to be allowed by the egress + ports: [] + ## Add ports to the egress by specifying - port: + ## E.X. + ## - port: 80 + ## - port: 443 + ## + ## @param networkPolicy.egress.to Allow egress traffic to specific destinations + to: [] + ## Add destinations to the egress by specifying - ipBlock: + ## E.X. + ## to: + ## - namespaceSelector: + ## matchExpressions: + ## - {key: role, operator: In, values: [grafana]} + ## + ## + ## + ## + ## + +# Enable backward compatibility of kubernetes where version below 1.13 doesn't have the enableServiceLinks option +enableKubeBackwardCompatibility: false +useStatefulSet: false +# Create a dynamic manifests via values: +extraObjects: [] + # - apiVersion: "kubernetes-client.io/v1" + # kind: ExternalSecret + # metadata: + # name: grafana-secrets + # spec: + # backendType: gcpSecretsManager + # data: + # - key: grafana-admin-password + # name: adminPassword + +# assertNoLeakedSecrets is a helper function defined in _helpers.tpl that checks if secret +# values are not exposed in the rendered grafana.ini configmap. It is enabled by default. +# +# To pass values into grafana.ini without exposing them in a configmap, use variable expansion: +# https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#variable-expansion +# +# Alternatively, if you wish to allow secret values to be exposed in the rendered grafana.ini configmap, +# you can disable this check by setting assertNoLeakedSecrets to false. +assertNoLeakedSecrets: true diff --git a/test/examples/k6-perf/prometheus-grafana/values-prometheus.yaml b/test/examples/k6-perf/prometheus-grafana/values-prometheus.yaml new file mode 100644 index 0000000000..d9b4129530 --- /dev/null +++ b/test/examples/k6-perf/prometheus-grafana/values-prometheus.yaml @@ -0,0 +1,1308 @@ +# yaml-language-server: $schema=values.schema.json +# Default values for prometheus. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +rbac: + create: true + +podSecurityPolicy: + enabled: false + +imagePullSecrets: [] +# - name: "image-pull-secret" + +## Define serviceAccount names for components. Defaults to component's fully qualified name. +## +serviceAccounts: + server: + create: true + name: "" + annotations: {} + + ## Opt out of automounting Kubernetes API credentials. + ## It will be overriden by server.automountServiceAccountToken value, if set. + # automountServiceAccountToken: false + +## Additional labels to attach to all resources +commonMetaLabels: {} + +## Monitors ConfigMap changes and POSTs to a URL +## Ref: https://github.com/prometheus-operator/prometheus-operator/tree/main/cmd/prometheus-config-reloader +## +configmapReload: + ## URL for configmap-reload to use for reloads + ## + reloadUrl: "" + + ## env sets environment variables to pass to the container. Can be set as name/value pairs, + ## read from secrets or configmaps. + env: [] + # - name: SOMEVAR + # value: somevalue + # - name: PASSWORD + # valueFrom: + # secretKeyRef: + # name: mysecret + # key: password + # optional: false + + prometheus: + ## If false, the configmap-reload container will not be deployed + ## + enabled: true + + ## configmap-reload container name + ## + name: configmap-reload + + ## configmap-reload container image + ## + image: + repository: quay.io/prometheus-operator/prometheus-config-reloader + tag: v0.76.0 + # When digest is set to a non-empty value, images will be pulled by digest (regardless of tag value). + digest: "" + pullPolicy: IfNotPresent + + ## config-reloader's container port and port name for probes and metrics + containerPort: 8080 + containerPortName: metrics + + ## Additional configmap-reload container arguments + ## Set to null for argumentless flags + ## + extraArgs: {} + + ## Additional configmap-reload volume directories + ## + extraVolumeDirs: [] + + ## Additional configmap-reload volume mounts + ## + extraVolumeMounts: [] + + ## Additional configmap-reload mounts + ## + extraConfigmapMounts: [] + # - name: prometheus-alerts + # mountPath: /etc/alerts.d + # subPath: "" + # configMap: prometheus-alerts + # readOnly: true + + ## Security context to be added to configmap-reload container + containerSecurityContext: {} + + ## Settings for Prometheus reloader's readiness, liveness and startup probes + ## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ + ## + + livenessProbe: + httpGet: + path: /healthz + port: metrics + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 2 + + readinessProbe: + httpGet: + path: /healthz + port: metrics + scheme: HTTP + periodSeconds: 10 + + startupProbe: + enabled: false + httpGet: + path: /healthz + port: metrics + scheme: HTTP + periodSeconds: 10 + + ## configmap-reload resource requests and limits + ## Ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## + resources: {} + +server: + ## Prometheus server container name + ## + name: server + + ## Opt out of automounting Kubernetes API credentials. + ## If set it will override serviceAccounts.server.automountServiceAccountToken value for ServiceAccount. + # automountServiceAccountToken: false + + ## Use a ClusterRole (and ClusterRoleBinding) + ## - If set to false - we define a RoleBinding in the defined namespaces ONLY + ## + ## NB: because we need a Role with nonResourceURL's ("/metrics") - you must get someone with Cluster-admin privileges to define this role for you, before running with this setting enabled. + ## This makes prometheus work - for users who do not have ClusterAdmin privs, but wants prometheus to operate on their own namespaces, instead of clusterwide. + ## + ## You MUST also set namespaces to the ones you have access to and want monitored by Prometheus. + ## + # useExistingClusterRoleName: nameofclusterrole + + ## If set it will override prometheus.server.fullname value for ClusterRole and ClusterRoleBinding + ## + clusterRoleNameOverride: "" + + # Enable only the release namespace for monitoring. By default all namespaces are monitored. + # If releaseNamespace and namespaces are both set a merged list will be monitored. + releaseNamespace: false + + ## namespaces to monitor (instead of monitoring all - clusterwide). Needed if you want to run without Cluster-admin privileges. + # namespaces: + # - yournamespace + + # sidecarContainers - add more containers to prometheus server + # Key/Value where Key is the sidecar `- name: ` + # Example: + # sidecarContainers: + # webserver: + # image: nginx + # OR for adding OAuth authentication to Prometheus + # sidecarContainers: + # oauth-proxy: + # image: quay.io/oauth2-proxy/oauth2-proxy:v7.1.2 + # args: + # - --upstream=http://127.0.0.1:9090 + # - --http-address=0.0.0.0:8081 + # - ... + # ports: + # - containerPort: 8081 + # name: oauth-proxy + # protocol: TCP + # resources: {} + sidecarContainers: {} + + # sidecarTemplateValues - context to be used in template for sidecarContainers + # Example: + # sidecarTemplateValues: *your-custom-globals + # sidecarContainers: + # webserver: |- + # {{ include "webserver-container-template" . }} + # Template for `webserver-container-template` might looks like this: + # image: "{{ .Values.server.sidecarTemplateValues.repository }}:{{ .Values.server.sidecarTemplateValues.tag }}" + # ... + # + sidecarTemplateValues: {} + + ## Prometheus server container image + ## + image: + repository: quay.io/prometheus/prometheus + # if not set appVersion field from Chart.yaml is used + tag: "" + # When digest is set to a non-empty value, images will be pulled by digest (regardless of tag value). + digest: "" + pullPolicy: IfNotPresent + + ## Prometheus server command + ## + command: [] + + ## prometheus server priorityClassName + ## + priorityClassName: "" + + ## EnableServiceLinks indicates whether information about services should be injected + ## into pod's environment variables, matching the syntax of Docker links. + ## WARNING: the field is unsupported and will be skipped in K8s prior to v1.13.0. + ## + enableServiceLinks: true + + ## The URL prefix at which the container can be accessed. Useful in the case the '-web.external-url' includes a slug + ## so that the various internal URLs are still able to access as they are in the default case. + ## (Optional) + prefixURL: "" + + ## External URL which can access prometheus + ## Maybe same with Ingress host name + baseURL: "" + + ## Additional server container environment variables + ## + ## You specify this manually like you would a raw deployment manifest. + ## This means you can bind in environment variables from secrets. + ## + ## e.g. static environment variable: + ## - name: DEMO_GREETING + ## value: "Hello from the environment" + ## + ## e.g. secret environment variable: + ## - name: USERNAME + ## valueFrom: + ## secretKeyRef: + ## name: mysecret + ## key: username + env: [] + + # List of flags to override default parameters, e.g: + # - --enable-feature=agent + # - --storage.agent.retention.max-time=30m + # - --config.file=/etc/config/prometheus.yml + defaultFlagsOverride: [] + + extraFlags: + - web.enable-lifecycle + - web.enable-remote-write-receiver + ## web.enable-admin-api flag controls access to the administrative HTTP API which includes functionality such as + ## deleting time series. This is disabled by default. + # - web.enable-admin-api + ## + ## storage.tsdb.no-lockfile flag controls BD locking + # - storage.tsdb.no-lockfile + ## + ## storage.tsdb.wal-compression flag enables compression of the write-ahead log (WAL) + # - storage.tsdb.wal-compression + + ## Path to a configuration file on prometheus server container FS + configPath: /etc/config/prometheus.yml + + ### The data directory used by prometheus to set --storage.tsdb.path + ### When empty server.persistentVolume.mountPath is used instead + storagePath: "" + + global: + ## How frequently to scrape targets by default + ## + scrape_interval: 1m + ## How long until a scrape request times out + ## + scrape_timeout: 10s + ## How frequently to evaluate rules + ## + evaluation_interval: 1m + ## https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_write + ## + remoteWrite: [] + ## https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_read + ## + remoteRead: [] + + ## https://prometheus.io/docs/prometheus/latest/configuration/configuration/#tsdb + ## + tsdb: {} + # out_of_order_time_window: 0s + + ## https://prometheus.io/docs/prometheus/latest/configuration/configuration/#exemplars + ## Must be enabled via --enable-feature=exemplar-storage + ## + exemplars: {} + # max_exemplars: 100000 + + ## Custom HTTP headers for Liveness/Readiness/Startup Probe + ## + ## Useful for providing HTTP Basic Auth to healthchecks + probeHeaders: [] + # - name: "Authorization" + # value: "Bearer ABCDEabcde12345" + + ## Additional Prometheus server container arguments + ## Set to null for argumentless flags + ## + # extraArgs: {} + # web.enable-remote-write-receiver: null + extraArgs: { + enable-feature: native-histograms + } + + ## Additional InitContainers to initialize the pod + ## + extraInitContainers: [] + + ## Additional Prometheus server Volume mounts + ## + extraVolumeMounts: [] + + ## Additional Prometheus server Volumes + ## + extraVolumes: [] + + ## Additional Prometheus server hostPath mounts + ## + extraHostPathMounts: [] + # - name: certs-dir + # mountPath: /etc/kubernetes/certs + # subPath: "" + # hostPath: /etc/kubernetes/certs + # readOnly: true + + extraConfigmapMounts: [] + # - name: certs-configmap + # mountPath: /prometheus + # subPath: "" + # configMap: certs-configmap + # readOnly: true + + ## Additional Prometheus server Secret mounts + # Defines additional mounts with secrets. Secrets must be manually created in the namespace. + extraSecretMounts: [] + # - name: secret-files + # mountPath: /etc/secrets + # subPath: "" + # secretName: prom-secret-files + # readOnly: true + + ## ConfigMap override where fullname is {{.Release.Name}}-{{.Values.server.configMapOverrideName}} + ## Defining configMapOverrideName will cause templates/server-configmap.yaml + ## to NOT generate a ConfigMap resource + ## + configMapOverrideName: "" + + ## Extra labels for Prometheus server ConfigMap (ConfigMap that holds serverFiles) + extraConfigmapLabels: {} + + ## Override the prometheus.server.fullname for all objects related to the Prometheus server + fullnameOverride: "" + + ingress: + ## If true, Prometheus server Ingress will be created + ## + enabled: false + + # For Kubernetes >= 1.18 you should specify the ingress-controller via the field ingressClassName + # See https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/#specifying-the-class-of-an-ingress + # ingressClassName: nginx + + ## Prometheus server Ingress annotations + ## + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: 'true' + + ## Prometheus server Ingress additional labels + ## + extraLabels: {} + + ## Redirect ingress to an additional defined port on the service + # servicePort: 8081 + + ## Prometheus server Ingress hostnames with optional path + ## Must be provided if Ingress is enabled + ## + hosts: [] + # - prometheus.domain.com + # - domain.com/prometheus + + path: / + + # pathType is only for k8s >= 1.18 + pathType: Prefix + + ## Extra paths to prepend to every host configuration. This is useful when working with annotation based services. + extraPaths: [] + # - path: /* + # backend: + # serviceName: ssl-redirect + # servicePort: use-annotation + + ## Prometheus server Ingress TLS configuration + ## Secrets must be manually created in the namespace + ## + tls: [] + # - secretName: prometheus-server-tls + # hosts: + # - prometheus.domain.com + + ## Server Deployment Strategy type + strategy: + type: Recreate + + ## hostAliases allows adding entries to /etc/hosts inside the containers + hostAliases: [] + # - ip: "127.0.0.1" + # hostnames: + # - "example.com" + + ## Node tolerations for server scheduling to nodes with taints + ## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ + ## + tolerations: [] + # - key: "key" + # operator: "Equal|Exists" + # value: "value" + # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)" + + ## Node labels for Prometheus server pod assignment + ## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ + ## + nodeSelector: {} + + ## Pod affinity + ## + affinity: {} + + ## Pod anti-affinity can prevent the scheduler from placing Prometheus server replicas on the same node. + ## The value "soft" means that the scheduler should *prefer* to not schedule two replica pods onto the same node but no guarantee is provided. + ## The value "hard" means that the scheduler is *required* to not schedule two replica pods onto the same node. + ## The default value "" will disable pod anti-affinity so that no anti-affinity rules will be configured (unless set in `server.affinity`). + ## + podAntiAffinity: "" + + ## If anti-affinity is enabled sets the topologyKey to use for anti-affinity. + ## This can be changed to, for example, failure-domain.beta.kubernetes.io/zone + ## + podAntiAffinityTopologyKey: kubernetes.io/hostname + + ## Pod topology spread constraints + ## ref. https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/ + topologySpreadConstraints: [] + + ## PodDisruptionBudget settings + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ + ## + podDisruptionBudget: + enabled: false + maxUnavailable: 1 + # minAvailable: 1 + ## unhealthyPodEvictionPolicy is available since 1.27.0 (beta) + ## https://kubernetes.io/docs/tasks/run-application/configure-pdb/#unhealthy-pod-eviction-policy + # unhealthyPodEvictionPolicy: IfHealthyBudget + + ## Use an alternate scheduler, e.g. "stork". + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + # schedulerName: + + persistentVolume: + ## If true, Prometheus server will create/use a Persistent Volume Claim + ## If false, use emptyDir + ## + enabled: true + + ## If set it will override the name of the created persistent volume claim + ## generated by the stateful set. + ## + statefulSetNameOverride: "" + + ## Prometheus server data Persistent Volume access modes + ## Must match those of existing PV or dynamic provisioner + ## Ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + accessModes: + - ReadWriteOnce + + ## Prometheus server data Persistent Volume labels + ## + labels: {} + + ## Prometheus server data Persistent Volume annotations + ## + annotations: {} + + ## Prometheus server data Persistent Volume existing claim name + ## Requires server.persistentVolume.enabled: true + ## If defined, PVC must be created manually before volume will be bound + existingClaim: "" + + ## Prometheus server data Persistent Volume mount root path + ## + mountPath: /data + + ## Prometheus server data Persistent Volume size + ## + size: 8Gi + + ## Prometheus server data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + + ## Prometheus server data Persistent Volume Binding Mode + ## If defined, volumeBindingMode: + ## If undefined (the default) or set to null, no volumeBindingMode spec is + ## set, choosing the default mode. + ## + # volumeBindingMode: "" + + ## Subdirectory of Prometheus server data Persistent Volume to mount + ## Useful if the volume's root directory is not empty + ## + subPath: "" + + ## Persistent Volume Claim Selector + ## Useful if Persistent Volumes have been provisioned in advance + ## Ref: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#selector + ## + # selector: + # matchLabels: + # release: "stable" + # matchExpressions: + # - { key: environment, operator: In, values: [ dev ] } + + ## Persistent Volume Name + ## Useful if Persistent Volumes have been provisioned in advance and you want to use a specific one + ## + # volumeName: "" + + emptyDir: + ## Prometheus server emptyDir volume size limit + ## + sizeLimit: "" + + ## Annotations to be added to Prometheus server pods + ## + podAnnotations: {} + # iam.amazonaws.com/role: prometheus + + ## Labels to be added to Prometheus server pods + ## + podLabels: {} + + ## Prometheus AlertManager configuration + ## + alertmanagers: [] + + ## Specify if a Pod Security Policy for node-exporter must be created + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/ + ## + podSecurityPolicy: + annotations: {} + ## Specify pod annotations + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#apparmor + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#seccomp + ## Ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#sysctl + ## + # seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*' + # seccomp.security.alpha.kubernetes.io/defaultProfileName: 'docker/default' + # apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default' + + ## Use a StatefulSet if replicaCount needs to be greater than 1 (see below) + ## + replicaCount: 1 + + ## Number of old history to retain to allow rollback + ## Default Kubernetes value is set to 10 + ## + revisionHistoryLimit: 10 + + ## Annotations to be added to ConfigMap + ## + configMapAnnotations: {} + + ## Annotations to be added to deployment + ## + deploymentAnnotations: {} + + statefulSet: + ## If true, use a statefulset instead of a deployment for pod management. + ## This allows to scale replicas to more than 1 pod + ## + enabled: false + + annotations: {} + labels: {} + podManagementPolicy: OrderedReady + + ## Alertmanager headless service to use for the statefulset + ## + headless: + annotations: {} + labels: {} + servicePort: 80 + ## Enable gRPC port on service to allow auto discovery with thanos-querier + gRPC: + enabled: false + servicePort: 10901 + # nodePort: 10901 + + ## Statefulset's persistent volume claim retention policy + ## pvcDeleteOnStsDelete and pvcDeleteOnStsScale determine whether + ## statefulset's PVCs are deleted (true) or retained (false) on scaling down + ## and deleting statefulset, respectively. Requires 1.27.0+. + ## Ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#persistentvolumeclaim-retention + ## + pvcDeleteOnStsDelete: false + pvcDeleteOnStsScale: false + + ## Prometheus server readiness and liveness probe initial delay and timeout + ## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ + ## + tcpSocketProbeEnabled: false + probeScheme: HTTP + readinessProbeInitialDelay: 30 + readinessProbePeriodSeconds: 5 + readinessProbeTimeout: 4 + readinessProbeFailureThreshold: 3 + readinessProbeSuccessThreshold: 1 + livenessProbeInitialDelay: 30 + livenessProbePeriodSeconds: 15 + livenessProbeTimeout: 10 + livenessProbeFailureThreshold: 3 + livenessProbeSuccessThreshold: 1 + startupProbe: + enabled: false + periodSeconds: 5 + failureThreshold: 30 + timeoutSeconds: 10 + + ## Prometheus server resource requests and limits + ## Ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## + resources: {} + # limits: + # cpu: 500m + # memory: 512Mi + # requests: + # cpu: 500m + # memory: 512Mi + + # Required for use in managed kubernetes clusters (such as AWS EKS) with custom CNI (such as calico), + # because control-plane managed by AWS cannot communicate with pods' IP CIDR and admission webhooks are not working + ## + hostNetwork: false + + # When hostNetwork is enabled, this will set to ClusterFirstWithHostNet automatically + dnsPolicy: ClusterFirst + + # Use hostPort + # hostPort: 9090 + + # Use portName + portName: "" + + ## Vertical Pod Autoscaler config + ## Ref: https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler + verticalAutoscaler: + ## If true a VPA object will be created for the controller (either StatefulSet or Deployemnt, based on above configs) + enabled: false + # updateMode: "Auto" + # containerPolicies: + # - containerName: 'prometheus-server' + + # Custom DNS configuration to be added to prometheus server pods + dnsConfig: {} + # nameservers: + # - 1.2.3.4 + # searches: + # - ns1.svc.cluster-domain.example + # - my.dns.search.suffix + # options: + # - name: ndots + # value: "2" + # - name: edns0 + + ## Security context to be added to server pods + ## + securityContext: + runAsUser: 65534 + runAsNonRoot: true + runAsGroup: 65534 + fsGroup: 65534 + + ## Security context to be added to server container + ## + containerSecurityContext: {} + + service: + ## If false, no Service will be created for the Prometheus server + ## + enabled: true + + annotations: {} + labels: {} + clusterIP: "" + + ## List of IP addresses at which the Prometheus server service is available + ## Ref: https://kubernetes.io/docs/concepts/services-networking/service/#external-ips + ## + externalIPs: [] + + loadBalancerIP: "" + loadBalancerSourceRanges: [] + servicePort: 80 + sessionAffinity: None + type: ClusterIP + + ## Enable gRPC port on service to allow auto discovery with thanos-querier + gRPC: + enabled: false + servicePort: 10901 + # nodePort: 10901 + + ## If using a statefulSet (statefulSet.enabled=true), configure the + ## service to connect to a specific replica to have a consistent view + ## of the data. + statefulsetReplica: + enabled: false + replica: 0 + + ## Additional port to define in the Service + additionalPorts: [] + # additionalPorts: + # - name: authenticated + # port: 8081 + # targetPort: 8081 + + ## Prometheus server pod termination grace period + ## + terminationGracePeriodSeconds: 300 + + ## Prometheus data retention period (default if not specified is 15 days) + ## + retention: "15d" + + ## Prometheus' data retention size. Supported units: B, KB, MB, GB, TB, PB, EB. + ## + retentionSize: "" + +## Prometheus server ConfigMap entries for rule files (allow prometheus labels interpolation) +ruleFiles: {} + +## Prometheus server ConfigMap entries for scrape_config_files +## (allows scrape configs defined in additional files) +## +scrapeConfigFiles: [] + +## Prometheus server ConfigMap entries +## +serverFiles: + ## Alerts configuration + ## Ref: https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/ + alerting_rules.yml: {} + # groups: + # - name: Instances + # rules: + # - alert: InstanceDown + # expr: up == 0 + # for: 5m + # labels: + # severity: page + # annotations: + # description: '{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minutes.' + # summary: 'Instance {{ $labels.instance }} down' + ## DEPRECATED DEFAULT VALUE, unless explicitly naming your files, please use alerting_rules.yml + alerts: {} + + ## Records configuration + ## Ref: https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/ + recording_rules.yml: {} + ## DEPRECATED DEFAULT VALUE, unless explicitly naming your files, please use recording_rules.yml + rules: {} + + prometheus.yml: + rule_files: + - /etc/config/recording_rules.yml + - /etc/config/alerting_rules.yml + ## Below two files are DEPRECATED will be removed from this default values file + - /etc/config/rules + - /etc/config/alerts + + scrape_configs: + - job_name: prometheus + static_configs: + - targets: + - localhost:9090 + + # A scrape configuration for running Prometheus on a Kubernetes cluster. + # This uses separate scrape configs for cluster components (i.e. API server, node) + # and services to allow each to use different authentication configs. + # + # Kubernetes labels will be added as Prometheus labels on metrics via the + # `labelmap` relabeling action. + + # Scrape config for API servers. + # + # Kubernetes exposes API servers as endpoints to the default/kubernetes + # service so this uses `endpoints` role and uses relabelling to only keep + # the endpoints associated with the default/kubernetes service using the + # default named port `https`. This works for single API server deployments as + # well as HA API server deployments. + - job_name: 'kubernetes-apiservers' + + kubernetes_sd_configs: + - role: endpoints + + # Default to scraping over https. If required, just disable this or change to + # `http`. + scheme: https + + # This TLS & bearer token file config is used to connect to the actual scrape + # endpoints for cluster components. This is separate to discovery auth + # configuration because discovery & scraping are two separate concerns in + # Prometheus. The discovery auth config is automatic if Prometheus runs inside + # the cluster. Otherwise, more config options have to be provided within the + # . + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + # If your node certificates are self-signed or use a different CA to the + # master CA, then disable certificate verification below. Note that + # certificate verification is an integral part of a secure infrastructure + # so this should only be disabled in a controlled environment. You can + # disable certificate verification by uncommenting the line below. + # + insecure_skip_verify: true + bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + + # Keep only the default/kubernetes service endpoints for the https port. This + # will add targets for each API server which Kubernetes adds an endpoint to + # the default/kubernetes service. + relabel_configs: + - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name] + action: keep + regex: default;kubernetes;https + + - job_name: 'kubernetes-nodes' + + # Default to scraping over https. If required, just disable this or change to + # `http`. + scheme: https + + # This TLS & bearer token file config is used to connect to the actual scrape + # endpoints for cluster components. This is separate to discovery auth + # configuration because discovery & scraping are two separate concerns in + # Prometheus. The discovery auth config is automatic if Prometheus runs inside + # the cluster. Otherwise, more config options have to be provided within the + # . + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + # If your node certificates are self-signed or use a different CA to the + # master CA, then disable certificate verification below. Note that + # certificate verification is an integral part of a secure infrastructure + # so this should only be disabled in a controlled environment. You can + # disable certificate verification by uncommenting the line below. + # + insecure_skip_verify: true + bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + + kubernetes_sd_configs: + - role: node + + relabel_configs: + - action: labelmap + regex: __meta_kubernetes_node_label_(.+) + - target_label: __address__ + replacement: kubernetes.default.svc:443 + - source_labels: [__meta_kubernetes_node_name] + regex: (.+) + target_label: __metrics_path__ + replacement: /api/v1/nodes/$1/proxy/metrics + + + - job_name: 'kubernetes-nodes-cadvisor' + + # Default to scraping over https. If required, just disable this or change to + # `http`. + scheme: https + + # This TLS & bearer token file config is used to connect to the actual scrape + # endpoints for cluster components. This is separate to discovery auth + # configuration because discovery & scraping are two separate concerns in + # Prometheus. The discovery auth config is automatic if Prometheus runs inside + # the cluster. Otherwise, more config options have to be provided within the + # . + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + # If your node certificates are self-signed or use a different CA to the + # master CA, then disable certificate verification below. Note that + # certificate verification is an integral part of a secure infrastructure + # so this should only be disabled in a controlled environment. You can + # disable certificate verification by uncommenting the line below. + # + insecure_skip_verify: true + bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + + kubernetes_sd_configs: + - role: node + + # This configuration will work only on kubelet 1.7.3+ + # As the scrape endpoints for cAdvisor have changed + # if you are using older version you need to change the replacement to + # replacement: /api/v1/nodes/$1:4194/proxy/metrics + # more info here https://github.com/coreos/prometheus-operator/issues/633 + relabel_configs: + - action: labelmap + regex: __meta_kubernetes_node_label_(.+) + - target_label: __address__ + replacement: kubernetes.default.svc:443 + - source_labels: [__meta_kubernetes_node_name] + regex: (.+) + target_label: __metrics_path__ + replacement: /api/v1/nodes/$1/proxy/metrics/cadvisor + + # Metric relabel configs to apply to samples before ingestion. + # [Metric Relabeling](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs) + # metric_relabel_configs: + # - action: labeldrop + # regex: (kubernetes_io_hostname|failure_domain_beta_kubernetes_io_region|beta_kubernetes_io_os|beta_kubernetes_io_arch|beta_kubernetes_io_instance_type|failure_domain_beta_kubernetes_io_zone) + + # Scrape config for service endpoints. + # + # The relabeling allows the actual service scrape endpoint to be configured + # via the following annotations: + # + # * `prometheus.io/scrape`: Only scrape services that have a value of + # `true`, except if `prometheus.io/scrape-slow` is set to `true` as well. + # * `prometheus.io/scheme`: If the metrics endpoint is secured then you will need + # to set this to `https` & most likely set the `tls_config` of the scrape config. + # * `prometheus.io/path`: If the metrics path is not `/metrics` override this. + # * `prometheus.io/port`: If the metrics are exposed on a different port to the + # service then set this appropriately. + # * `prometheus.io/param_`: If the metrics endpoint uses parameters + # then you can set any parameter + - job_name: 'kubernetes-service-endpoints' + honor_labels: true + + kubernetes_sd_configs: + - role: endpoints + + relabel_configs: + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape] + action: keep + regex: true + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape_slow] + action: drop + regex: true + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme] + action: replace + target_label: __scheme__ + regex: (https?) + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path] + action: replace + target_label: __metrics_path__ + regex: (.+) + - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port] + action: replace + target_label: __address__ + regex: (.+?)(?::\d+)?;(\d+) + replacement: $1:$2 + - action: labelmap + regex: __meta_kubernetes_service_annotation_prometheus_io_param_(.+) + replacement: __param_$1 + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - source_labels: [__meta_kubernetes_namespace] + action: replace + target_label: namespace + - source_labels: [__meta_kubernetes_service_name] + action: replace + target_label: service + - source_labels: [__meta_kubernetes_pod_node_name] + action: replace + target_label: node + + # Scrape config for slow service endpoints; same as above, but with a larger + # timeout and a larger interval + # + # The relabeling allows the actual service scrape endpoint to be configured + # via the following annotations: + # + # * `prometheus.io/scrape-slow`: Only scrape services that have a value of `true` + # * `prometheus.io/scheme`: If the metrics endpoint is secured then you will need + # to set this to `https` & most likely set the `tls_config` of the scrape config. + # * `prometheus.io/path`: If the metrics path is not `/metrics` override this. + # * `prometheus.io/port`: If the metrics are exposed on a different port to the + # service then set this appropriately. + # * `prometheus.io/param_`: If the metrics endpoint uses parameters + # then you can set any parameter + - job_name: 'kubernetes-service-endpoints-slow' + honor_labels: true + + scrape_interval: 5m + scrape_timeout: 30s + + kubernetes_sd_configs: + - role: endpoints + + relabel_configs: + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape_slow] + action: keep + regex: true + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme] + action: replace + target_label: __scheme__ + regex: (https?) + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path] + action: replace + target_label: __metrics_path__ + regex: (.+) + - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port] + action: replace + target_label: __address__ + regex: (.+?)(?::\d+)?;(\d+) + replacement: $1:$2 + - action: labelmap + regex: __meta_kubernetes_service_annotation_prometheus_io_param_(.+) + replacement: __param_$1 + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - source_labels: [__meta_kubernetes_namespace] + action: replace + target_label: namespace + - source_labels: [__meta_kubernetes_service_name] + action: replace + target_label: service + - source_labels: [__meta_kubernetes_pod_node_name] + action: replace + target_label: node + + - job_name: 'prometheus-pushgateway' + honor_labels: true + + kubernetes_sd_configs: + - role: service + + relabel_configs: + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe] + action: keep + regex: pushgateway + + # Example scrape config for probing services via the Blackbox Exporter. + # + # The relabeling allows the actual service scrape endpoint to be configured + # via the following annotations: + # + # * `prometheus.io/probe`: Only probe services that have a value of `true` + - job_name: 'kubernetes-services' + honor_labels: true + + metrics_path: /probe + params: + module: [http_2xx] + + kubernetes_sd_configs: + - role: service + + relabel_configs: + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe] + action: keep + regex: true + - source_labels: [__address__] + target_label: __param_target + - target_label: __address__ + replacement: blackbox + - source_labels: [__param_target] + target_label: instance + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - source_labels: [__meta_kubernetes_namespace] + target_label: namespace + - source_labels: [__meta_kubernetes_service_name] + target_label: service + + # Example scrape config for pods + # + # The relabeling allows the actual pod scrape endpoint to be configured via the + # following annotations: + # + # * `prometheus.io/scrape`: Only scrape pods that have a value of `true`, + # except if `prometheus.io/scrape-slow` is set to `true` as well. + # * `prometheus.io/scheme`: If the metrics endpoint is secured then you will need + # to set this to `https` & most likely set the `tls_config` of the scrape config. + # * `prometheus.io/path`: If the metrics path is not `/metrics` override this. + # * `prometheus.io/port`: Scrape the pod on the indicated port instead of the default of `9102`. + - job_name: 'kubernetes-pods' + honor_labels: true + + kubernetes_sd_configs: + - role: pod + + relabel_configs: + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape] + action: keep + regex: true + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape_slow] + action: drop + regex: true + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scheme] + action: replace + regex: (https?) + target_label: __scheme__ + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path] + action: replace + target_label: __metrics_path__ + regex: (.+) + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_port, __meta_kubernetes_pod_ip] + action: replace + regex: (\d+);(([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}) + replacement: '[$2]:$1' + target_label: __address__ + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_port, __meta_kubernetes_pod_ip] + action: replace + regex: (\d+);((([0-9]+?)(\.|$)){4}) + replacement: $2:$1 + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_pod_annotation_prometheus_io_param_(.+) + replacement: __param_$1 + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) + - source_labels: [__meta_kubernetes_namespace] + action: replace + target_label: namespace + - source_labels: [__meta_kubernetes_pod_name] + action: replace + target_label: pod + - source_labels: [__meta_kubernetes_pod_phase] + regex: Pending|Succeeded|Failed|Completed + action: drop + - source_labels: [__meta_kubernetes_pod_node_name] + action: replace + target_label: node + + # Example Scrape config for pods which should be scraped slower. An useful example + # would be stackriver-exporter which queries an API on every scrape of the pod + # + # The relabeling allows the actual pod scrape endpoint to be configured via the + # following annotations: + # + # * `prometheus.io/scrape-slow`: Only scrape pods that have a value of `true` + # * `prometheus.io/scheme`: If the metrics endpoint is secured then you will need + # to set this to `https` & most likely set the `tls_config` of the scrape config. + # * `prometheus.io/path`: If the metrics path is not `/metrics` override this. + # * `prometheus.io/port`: Scrape the pod on the indicated port instead of the default of `9102`. + - job_name: 'kubernetes-pods-slow' + honor_labels: true + + scrape_interval: 5m + scrape_timeout: 30s + + kubernetes_sd_configs: + - role: pod + + relabel_configs: + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape_slow] + action: keep + regex: true + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scheme] + action: replace + regex: (https?) + target_label: __scheme__ + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path] + action: replace + target_label: __metrics_path__ + regex: (.+) + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_port, __meta_kubernetes_pod_ip] + action: replace + regex: (\d+);(([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}) + replacement: '[$2]:$1' + target_label: __address__ + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_port, __meta_kubernetes_pod_ip] + action: replace + regex: (\d+);((([0-9]+?)(\.|$)){4}) + replacement: $2:$1 + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_pod_annotation_prometheus_io_param_(.+) + replacement: __param_$1 + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) + - source_labels: [__meta_kubernetes_namespace] + action: replace + target_label: namespace + - source_labels: [__meta_kubernetes_pod_name] + action: replace + target_label: pod + - source_labels: [__meta_kubernetes_pod_phase] + regex: Pending|Succeeded|Failed|Completed + action: drop + - source_labels: [__meta_kubernetes_pod_node_name] + action: replace + target_label: node + +# adds additional scrape configs to prometheus.yml +# must be a string so you have to add a | after extraScrapeConfigs: +# example adds prometheus-blackbox-exporter scrape config +extraScrapeConfigs: "" + # - job_name: 'prometheus-blackbox-exporter' + # metrics_path: /probe + # params: + # module: [http_2xx] + # static_configs: + # - targets: + # - https://example.com + # relabel_configs: + # - source_labels: [__address__] + # target_label: __param_target + # - source_labels: [__param_target] + # target_label: instance + # - target_label: __address__ + # replacement: prometheus-blackbox-exporter:9115 + +# Adds option to add alert_relabel_configs to avoid duplicate alerts in alertmanager +# useful in H/A prometheus with different external labels but the same alerts +alertRelabelConfigs: {} + # alert_relabel_configs: + # - source_labels: [dc] + # regex: (.+)\d+ + # target_label: dc + +networkPolicy: + ## Enable creation of NetworkPolicy resources. + ## + enabled: false + +# Force namespace of namespaced resources +forceNamespace: "" + +# Extra manifests to deploy as an array +extraManifests: [] + # - | + # apiVersion: v1 + # kind: ConfigMap + # metadata: + # labels: + # name: prometheus-extra + # data: + # extra-data: "value" + +# Configuration of subcharts defined in Chart.yaml + +## alertmanager sub-chart configurable values +## Please see https://github.com/prometheus-community/helm-charts/tree/main/charts/alertmanager +## +alertmanager: + ## If false, alertmanager will not be installed + ## + enabled: true + + persistence: + size: 2Gi + + podSecurityContext: + runAsUser: 65534 + runAsNonRoot: true + runAsGroup: 65534 + fsGroup: 65534 + +## kube-state-metrics sub-chart configurable values +## Please see https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-state-metrics +## +kube-state-metrics: + ## If false, kube-state-metrics sub-chart will not be installed + ## + enabled: true + +## prometheus-node-exporter sub-chart configurable values +## Please see https://github.com/prometheus-community/helm-charts/tree/main/charts/prometheus-node-exporter +## +prometheus-node-exporter: + ## If false, node-exporter will not be installed + ## + enabled: true + + rbac: + pspEnabled: false + + containerSecurityContext: + allowPrivilegeEscalation: false + +## prometheus-pushgateway sub-chart configurable values +## Please see https://github.com/prometheus-community/helm-charts/tree/main/charts/prometheus-pushgateway +## +prometheus-pushgateway: + ## If false, pushgateway will not be installed + ## + enabled: true + + # Optional service annotations + serviceAnnotations: + prometheus.io/probe: pushgateway diff --git a/test/k6/executor-tests/k6-perf-test.js b/test/k6/executor-tests/k6-perf-test.js new file mode 100644 index 0000000000..ec72656f5c --- /dev/null +++ b/test/k6/executor-tests/k6-perf-test.js @@ -0,0 +1,11 @@ +import http from 'k6/http'; +import { check } from 'k6'; + +export default function () { + const res = http.get('https://testkube-test-page-lipsum.pages.dev/'); + check(res, { 'status was 200': (r) => r.status == 200 }); + check(res, { + 'verify partial text': (r) => + r.body.includes('Testkube test page - Lipsum'), + }); +} From a30cf077043fe9cbf014806cfe330b288192c4b1 Mon Sep 17 00:00:00 2001 From: Vladislav Sukhin Date: Thu, 21 Nov 2024 09:54:57 +0300 Subject: [PATCH 12/30] fix: list selector test workflows and check for empty list (#6046) Signed-off-by: Vladislav Sukhin --- cmd/tcl/testworkflow-toolkit/commands/execute.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cmd/tcl/testworkflow-toolkit/commands/execute.go b/cmd/tcl/testworkflow-toolkit/commands/execute.go index ce47f57f83..d85e069fb0 100644 --- a/cmd/tcl/testworkflow-toolkit/commands/execute.go +++ b/cmd/tcl/testworkflow-toolkit/commands/execute.go @@ -399,11 +399,22 @@ func NewExecuteCmd() *cobra.Command { ui.Fail(errors.Wrap(err, "error listing test workflows using selector")) } + if len(testWorkflowsList) > 0 { + ui.Info("List of test workflows found for selector specification:") + } else { + ui.Warn("No test workflows found for selector specification") + } + for _, item := range testWorkflowsList { testWorkflowNames = append(testWorkflowNames, item.Name) + ui.Info("- " + item.Name) } } + if len((testWorkflowNames)) == 0 { + ui.Fail(errors.New("no test workflows to run")) + } + // Resolve the params params, err := commontcl.GetParamsSpec(w.Matrix, w.Shards, w.Count, w.MaxCount, baseMachine) if err != nil { From de1ea6e7fecdd7a6dc2279a86446a58f14c6a122 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:13:13 +0300 Subject: [PATCH 13/30] build(deps): bump github/codeql-action from 3.27.4 to 3.27.5 (#6048) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.27.4 to 3.27.5. - [Release notes](https://github.com/github/codeql-action/releases) - [Commits](https://github.com/github/codeql-action/compare/v3.27.4...v3.27.5) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 91b651ca85..50a453db4b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -52,7 +52,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3.27.4 + uses: github/codeql-action/init@v3.27.5 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -82,4 +82,4 @@ jobs: test-${{ runner.os }}-go- - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3.27.4 + uses: github/codeql-action/analyze@v3.27.5 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 97803f73ec..469486e3e1 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -67,6 +67,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@a1695c562bbfa68dc5ab58c9b5e9f616b52bf5be # v2.25.3 + uses: github/codeql-action/upload-sarif@f8e782af5670be7b12c0acb5b25c1c5703b931b9 # v2.25.3 with: sarif_file: results.sarif From c805f6b0c9ecbf4b2629d3a8bba31583693dd3b2 Mon Sep 17 00:00:00 2001 From: Jacek Wysocki Date: Fri, 22 Nov 2024 08:11:16 +0100 Subject: [PATCH 14/30] feat: CLI license Diagnostics (#6040) * feat: diagnostics * feat: diagnostics * chore: changed results * chore: removed mess * feat: CLI renderer updates * feat: CLI renderer updates -k adahsjdhsajh * fix: golang ci fixes * feat: added deps * feat: offline license validation * feat: added separated commands * validators update * feat: ui polishing * fix: go mod tidy * fix: golang ci fixes and moving public key to be set through build process --- .builds-windows.goreleaser.yml | 1 + .../commands/common/cloudcontext.go | 2 +- .../commands/common/helper.go | 199 +++++++++++++++++- .../commands/common/render/common.go | 2 +- cmd/kubectl-testkube/commands/diagnostics.go | 40 ++++ .../commands/diagnostics/install.go | 40 ++++ .../commands/diagnostics/license.go | 72 +++++++ cmd/kubectl-testkube/commands/help.go | 7 +- cmd/kubectl-testkube/commands/root.go | 1 + go.mod | 78 ++++--- go.sum | 188 +++++++++-------- pkg/diagnostics/diagnostics.go | 88 ++++++++ pkg/diagnostics/loader/license_loader.go | 77 +++++++ pkg/diagnostics/renderer/cli.go | 57 +++++ pkg/diagnostics/renderer/interface.go | 9 + pkg/diagnostics/renderer/json.go | 30 +++ pkg/diagnostics/validators/deps/errors.go | 24 +++ .../validators/deps/helm_binary.go | 26 +++ .../validators/deps/kuebctl_binary.go | 55 +++++ pkg/diagnostics/validators/deps/utils.go | 15 ++ pkg/diagnostics/validators/error.go | 48 +++++ pkg/diagnostics/validators/group.go | 13 ++ pkg/diagnostics/validators/interface.go | 8 + pkg/diagnostics/validators/kinds.go | 46 ++++ pkg/diagnostics/validators/license/client.go | 70 ++++++ .../validators/license/client_test.go | 69 ++++++ pkg/diagnostics/validators/license/errors.go | 56 +++++ .../validators/license/file_validator.go | 42 ++++ .../validators/license/keygensh_validator.go | 63 ++++++ .../license/offline_key_validator.go | 39 ++++ .../license/offline_license_validator.go | 167 +++++++++++++++ .../license/offline_license_validator_test.go | 54 +++++ .../license/online_key_validator.go | 47 +++++ .../validators/license/validator_test.go | 14 ++ pkg/diagnostics/validators/mock/invalid.go | 11 + .../validators/mock/invalid_multi.go | 15 ++ pkg/diagnostics/validators/mock/valid.go | 11 + pkg/diagnostics/validators/requirements.go | 8 + pkg/diagnostics/validators/result.go | 87 ++++++++ pkg/ui/printers.go | 1 - 40 files changed, 1747 insertions(+), 133 deletions(-) create mode 100644 cmd/kubectl-testkube/commands/diagnostics.go create mode 100644 cmd/kubectl-testkube/commands/diagnostics/install.go create mode 100644 cmd/kubectl-testkube/commands/diagnostics/license.go create mode 100644 pkg/diagnostics/diagnostics.go create mode 100644 pkg/diagnostics/loader/license_loader.go create mode 100644 pkg/diagnostics/renderer/cli.go create mode 100644 pkg/diagnostics/renderer/interface.go create mode 100644 pkg/diagnostics/renderer/json.go create mode 100644 pkg/diagnostics/validators/deps/errors.go create mode 100644 pkg/diagnostics/validators/deps/helm_binary.go create mode 100644 pkg/diagnostics/validators/deps/kuebctl_binary.go create mode 100644 pkg/diagnostics/validators/deps/utils.go create mode 100644 pkg/diagnostics/validators/error.go create mode 100644 pkg/diagnostics/validators/group.go create mode 100644 pkg/diagnostics/validators/interface.go create mode 100644 pkg/diagnostics/validators/kinds.go create mode 100644 pkg/diagnostics/validators/license/client.go create mode 100644 pkg/diagnostics/validators/license/client_test.go create mode 100644 pkg/diagnostics/validators/license/errors.go create mode 100644 pkg/diagnostics/validators/license/file_validator.go create mode 100644 pkg/diagnostics/validators/license/keygensh_validator.go create mode 100644 pkg/diagnostics/validators/license/offline_key_validator.go create mode 100644 pkg/diagnostics/validators/license/offline_license_validator.go create mode 100644 pkg/diagnostics/validators/license/offline_license_validator_test.go create mode 100644 pkg/diagnostics/validators/license/online_key_validator.go create mode 100644 pkg/diagnostics/validators/license/validator_test.go create mode 100644 pkg/diagnostics/validators/mock/invalid.go create mode 100644 pkg/diagnostics/validators/mock/invalid_multi.go create mode 100644 pkg/diagnostics/validators/mock/valid.go create mode 100644 pkg/diagnostics/validators/requirements.go create mode 100644 pkg/diagnostics/validators/result.go diff --git a/.builds-windows.goreleaser.yml b/.builds-windows.goreleaser.yml index ab186df3b8..36c46e726e 100644 --- a/.builds-windows.goreleaser.yml +++ b/.builds-windows.goreleaser.yml @@ -21,5 +21,6 @@ builds: - -X main.builtBy=goreleaser - -X github.com/kubeshop/testkube/pkg/telemetry.TestkubeMeasurementID={{.Env.ANALYTICS_TRACKING_ID}} - -X github.com/kubeshop/testkube/pkg/telemetry.TestkubeMeasurementSecret={{.Env.ANALYTICS_API_KEY}} + - -X github.com/kubeshop/testkube/pkg/diagnostics/validators/license.KeygenOfflinePublicKey={{.Env.KEYGEN_PUBLIC_KEY}} archives: - format: binary diff --git a/cmd/kubectl-testkube/commands/common/cloudcontext.go b/cmd/kubectl-testkube/commands/common/cloudcontext.go index 178a281361..972e55a997 100644 --- a/cmd/kubectl-testkube/commands/common/cloudcontext.go +++ b/cmd/kubectl-testkube/commands/common/cloudcontext.go @@ -26,7 +26,7 @@ func UiPrintContext(cfg config.Data) { } // add agent information only when need to change agent data, it's usually not needed in usual workflow - if ui.Verbose { + if ui.IsVerbose() { contextData["Agent Key"] = text.Obfuscate(cfg.CloudContext.AgentKey) contextData["Agent URI"] = cfg.CloudContext.AgentUri } diff --git a/cmd/kubectl-testkube/commands/common/helper.go b/cmd/kubectl-testkube/commands/common/helper.go index 778ba132ac..22b6f1ad0b 100644 --- a/cmd/kubectl-testkube/commands/common/helper.go +++ b/cmd/kubectl-testkube/commands/common/helper.go @@ -2,7 +2,9 @@ package common import ( "bufio" + "bytes" "context" + "encoding/base64" "encoding/json" "fmt" "io" @@ -628,6 +630,51 @@ func KubectlPrintEvents(namespace string) error { return process.ExecuteAndStreamOutput(kubectl, args...) } +func KubectlVersion() (client string, server string, err error) { + kubectl, err := exec.LookPath("kubectl") + if err != nil { + return "", "", err + } + + args := []string{ + "version", + "-o", "json", + } + + if ui.IsVerbose() { + ui.ShellCommand(kubectl, args...) + ui.NL() + } + + out, eerr := process.Execute(kubectl, args...) + if eerr != nil { + return "", "", eerr + } + + type Version struct { + ClientVersion struct { + Version string `json:"gitVersion,omitempty"` + } `json:"clientVersion,omitempty"` + ServerVersion struct { + Version string `json:"gitVersion,omitempty"` + } `json:"serverVersion,omitempty"` + } + + var v Version + + out, err = extractJSONObject(out) + if err != nil { + return "", "", err + } + + err = json.Unmarshal(out, &v) + if err != nil { + return "", "", err + } + + return strings.TrimLeft(v.ClientVersion.Version, "v"), strings.TrimLeft(v.ServerVersion.Version, "v"), nil +} + func KubectlDescribePods(namespace string) error { kubectl, err := lookupKubectlPath() if err != nil { @@ -640,8 +687,10 @@ func KubectlDescribePods(namespace string) error { "-n", namespace, } - ui.ShellCommand(kubectl, args...) - ui.NL() + if ui.IsVerbose() { + ui.ShellCommand(kubectl, args...) + ui.NL() + } return process.ExecuteAndStreamOutput(kubectl, args...) } @@ -659,8 +708,10 @@ func KubectlPrintPods(namespace string) error { "--show-labels", } - ui.ShellCommand(kubectl, args...) - ui.NL() + if ui.IsVerbose() { + ui.ShellCommand(kubectl, args...) + ui.NL() + } return process.ExecuteAndStreamOutput(kubectl, args...) } @@ -676,8 +727,10 @@ func KubectlGetStorageClass(namespace string) error { "storageclass", } - ui.ShellCommand(kubectl, args...) - ui.NL() + if ui.IsVerbose() { + ui.ShellCommand(kubectl, args...) + ui.NL() + } return process.ExecuteAndStreamOutput(kubectl, args...) } @@ -694,8 +747,10 @@ func KubectlGetServices(namespace string) error { "-n", namespace, } - ui.ShellCommand(kubectl, args...) - ui.NL() + if ui.IsVerbose() { + ui.ShellCommand(kubectl, args...) + ui.NL() + } return process.ExecuteAndStreamOutput(kubectl, args...) } @@ -713,8 +768,10 @@ func KubectlDescribeServices(namespace string) error { "-o", "yaml", } - ui.ShellCommand(kubectl, args...) - ui.NL() + if ui.IsVerbose() { + ui.ShellCommand(kubectl, args...) + ui.NL() + } return process.ExecuteAndStreamOutput(kubectl, args...) } @@ -756,6 +813,60 @@ func KubectlDescribeIngresses(namespace string) error { return process.ExecuteAndStreamOutput(kubectl, args...) } +func KubectlGetPodEnvs(selector, namespace string) (map[string]string, error) { + kubectl, clierr := lookupKubectlPath() + if clierr != nil { + return nil, clierr.ActualError + } + + args := []string{ + "get", + "pod", + selector, + "-n", namespace, + "-o", `jsonpath='{range .items[*].spec.containers[*]}{"\nContainer: "}{.name}{"\n"}{range .env[*]}{.name}={.value}{"\n"}{end}{end}'`, + } + + if ui.IsVerbose() { + ui.ShellCommand(kubectl, args...) + ui.NL() + } + + out, err := process.Execute(kubectl, args...) + if err != nil { + return nil, err + } + + return convertEnvToMap(string(out)), nil +} + +func KubectlGetSecret(selector, namespace string) (map[string]string, error) { + kubectl, clierr := lookupKubectlPath() + if clierr != nil { + return nil, clierr.ActualError + } + + args := []string{ + "get", + "secret", + selector, + "-n", namespace, + "-o", `jsonpath='{.data}'`, + } + + if ui.IsVerbose() { + ui.ShellCommand(kubectl, args...) + ui.NL() + } + + out, err := process.Execute(kubectl, args...) + if err != nil { + return nil, err + } + + return secretsJSONToMap(string(out)) +} + func lookupKubectlPath() (string, *CLIError) { kubectlPath, err := exec.LookPath("kubectl") if err != nil { @@ -1004,3 +1115,71 @@ func GetLatestVersion() (string, error) { return strings.TrimPrefix(metadata.TagName, "v"), nil } + +func convertEnvToMap(input string) map[string]string { + result := make(map[string]string) + scanner := bufio.NewScanner(strings.NewReader(input)) + + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + + // Skip empty lines + if line == "" { + continue + } + + // Split on first = only + parts := strings.SplitN(line, "=", 2) + if len(parts) != 2 { + continue // Skip invalid lines + } + + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + + // Store in map + result[key] = value + } + + return result +} + +func secretsJSONToMap(in string) (map[string]string, error) { + res := map[string]string{} + in = strings.TrimLeft(in, "'") + in = strings.TrimRight(in, "'") + err := json.Unmarshal([]byte(in), &res) + + if len(res) > 0 { + for k := range res { + decoded, err := base64.StdEncoding.DecodeString(res[k]) + if err != nil { + return nil, err + } + res[k] = string(decoded) + } + } + + return res, err +} + +// extractJSONObject extracts JSON from any string +func extractJSONObject(input []byte) ([]byte, error) { + // Find the first '{' and last '}' to extract JSON object + start := bytes.Index(input, []byte("{")) + end := bytes.LastIndex(input, []byte("}")) + + if start == -1 || end == -1 || start > end { + return []byte(""), fmt.Errorf("invalid JSON format") + } + + jsonStr := input[start : end+1] + + // Validate JSON + var prettyJSON bytes.Buffer + if err := json.Indent(&prettyJSON, []byte(jsonStr), "", " "); err != nil { + return []byte(""), err + } + + return prettyJSON.Bytes(), nil +} diff --git a/cmd/kubectl-testkube/commands/common/render/common.go b/cmd/kubectl-testkube/commands/common/render/common.go index 0c88caf60f..5f6f5f0981 100644 --- a/cmd/kubectl-testkube/commands/common/render/common.go +++ b/cmd/kubectl-testkube/commands/common/render/common.go @@ -155,7 +155,7 @@ func PrintLogs(client client.Client, info testkube.ServerInfo, execution testkub lastSource = log.Source } - if ui.Verbose { + if ui.IsVerbose() { ui.Print(log.Time.Format("2006-01-02 15:04:05") + " " + log.Content) } else { ui.Print(log.Content) diff --git a/cmd/kubectl-testkube/commands/diagnostics.go b/cmd/kubectl-testkube/commands/diagnostics.go new file mode 100644 index 0000000000..fb29375451 --- /dev/null +++ b/cmd/kubectl-testkube/commands/diagnostics.go @@ -0,0 +1,40 @@ +package commands + +import ( + "github.com/spf13/cobra" + + commands "github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/diagnostics" + "github.com/kubeshop/testkube/pkg/diagnostics" + "github.com/kubeshop/testkube/pkg/ui" +) + +// NewDebugCmd creates the 'testkube debug' command +func NewDiagnosticsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "diagnostics", + Aliases: []string{"diagnose", "diag", "di"}, + Short: "Diagnoze testkube issues with ease", + Run: NewRunDiagnosticsCmdFunc(), + } + + cmd.Flags().StringP("key-override", "k", "", "Pass License key manually (we will not try to locate it automatically)") + cmd.Flags().StringP("file-override", "f", "", "Pass License file manually (we will not try to locate it automatically)") + + cmd.AddCommand(commands.NewLicenseCheckCmd()) + cmd.AddCommand(commands.NewInstallCheckCmd()) + + return cmd +} + +func NewRunDiagnosticsCmdFunc() func(cmd *cobra.Command, args []string) { + return func(cmd *cobra.Command, args []string) { + d := diagnostics.New() + + commands.RegisterInstallValidators(cmd, d) + commands.RegisterLicenseValidators(cmd, d) + + err := d.Run() + ui.ExitOnError("Running validations", err) + ui.NL(2) + } +} diff --git a/cmd/kubectl-testkube/commands/diagnostics/install.go b/cmd/kubectl-testkube/commands/diagnostics/install.go new file mode 100644 index 0000000000..a1ffd6b551 --- /dev/null +++ b/cmd/kubectl-testkube/commands/diagnostics/install.go @@ -0,0 +1,40 @@ +package diagnostics + +import ( + "github.com/spf13/cobra" + + "github.com/kubeshop/testkube/pkg/diagnostics" + "github.com/kubeshop/testkube/pkg/diagnostics/validators/deps" + "github.com/kubeshop/testkube/pkg/ui" +) + +func RegisterInstallValidators(_ *cobra.Command, d diagnostics.Diagnostics) { + depsGroup := d.AddValidatorGroup("install.dependencies", nil) + depsGroup.AddValidator(deps.NewKubectlDependencyValidator()) + depsGroup.AddValidator(deps.NewHelmDependencyValidator()) +} + +func NewInstallCheckCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "install", + Aliases: []string{"ins", "i"}, + Short: "Diagnose pre-installation dependencies", + Run: RunInstallCheckFunc(), + } + + cmd.Flags().StringP("key-override", "k", "", "Pass License key manually (we will not try to locate it automatically)") + cmd.Flags().StringP("file-override", "f", "", "Pass License file manually (we will not try to locate it automatically)") + + return cmd +} + +func RunInstallCheckFunc() func(cmd *cobra.Command, args []string) { + return func(cmd *cobra.Command, args []string) { + d := diagnostics.New() + RegisterInstallValidators(cmd, d) + + err := d.Run() + ui.ExitOnError("Running validations", err) + ui.NL(2) + } +} diff --git a/cmd/kubectl-testkube/commands/diagnostics/license.go b/cmd/kubectl-testkube/commands/diagnostics/license.go new file mode 100644 index 0000000000..165614c2a2 --- /dev/null +++ b/cmd/kubectl-testkube/commands/diagnostics/license.go @@ -0,0 +1,72 @@ +package diagnostics + +import ( + "github.com/spf13/cobra" + + "github.com/kubeshop/testkube/pkg/diagnostics" + "github.com/kubeshop/testkube/pkg/diagnostics/loader" + "github.com/kubeshop/testkube/pkg/diagnostics/validators/license" + "github.com/kubeshop/testkube/pkg/ui" +) + +func RegisterLicenseValidators(cmd *cobra.Command, d diagnostics.Diagnostics) { + + namespace := cmd.Flag("namespace").Value.String() + keyOverride := cmd.Flag("key-override").Value.String() + fileOverride := cmd.Flag("file-override").Value.String() + + var err error + l := loader.License{} + + if keyOverride != "" { + l.EnterpriseLicenseKey = keyOverride + } + if fileOverride != "" { + l.EnterpriseLicenseFile = fileOverride + } + + if fileOverride != "" && keyOverride != "" { + l.EnterpriseOfflineActivation = true + } + + if fileOverride == "" || keyOverride == "" { + l, err = loader.GetLicenseConfig(namespace, "") + ui.ExitOnError("loading license data", err) + } + + // License validator + licenseGroup := d.AddValidatorGroup("license.validation", l.EnterpriseLicenseKey) + if l.EnterpriseOfflineActivation { + licenseGroup.AddValidator(license.NewFileValidator()) + licenseGroup.AddValidator(license.NewOfflineLicenseKeyValidator()) + licenseGroup.AddValidator(license.NewOfflineLicenseValidator(l.EnterpriseLicenseKey, l.EnterpriseLicenseFile)) + } else { + licenseGroup.AddValidator(license.NewOnlineLicenseKeyValidator()) + licenseGroup.AddValidator(license.NewKeygenShValidator()) + } +} + +func NewLicenseCheckCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "license", + Aliases: []string{"lic", "l"}, + Short: "Diagnose license errors", + Run: RunLicenseCheckFunc(), + } + + cmd.Flags().StringP("key-override", "k", "", "Pass License key manually (we will not try to locate it automatically)") + cmd.Flags().StringP("file-override", "f", "", "Pass License file manually (we will not try to locate it automatically)") + + return cmd +} + +func RunLicenseCheckFunc() func(cmd *cobra.Command, args []string) { + return func(cmd *cobra.Command, args []string) { + d := diagnostics.New() + RegisterLicenseValidators(cmd, d) + + err := d.Run() + ui.ExitOnError("Running validations", err) + ui.NL(2) + } +} diff --git a/cmd/kubectl-testkube/commands/help.go b/cmd/kubectl-testkube/commands/help.go index 6cf7281e61..bfb93c1a67 100644 --- a/cmd/kubectl-testkube/commands/help.go +++ b/cmd/kubectl-testkube/commands/help.go @@ -29,16 +29,17 @@ func NewHelpCmd() *cobra.Command { ui.Print(RootCmd.Short) ui.NL() ui.Print(ui.LightGray("Usage")) - ui.Printf(fmt.Sprintf("%s %s", ui.White(RootCmd.Use), ui.LightGray("[flags]"))) - ui.Printf("%s %s", ui.White(RootCmd.Use), ui.LightGray("[command]")) + ui.Printf("%s %s\n", ui.White(RootCmd.Use), ui.LightGray("[flags]")) + ui.Printf("%s %s\n", ui.White(RootCmd.Use), ui.LightGray("[command]")) ui.NL() usage := helpMessageByGroups(RootCmd) ui.Print(usage) ui.Print(ui.LightGray("Flags")) ui.Printf(RootCmd.Flags().FlagUsages()) + ui.NL() ui.Print(ui.LightGray("Use \"kubectl testkube [command] --help\" for more information about a command.")) ui.NL() - ui.Printf("%s %s", ui.LightGray("Docs & Support:"), ui.White("https://docs.testkube.io")) + ui.Printf("%s %s\n", ui.LightGray("Docs & Support:"), ui.White("https://docs.testkube.io")) ui.NL() }, } diff --git a/cmd/kubectl-testkube/commands/root.go b/cmd/kubectl-testkube/commands/root.go index a489786f29..abd9c7e797 100644 --- a/cmd/kubectl-testkube/commands/root.go +++ b/cmd/kubectl-testkube/commands/root.go @@ -58,6 +58,7 @@ func init() { RootCmd.AddCommand(NewConfigCmd()) RootCmd.AddCommand(NewDebugCmd()) + RootCmd.AddCommand(NewDiagnosticsCmd()) RootCmd.AddCommand(NewCreateTicketCmd()) RootCmd.AddCommand(NewAgentCmd()) diff --git a/go.mod b/go.mod index 531fe38009..f2e215d06b 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.23.2 require ( github.com/99designs/gqlgen v0.17.27 github.com/Masterminds/semver v1.5.0 - github.com/adhocore/gronx v1.6.3 + github.com/adhocore/gronx v1.8.1 github.com/avast/retry-go/v4 v4.6.0 github.com/bmatcuk/doublestar/v4 v4.6.1 github.com/cdevents/sdk-go v0.3.0 @@ -21,7 +21,7 @@ require ( github.com/fluxcd/pkg/apis/event v0.2.0 github.com/fsnotify/fsnotify v1.7.0 github.com/gabriel-vasile/mimetype v1.4.6 - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 + github.com/go-task/slim-sprig v2.20.0+incompatible github.com/gofiber/adaptor/v2 v2.1.29 github.com/gofiber/fiber/v2 v2.52.5 github.com/gofiber/websocket/v2 v2.1.1 @@ -39,10 +39,12 @@ require ( github.com/json-iterator/go v1.1.12 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/kelseyhightower/envconfig v1.4.0 + github.com/keygen-sh/jsonapi-go v1.2.1 + github.com/keygen-sh/keygen-go/v3 v3.2.0 github.com/kubepug/kubepug v1.7.1 github.com/kubeshop/testkube-operator v1.17.55-0.20241118133003-70462ac10f4a - github.com/minio/minio-go/v7 v7.0.47 - github.com/montanaflynn/stats v0.6.6 + github.com/minio/minio-go/v7 v7.0.66 + github.com/montanaflynn/stats v0.7.1 github.com/moogar0880/problems v0.1.1 github.com/nats-io/nats-server/v2 v2.10.16 github.com/nats-io/nats.go v1.35.0 @@ -52,7 +54,7 @@ require ( github.com/onsi/gomega v1.31.0 github.com/otiai10/copy v1.11.0 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.18.0 + github.com/prometheus/client_golang v1.19.0 github.com/pterm/pterm v0.12.79 github.com/robfig/cron v1.2.0 github.com/savioxavier/termlink v1.4.1 @@ -63,12 +65,12 @@ require ( github.com/spf13/afero v1.11.0 github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.9.0 - github.com/valyala/fasthttp v1.51.0 + github.com/valyala/fasthttp v1.52.0 github.com/vektah/gqlparser/v2 v2.5.2-0.20230422221642-25e09f9d292d github.com/wI2L/jsondiff v0.6.0 go.mongodb.org/mongo-driver v1.14.0 go.uber.org/zap v1.27.0 - golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 + golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f golang.org/x/oauth2 v0.22.0 golang.org/x/sync v0.8.0 golang.org/x/text v0.19.0 @@ -77,9 +79,9 @@ require ( google.golang.org/protobuf v1.35.1 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.28.4 - k8s.io/apimachinery v0.28.4 - k8s.io/client-go v0.28.4 + k8s.io/api v0.29.3 + k8s.io/apimachinery v0.29.3 + k8s.io/client-go v0.29.3 k8s.io/utils v0.0.0-20230726121419-3b25d923346b sigs.k8s.io/controller-runtime v0.16.3 sigs.k8s.io/kustomize/kyaml v0.15.0 @@ -90,10 +92,11 @@ require ( atomicgo.dev/keyboard v0.2.9 // indirect atomicgo.dev/schedule v0.1.0 // indirect github.com/AlecAivazis/survey/v2 v2.3.7 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect github.com/alecthomas/chroma/v2 v2.8.0 // indirect - github.com/andybalholm/brotli v1.0.5 // indirect + github.com/andybalholm/brotli v1.1.0 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -107,21 +110,21 @@ require ( github.com/cli/go-gh/v2 v2.11.0 // indirect github.com/cli/safeexec v1.0.1 // indirect github.com/cli/shurcooL-graphql v0.0.4 // indirect - github.com/containerd/console v1.0.3 // indirect + github.com/containerd/console v1.0.4 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/dlclark/regexp2 v1.4.0 // indirect + github.com/dlclark/regexp2 v1.11.0 // indirect github.com/docker/cli v27.1.1+incompatible // indirect github.com/docker/distribution v2.8.2+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.2 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/evanphx/json-patch/v5 v5.7.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/fatih/color v1.16.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-errors/errors v1.5.1 // indirect @@ -131,26 +134,30 @@ require ( github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect - github.com/go-playground/locales v0.14.0 // indirect - github.com/go-playground/universal-translator v0.18.0 // indirect - github.com/go-playground/validator/v10 v10.11.1 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.19.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/golang/snappy v0.0.1 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v0.0.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gorilla/css v1.0.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/henvic/httpretty v0.1.4 // indirect + github.com/huandu/xstrings v1.4.0 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/itchyny/timefmt-go v0.1.5 // indirect github.com/josharian/intern v1.0.0 // indirect + github.com/keygen-sh/go-update v1.0.0 // indirect github.com/klauspost/compress v1.17.8 // indirect - github.com/klauspost/cpuid/v2 v2.2.3 // indirect - github.com/leodido/go-urn v1.2.1 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/lithammer/fuzzysearch v1.1.8 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect @@ -158,12 +165,11 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/microcosm-cc/bluemonday v1.0.26 // indirect github.com/minio/highwayhash v1.0.2 // indirect github.com/minio/md5-simd v1.1.2 // indirect - github.com/minio/sha256-simd v1.0.0 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect @@ -173,24 +179,26 @@ require ( github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.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/nats-io/jwt/v2 v2.5.7 // indirect github.com/nats-io/nkeys v0.4.7 // indirect github.com/nats-io/nuid v1.0.1 // indirect + github.com/oasisprotocol/curve25519-voi v0.0.0-20230110094441-db37f07504ce // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc5 // indirect - github.com/package-url/packageurl-go v0.1.0 // indirect + github.com/package-url/packageurl-go v0.1.2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/pquerna/cachecontrol v0.2.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/rs/xid v1.4.0 // indirect + github.com/rs/xid v1.5.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 // indirect + github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect github.com/savsgio/gotils v0.0.0-20211223103454-d0aaa54c5899 // indirect - github.com/segmentio/backo-go v1.0.0 // indirect + github.com/segmentio/backo-go v1.0.1 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc // indirect github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect @@ -212,7 +220,7 @@ require ( github.com/xdg-go/stringprep v1.0.4 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect + github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect github.com/yuin/goldmark v1.5.4 // indirect github.com/yuin/goldmark-emoji v1.0.2 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect @@ -235,11 +243,11 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect - k8s.io/apiextensions-apiserver v0.28.3 // indirect - k8s.io/component-base v0.28.3 // indirect + k8s.io/apiextensions-apiserver v0.29.3 // indirect + k8s.io/component-base v0.29.3 // indirect k8s.io/klog/v2 v2.120.1 // indirect - k8s.io/kube-openapi v0.0.0-20230918164632-68afd615200d // indirect + k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index cd93994055..56f08fbd15 100644 --- a/go.sum +++ b/go.sum @@ -26,14 +26,16 @@ github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/ github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE= github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4= github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +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/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= -github.com/adhocore/gronx v1.6.3 h1:bnm5vieTrY3QQPpsfB0hrAaeaHDpuZTUC2LLCVMLe9c= -github.com/adhocore/gronx v1.6.3/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg= +github.com/adhocore/gronx v1.8.1 h1:F2mLTG5sB11z7vplwD4iydz3YCEjstSfYmCrdSm3t6A= +github.com/adhocore/gronx v1.8.1/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= @@ -48,8 +50,8 @@ github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPp github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -100,8 +102,9 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cloudevents/sdk-go/v2 v2.15.2 h1:54+I5xQEnI73RBhWHxbI1XJcqOFOVJN85vb41+8mHUc= github.com/cloudevents/sdk-go/v2 v2.15.2/go.mod h1:lL7kSWAE/V8VI4Wh0jbL2v/jvqsm6tjmaQBSvxcv4uE= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= +github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= @@ -112,7 +115,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= @@ -130,8 +132,8 @@ github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+ github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= 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/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= -github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE= github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= @@ -148,16 +150,16 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustinkirkland/golang-petname v0.0.0-20191129215211-8e5a1ed0cff0 h1:90Ly+6UfUypEF6vvvW5rQIv9opIL8CbmW9FT20LDQoY= github.com/dustinkirkland/golang-petname v0.0.0-20191129215211-8e5a1ed0cff0/go.mod h1:V+Qd57rJe8gd4eiGzZyg4h54VLHmYVVw54iMnlAMrF8= -github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= -github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+BtnqkLAU= +github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.7.0 h1:nJqP7uwL84RJInrohHfW0Fx3awjbm8qZeFv0nW9SYGc= -github.com/evanphx/json-patch/v5 v5.7.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +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/fasthttp/websocket v1.5.0 h1:B4zbe3xXyvIdnqjOZrafVFklCUq5ZLo/TqCt5JA1wLE= github.com/fasthttp/websocket v1.5.0/go.mod h1:n0BlOQvJdPbTuBkZT0O5+jk/sp/1/VCzquR1BehI2F4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -167,6 +169,7 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fluxcd/pkg/apis/event v0.2.0 h1:cmAtkZfoEaNVYegI4SFM8XstdRAil3O9AoP+8fpbR34= github.com/fluxcd/pkg/apis/event v0.2.0/go.mod h1:OyzKqs90J+MK7rQaEOFMMCkALpPkfmxlkabgyY2wSFQ= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 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/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc= @@ -190,17 +193,17 @@ github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= -github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= +github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-task/slim-sprig v2.20.0+incompatible h1:4Xh3bDzO29j4TWNOI+24ubc0vbVFMg2PMnXKxK54/CA= +github.com/go-task/slim-sprig v2.20.0+incompatible/go.mod h1:N/mhXZITr/EQAOErEHciKvO1bFei2Lld2Ym6h96pdy0= github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= @@ -225,13 +228,14 @@ github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 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.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.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= @@ -240,6 +244,8 @@ 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-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo= github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8= +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= @@ -268,6 +274,10 @@ github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= +github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4= github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/henvic/httpretty v0.1.4 h1:Jo7uwIRWVFxkqOnErcoYfH90o3ddQyVrSANeS4cxYmU= @@ -276,6 +286,9 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +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/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= @@ -295,6 +308,12 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/keygen-sh/go-update v1.0.0 h1:M65sTVUHUO07tEK4l1Hq7u5D4kdEqkcgfdzUt3q3S08= +github.com/keygen-sh/go-update v1.0.0/go.mod h1:wn0UWRHLnBP5hwXtj1IdHZqWlHvIadh2Nn+becFf8Ro= +github.com/keygen-sh/jsonapi-go v1.2.1 h1:NTSIAxl2+7S5fPnKgrYwNjQSWbdKRtrFq26SD8AOkiU= +github.com/keygen-sh/jsonapi-go v1.2.1/go.mod h1:8j9vsLiKyJyDqmt8r3tYaYNmXszq2+cFhoO6QdMdAes= +github.com/keygen-sh/keygen-go/v3 v3.2.0 h1:OJqnGtY6z4ZA434kZqfNVHDmSrN5zq4l4XItcB3tECY= +github.com/keygen-sh/keygen-go/v3 v3.2.0/go.mod h1:YoFyryzXEk6XrbT3H8EUUU+JcIJkQu414TA6CvZgS/E= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.14.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= @@ -302,16 +321,13 @@ github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= -github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -322,8 +338,8 @@ github.com/kubepug/kubepug v1.7.1 h1:LKhfSxS8Y5mXs50v+3Lpyec+cogErDLcV7CMUuiaisw github.com/kubepug/kubepug v1.7.1/go.mod h1:lv+HxD0oTFL7ZWjj0u6HKhMbbTIId3eG7aWIW0gyF8g= github.com/kubeshop/testkube-operator v1.17.55-0.20241118133003-70462ac10f4a h1:xget2cwwqOL+K2Op9FPbMgfzj9lSVJAzZ9p48yxuFrE= github.com/kubeshop/testkube-operator v1.17.55-0.20241118133003-70462ac10f4a/go.mod h1:P47tw1nKQFufdsZndyq2HG2MSa0zK/lU0XpRfZtEmIk= -github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= @@ -345,8 +361,6 @@ github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -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/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= @@ -356,10 +370,10 @@ github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.47 h1:sLiuCKGSIcn/MI6lREmTzX91DX/oRau4ia0j6e6eOSs= -github.com/minio/minio-go/v7 v7.0.47/go.mod h1:nCrRzjoSUQh8hgKKtu3Y708OLvRLtuASMg2/nvmbarw= -github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= -github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/minio/minio-go/v7 v7.0.66 h1:bnTOXOHjOqv/gcMuiVbN9o2ngRItvqE774dG9nq0Dzw= +github.com/minio/minio-go/v7 v7.0.66/go.mod h1:DHAgmyQEGdW3Cif0UooKOyrT3Vxs82zNdV6tkKhRtbs= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= 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= @@ -377,8 +391,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= -github.com/montanaflynn/stats v0.6.6 h1:Duep6KMIDpY4Yo11iFsvyqJDyfzLF9+sndUKT+v64GQ= -github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/moogar0880/problems v0.1.1 h1:bktLhq8NDG/czU2ZziYNigBFksx13RaYe5AVdNmHDT4= github.com/moogar0880/problems v0.1.1/go.mod h1:5Dxrk2sD7BfBAgnOzQ1yaTiuCYdGPUh49L8Vhfky62c= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -389,6 +403,8 @@ github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nats-io/jwt/v2 v2.5.7 h1:j5lH1fUXCnJnY8SsQeB/a/z9Azgu2bYIDvtPVNdxe2c= github.com/nats-io/jwt/v2 v2.5.7/go.mod h1:ZdWS1nZa6WMZfFwwgpEaqBV8EPGVgOTDHN/wTbz0Y5A= github.com/nats-io/nats-server/v2 v2.10.16 h1:2jXaiydp5oB/nAx/Ytf9fdCi9QN6ItIc9eehX8kwVV0= @@ -401,14 +417,20 @@ github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oasisprotocol/curve25519-voi v0.0.0-20230110094441-db37f07504ce h1:/pEpMk55wH0X+E5zedGEMOdLuWmV8P4+4W3+LZaM6kg= +github.com/oasisprotocol/curve25519-voi v0.0.0-20230110094441-db37f07504ce/go.mod h1:hVoHR2EVESiICEMbg137etN/Lx+lSrHPTD39Z/uE+2s= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/olekukonko/tablewriter v0.0.6-0.20230925090304-df64c4bbad77 h1:3bMMZ1f+GPXFQ1uNaYbO/uECWvSfqEA+ZEXn1rFAT88= github.com/olekukonko/tablewriter v0.0.6-0.20230925090304-df64c4bbad77/go.mod h1:8Hf+pH6thup1sPZPD+NLg7d6vbpsdilu9CPIeikvgMQ= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.31.0 h1:54UJxxj6cPInHS3a35wm6BK/F9nHYueZ1NVujHDrnXE= github.com/onsi/gomega v1.31.0/go.mod h1:DW9aCi7U6Yi40wNVAvT6kzFnEVEI5n3DloYBiKiT6zk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -420,9 +442,8 @@ github.com/otiai10/copy v1.11.0 h1:OKBD80J/mLBrwnzXqGtFCzprFSGioo30JcmR4APsNwc= github.com/otiai10/copy v1.11.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= -github.com/package-url/packageurl-go v0.1.0 h1:efWBc98O/dBZRg1pw2xiDzovnlMjCa9NPnfaiBduh8I= -github.com/package-url/packageurl-go v0.1.0/go.mod h1:C/ApiuWpmbpni4DIOECf6WCjFUZV7O1Fx7VAzrZHgBw= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/package-url/packageurl-go v0.1.2 h1:0H2DQt6DHd/NeRlVwW4EZ4oEI6Bn40XlNPRqegcxuo4= +github.com/package-url/packageurl-go v0.1.2/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c= 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= @@ -433,13 +454,13 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/pquerna/cachecontrol v0.2.0 h1:vBXSNuE5MYP9IJ5kjsdo8uq+w41jSPgvba2DEnkRx9k= github.com/pquerna/cachecontrol v0.2.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= -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_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= -github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= @@ -457,24 +478,22 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 h1:TToq11gyfNlrMFZiYujSekIsPd9AmsA2Bj/iv+s4JHE= -github.com/santhosh-tekuri/jsonschema/v5 v5.0.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= github.com/savioxavier/termlink v1.4.1 h1:pFcd+XH8iQjL+2mB4buCDUo+CMt5kKsr8jGG+VLfYAg= github.com/savioxavier/termlink v1.4.1/go.mod h1:5T5ePUlWbxCHIwyF8/Ez1qufOoGM89RCg9NvG+3G3gc= github.com/savsgio/gotils v0.0.0-20211223103454-d0aaa54c5899 h1:Orn7s+r1raRTBKLSc9DmbktTT04sL+vkzsbRD2Q8rOI= github.com/savsgio/gotils v0.0.0-20211223103454-d0aaa54c5899/go.mod h1:oejLrk1Y/5zOF+c/aHtXqn3TFlzzbAgPWg8zBiAHDas= github.com/segmentio/analytics-go/v3 v3.2.1 h1:G+f90zxtc1p9G+WigVyTR0xNfOghOGs/PYAlljLOyeg= github.com/segmentio/analytics-go/v3 v3.2.1/go.mod h1:p8owAF8X+5o27jmvUognuXxdtqvSGtD0ZrfY2kcS9bE= -github.com/segmentio/backo-go v1.0.0 h1:kbOAtGJY2DqOR0jfRkYEorx/b18RgtepGtY3+Cpe6qA= -github.com/segmentio/backo-go v1.0.0/go.mod h1:kJ9mm9YmoWSkk+oQ+5Cj8DEoRCX2JT6As4kEtIIOp1M= +github.com/segmentio/backo-go v1.0.1 h1:68RQccglxZeyURy93ASB/2kc9QudzgIDexJ927N++y4= +github.com/segmentio/backo-go v1.0.1/go.mod h1:9/Rh6yILuLysoQnZ2oNooD2g7aBnvM7r/fNVxRNWfBc= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= @@ -541,8 +560,8 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.33.0/go.mod h1:KJRK/MXx0J+yd0c5hlR+s1tIHD72sniU8ZJjl97LIw4= github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= -github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= -github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= +github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0= +github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck= @@ -564,8 +583,8 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= +github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -609,16 +628,16 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= -golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= +golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -632,11 +651,13 @@ golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= @@ -661,13 +682,16 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -676,7 +700,6 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -685,11 +708,11 @@ golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/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-20220704084225-05e143d24a9e/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.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -705,6 +728,7 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -764,7 +788,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= @@ -788,20 +812,20 @@ gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY= -k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0= -k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08= -k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc= -k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8= -k8s.io/apimachinery v0.28.4/go.mod h1:wI37ncBvfAoswfq626yPTe6Bz1c22L7uaJ8dho83mgg= -k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY= -k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4= -k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI= -k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8= +k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw= +k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= +k8s.io/apiextensions-apiserver v0.29.3 h1:9HF+EtZaVpFjStakF4yVufnXGPRppWFEQ87qnO91YeI= +k8s.io/apiextensions-apiserver v0.29.3/go.mod h1:po0XiY5scnpJfFizNGo6puNU6Fq6D70UJY2Cb2KwAVc= +k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= +k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= +k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg= +k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0= +k8s.io/component-base v0.29.3 h1:Oq9/nddUxlnrCuuR2K/jp6aflVvc0uDvxMzAWxnGzAo= +k8s.io/component-base v0.29.3/go.mod h1:Yuj33XXjuOk2BAaHsIGHhCKZQAgYKhqIxIjIr2UXYio= k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20230918164632-68afd615200d h1:/CFeJBjBrZvHX09rObS2+2iEEDevMWYc1v3aIYAjIYI= -k8s.io/kube-openapi v0.0.0-20230918164632-68afd615200d/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4= @@ -810,7 +834,7 @@ sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMm sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kustomize/kyaml v0.15.0 h1:ynlLMAxDhrY9otSg5GYE2TcIz31XkGZ2Pkj7SdolD84= sigs.k8s.io/kustomize/kyaml v0.15.0/go.mod h1:+uMkBahdU1KNOj78Uta4rrXH+iH7wvg+nW7+GULvREA= -sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk= -sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +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= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/pkg/diagnostics/diagnostics.go b/pkg/diagnostics/diagnostics.go new file mode 100644 index 0000000000..726762c4ff --- /dev/null +++ b/pkg/diagnostics/diagnostics.go @@ -0,0 +1,88 @@ +package diagnostics + +import ( + "errors" + "sync" + + "github.com/kubeshop/testkube/pkg/diagnostics/renderer" + "github.com/kubeshop/testkube/pkg/diagnostics/validators" +) + +const ( + DefaultValidatorGroupName = "default " +) + +var ( + ErrGroupNotFound = errors.New("group not found") +) + +func New() Diagnostics { + return Diagnostics{ + Renderer: renderer.NewCLIRenderer(), + Groups: map[string]*validators.ValidatorGroup{}, + } +} + +// Diagnostics Top level diagnostics service to organize validators in groups +type Diagnostics struct { + Renderer renderer.Renderer + Groups map[string]*validators.ValidatorGroup +} + +// Run executes all validators in all groups and renders the results +func (d Diagnostics) Run() error { + + // for now we'll make validators concurrent + for groupName, _ := range d.Groups { + ch, err := d.RunGroup(groupName) + if err != nil { + return err + } + + d.Renderer.RenderGroupStart(groupName) + for r := range ch { + d.Renderer.RenderResult(r) + if r.BreakValidationChain { + break + } + } + } + + return nil +} + +// RunGroup tries to locate group and run it +func (d Diagnostics) RunGroup(group string) (chan validators.ValidationResult, error) { + ch := make(chan validators.ValidationResult) + g, ok := d.Groups[group] + if !ok { + return ch, ErrGroupNotFound + } + + go func() { + var wg sync.WaitGroup + + defer close(ch) + + if len(g.Validators) > 0 { + for _, v := range g.Validators { + wg.Add(1) + go func(v validators.Validator) { + defer wg.Done() + ch <- v.Validate(g.Subject).WithValidator(v.Name()) + }(v) + } + wg.Wait() + } + }() + + return ch, nil +} + +func (d *Diagnostics) AddValidatorGroup(group string, subject any) *validators.ValidatorGroup { + d.Groups[group] = &validators.ValidatorGroup{ + Subject: subject, + Name: group, + } + return d.Groups[group] +} diff --git a/pkg/diagnostics/loader/license_loader.go b/pkg/diagnostics/loader/license_loader.go new file mode 100644 index 0000000000..90c8c63a90 --- /dev/null +++ b/pkg/diagnostics/loader/license_loader.go @@ -0,0 +1,77 @@ +package loader + +import ( + "github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common" + "github.com/kubeshop/testkube/pkg/ui" +) + +type License struct { + EnterpriseOfflineActivation bool `envconfig:"ENTERPRISE_OFFLINE_ACTIVATION" default:"false"` + EnterpriseLicenseKey string `envconfig:"ENTERPRISE_LICENSE_KEY"` + EnterpriseLicenseKeyPath string `envconfig:"ENTERPRISE_LICENSE_KEY_PATH" default:"/testkube/license.key"` + EnterpriseLicenseFile string `envconfig:"ENTERPRISE_LICENSE_FILE"` + EnterpriseLicenseFilePath string `envconfig:"ENTERPRISE_LICENSE_FILE_PATH" default:"/testkube/license.lic"` + EnterpriseLicenseFileEncryption string `envconfig:"ENTERPRISE_LICENSE_FILE_ENCRYPTION"` + EnterpriseLicenseName string `envconfig:"ENTERPRISE_LICENSE_NAME"` +} + +const DefaultSecretName = "testkube-enterprise-license" + +func GetLicenseConfig(namespace, secretName string) (l License, err error) { + if secretName == "" { + secretName = DefaultSecretName + } + + // get control plane api pod envs + envs, err := common.KubectlGetPodEnvs("-l app.kubernetes.io/name=testkube-cloud-api", namespace) + if err != nil { + return l, err + } + ui.ExitOnError("getting env variables from pods", err) + + if offlineActivation, ok := envs["ENTERPRISE_OFFLINE_ACTIVATION"]; ok && offlineActivation == "true" { + l.EnterpriseOfflineActivation = true + } + + if f, ok := envs["ENTERPRISE_LICENSE_FILE"]; ok && f != "" { + l.EnterpriseLicenseFile = f + } + + if k, ok := envs["ENTERPRISE_LICENSE_KEY_PATH"]; ok && k != "" { + l.EnterpriseLicenseKeyPath = k + } + + if k, ok := envs["ENTERPRISE_LICENSE_FILE_PATH"]; ok && k != "" { + l.EnterpriseLicenseFilePath = k + } + + if k, ok := envs["ENTERPRISE_LICENSE_KEY"]; ok && k != "" { + l.EnterpriseLicenseKey = k + } + + // try to load from secret - there is no easy way of just stream the key content + secrets, err := common.KubectlGetSecret(secretName, namespace) + ui.WarnOnError("getting secrets from pods", err) + + const ( + keySecretKeyName = "LICENSE_KEY" + fileSecretKeyName = "license.lic" + ) + + // If no direct key value provided try to figure out it from secret mapped as file + if l.EnterpriseLicenseKey == "" { + // try to load from secret - there is no easy way of just stream the key content + if k, ok := secrets[keySecretKeyName]; ok { + l.EnterpriseLicenseKey = k + } + } + + // If no direct file value provided try to figure out it from secret mapped as file + if l.EnterpriseLicenseFile == "" { + if k, ok := secrets[fileSecretKeyName]; ok { + l.EnterpriseLicenseFile = k + } + } + + return l, err +} diff --git a/pkg/diagnostics/renderer/cli.go b/pkg/diagnostics/renderer/cli.go new file mode 100644 index 0000000000..13fd548d8b --- /dev/null +++ b/pkg/diagnostics/renderer/cli.go @@ -0,0 +1,57 @@ +package renderer + +import ( + "strings" + + "github.com/kubeshop/testkube/pkg/diagnostics/validators" + "github.com/kubeshop/testkube/pkg/ui" +) + +var _ Renderer = CLIRenderer{} + +func NewCLIRenderer() CLIRenderer { + return CLIRenderer{} +} + +type CLIRenderer struct { +} + +func (r CLIRenderer) RenderGroupStart(message string) { + message = strings.ToUpper(strings.Replace(message, ".", " ", -1)) + lines := strings.Repeat("=", len(message)) + ui.Printf("\n%s\n%s\n\n", ui.Green(message), ui.Yellow(lines)) +} + +func (r CLIRenderer) RenderProgress(message string) { + ui.Printf("%s", message) +} + +func (r CLIRenderer) RenderResult(res validators.ValidationResult) { + + ui.Printf(" %s %s: ", ui.Green(">"), res.Validator) + + if len(res.Errors) > 0 { + ui.Printf("%s\n", ui.IconCross) + + for _, err := range res.Errors { + ui.NL() + ui.Printf(" %s %s\n", ui.IconError, err.Message) + if err.Details != "" { + ui.Printf(" %s\n", ui.LightCyan(err.Details)) + } + if len(err.Suggestions) > 0 { + ui.Info(ui.LightGray(" Consider following suggestions/fixes before proceeding: ")) + for _, s := range err.Suggestions { + ui.Printf(" * %s\n", ui.LightBlue(s)) + } + } + if err.DocsURI != "" { + ui.Printf(" For more details follow docs: [%s]\n", ui.Yellow(err.DocsURI)) + } + } + } else { + ui.Printf("%s", ui.IconCheckMark) + } + ui.NL() + +} diff --git a/pkg/diagnostics/renderer/interface.go b/pkg/diagnostics/renderer/interface.go new file mode 100644 index 0000000000..39b4e0721b --- /dev/null +++ b/pkg/diagnostics/renderer/interface.go @@ -0,0 +1,9 @@ +package renderer + +import "github.com/kubeshop/testkube/pkg/diagnostics/validators" + +type Renderer interface { + RenderGroupStart(group string) + RenderResult(validators.ValidationResult) + RenderProgress(message string) +} diff --git a/pkg/diagnostics/renderer/json.go b/pkg/diagnostics/renderer/json.go new file mode 100644 index 0000000000..8457e3395c --- /dev/null +++ b/pkg/diagnostics/renderer/json.go @@ -0,0 +1,30 @@ +package renderer + +import ( + "encoding/json" + "os" + + "github.com/kubeshop/testkube/pkg/diagnostics/validators" +) + +var _ Renderer = JSONRenderer{} + +func NewJSONRenderer() JSONRenderer { + return JSONRenderer{ + encoder: json.NewEncoder(os.Stdout), + } +} + +type JSONRenderer struct { + encoder *json.Encoder +} + +func (r JSONRenderer) RenderGroupStart(message string) { +} + +func (r JSONRenderer) RenderProgress(message string) { +} + +func (r JSONRenderer) RenderResult(res validators.ValidationResult) { + r.encoder.Encode(res) +} diff --git a/pkg/diagnostics/validators/deps/errors.go b/pkg/diagnostics/validators/deps/errors.go new file mode 100644 index 0000000000..063fff7f8c --- /dev/null +++ b/pkg/diagnostics/validators/deps/errors.go @@ -0,0 +1,24 @@ +package deps + +import v "github.com/kubeshop/testkube/pkg/diagnostics/validators" + +var ( + ErrKubernetesInvalidVersion = v.Err("Your Kubernetes cluster has older version than Testkube require to run correctly", v.ErrorKindFileNotFound). + WithSuggestion("Consider upgrading Kubernetes to recent version"). + WithSuggestion("Please follow your provider upgrading instructions") + ErrKubectlInvalidVersion = v.Err("kubectl has older version than required", v.ErrorKindFileNotFound). + WithSuggestion("Consider upgrading kubectl to recent version"). + WithDocsURI("https://kubernetes.io/docs/tasks/tools") + + ErrKubectlFileNotFound = v.Err("kubectl binary not found", v.ErrorKindFileNotFound). + WithSuggestion("Make sure Kubectl is correctly installed and provided in system PATH"). + WithDocsURI("https://kubernetes.io/docs/tasks/tools") + + ErrHelmFileNotFound = v.Err("helm binary not found", v.ErrorKindFileNotFound). + WithSuggestion("Make sure Helm is correctly installed and provided in system PATH"). + WithDocsURI("https://helm.sh/docs/intro/install/") + + ErrHelmInvalidVersion = v.Err("helm has older version than required", v.ErrorKindFileNotFound). + WithSuggestion("Consider upgrading helm to recent version"). + WithDocsURI("https://helm.sh/docs/intro/install/") +) diff --git a/pkg/diagnostics/validators/deps/helm_binary.go b/pkg/diagnostics/validators/deps/helm_binary.go new file mode 100644 index 0000000000..6de4bf50c7 --- /dev/null +++ b/pkg/diagnostics/validators/deps/helm_binary.go @@ -0,0 +1,26 @@ +package deps + +import ( + "errors" + + "github.com/kubeshop/testkube/pkg/diagnostics/validators" +) + +type HelmDependencyValidator struct{} + +func (v HelmDependencyValidator) Name() string { + return "helm check" +} + +func (v HelmDependencyValidator) Validate(subject any) (r validators.ValidationResult) { + + if !checkFileExists("helm") { + return r.WithStdError(errors.New("helm not found")) + } + + return r.WithValidStatus() +} + +func NewHelmDependencyValidator() HelmDependencyValidator { + return HelmDependencyValidator{} +} diff --git a/pkg/diagnostics/validators/deps/kuebctl_binary.go b/pkg/diagnostics/validators/deps/kuebctl_binary.go new file mode 100644 index 0000000000..b2741da70b --- /dev/null +++ b/pkg/diagnostics/validators/deps/kuebctl_binary.go @@ -0,0 +1,55 @@ +package deps + +import ( + "fmt" + + "github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common" + "github.com/kubeshop/testkube/pkg/diagnostics/validators" + "github.com/kubeshop/testkube/pkg/semver" +) + +func NewKubectlDependencyValidator() KubectlDependencyValidator { + return KubectlDependencyValidator{ + RequiredKubectlVersion: validators.RequiredKubectlVersion, + RequiredKubernetesVersion: validators.RequiredKubernetesVersion, + } +} + +type KubectlDependencyValidator struct { + RequiredKubectlVersion string + RequiredKubernetesVersion string +} + +func (v KubectlDependencyValidator) Name() string { + return "Kubernetes check" +} + +func (v KubectlDependencyValidator) Validate(subject any) (r validators.ValidationResult) { + + if !checkFileExists("kubectl") { + return r.WithError(ErrKubectlFileNotFound) + } + + clientVersion, kubernetesVersion, err := common.KubectlVersion() + if err != nil { + return r.WithStdError(err) + } + + ok, err := semver.Lte(v.RequiredKubectlVersion, clientVersion) + if err != nil { + return r.WithStdError(err) + } + if !ok { + return r.WithError(ErrKubectlInvalidVersion.WithDetails(fmt.Sprintf("We need at least version %s, but your is %s, please consider upgrading", v.RequiredKubectlVersion, clientVersion))) + } + + ok, err = semver.Lte(v.RequiredKubernetesVersion, kubernetesVersion) + if err != nil { + return r.WithStdError(err) + } + if !ok { + return r.WithError(ErrKubernetesInvalidVersion.WithDetails(fmt.Sprintf("We need at least version %s, but your is %s, please consider upgrading", v.RequiredKubectlVersion, kubernetesVersion))) + } + + return r.WithValidStatus() +} diff --git a/pkg/diagnostics/validators/deps/utils.go b/pkg/diagnostics/validators/deps/utils.go new file mode 100644 index 0000000000..1ff79d6623 --- /dev/null +++ b/pkg/diagnostics/validators/deps/utils.go @@ -0,0 +1,15 @@ +package deps + +import ( + "os" + "os/exec" +) + +func checkFileExists(fileName string) bool { + path, err := exec.LookPath(fileName) + if err != nil { + return false + } + _, err = os.Stat(path) + return !os.IsNotExist(err) +} diff --git a/pkg/diagnostics/validators/error.go b/pkg/diagnostics/validators/error.go new file mode 100644 index 0000000000..432641f8ac --- /dev/null +++ b/pkg/diagnostics/validators/error.go @@ -0,0 +1,48 @@ +package validators + +import "errors" + +func IsError(err error) bool { + return errors.Is(err, &Error{}) +} + +func Err(e string, kind ErrorKind, suggestions ...string) Error { + err := Error{Message: e, Suggestions: suggestions} + return err +} + +type ErrorKind string + +type Error struct { + Kind ErrorKind + Message string + Details string + Suggestions []string + DocsURI string +} + +func (e Error) Error() string { + s := "" + if e.Message != "" { + s += e.Message + } + if e.Details != "" { + s += " - " + e.Details + } + return s +} + +func (e Error) WithSuggestion(s string) Error { + e.Suggestions = append(e.Suggestions, s) + return e +} + +func (e Error) WithDetails(d string) Error { + e.Details = d + return e +} + +func (e Error) WithDocsURI(d string) Error { + e.DocsURI = d + return e +} diff --git a/pkg/diagnostics/validators/group.go b/pkg/diagnostics/validators/group.go new file mode 100644 index 0000000000..e2da24693b --- /dev/null +++ b/pkg/diagnostics/validators/group.go @@ -0,0 +1,13 @@ +package validators + +// ValidatorGroup to organize validators around given subject e.g. "License Key" +type ValidatorGroup struct { + Subject any + Name string + Validators []Validator +} + +// AddValidator adds a new validator to the group +func (vg *ValidatorGroup) AddValidator(v Validator) { + vg.Validators = append(vg.Validators, v) +} diff --git a/pkg/diagnostics/validators/interface.go b/pkg/diagnostics/validators/interface.go new file mode 100644 index 0000000000..4779f93a04 --- /dev/null +++ b/pkg/diagnostics/validators/interface.go @@ -0,0 +1,8 @@ +package validators + +// Validator interface defines the Validate method for validation logic +type Validator interface { + // Validate runs validation logic against subject + Validate(subject any) ValidationResult + Name() string +} diff --git a/pkg/diagnostics/validators/kinds.go b/pkg/diagnostics/validators/kinds.go new file mode 100644 index 0000000000..c57962b2f7 --- /dev/null +++ b/pkg/diagnostics/validators/kinds.go @@ -0,0 +1,46 @@ +package validators + +const ( + ErrorKindCustom ErrorKind = "custom" + ErrorKindFileNotFound ErrorKind = "file not found" + ErrorKindKeyNotFound ErrorKind = "key not found" + + ErrorKindInvalidFileContent ErrorKind = "invalid file content" + ErrorKindInvalidKeyContent ErrorKind = "invalid key content" + ErrorKindBadWhitespaces ErrorKind = "bad whitespaces" + + ErrorKindLicenseInvalid ErrorKind = "license invalid" + ErrorKindLicenseExpired ErrorKind = "license expired" +) + +var ( + // Suggestions map + Suggestions = map[ErrorKind][]string{ + ErrorKindKeyNotFound: { + "please provide valid file for your control plane installation", + "you can pass file as environment variable details here https://docs.testkube.io/installation", + "make sure valid environment variables are set in pods", + }, + ErrorKindFileNotFound: { + "please provide valid file for your control plane installation", + "you can pass file as environment variable details here https://docs.testkube.io/blabalbalabl", + "make sure valid environment variables are set in pods, you can use `kubectl describe pods ....`", + }, + ErrorKindInvalidKeyContent: { + "please make sure your key is in valid format", + "please make sure given key was not modified in any editor", + "check if provided value was not changed by accident", + "check if additional whitespases were not added on the beggining and the end of the key", + }, + ErrorKindInvalidFileContent: { + "please make sure your key is in valid format", + "please make sure given key was not modified in any editor", + "check if provided value was not changed by accident", + "check if additional whitespases were not added on the beggining and the end of the key", + }, + ErrorKindBadWhitespaces: { + "please make sure given key was not modified in any editor", + "check if additional whitespases were not added on the beggining and the end of the key", + }, + } +) diff --git a/pkg/diagnostics/validators/license/client.go b/pkg/diagnostics/validators/license/client.go new file mode 100644 index 0000000000..1564828bae --- /dev/null +++ b/pkg/diagnostics/validators/license/client.go @@ -0,0 +1,70 @@ +package license + +import ( + "bytes" + "encoding/json" + "io" + "net/http" +) + +type LicenseResponse struct { + Valid bool `json:"valid,omitempty"` + Code string `json:"code,omitempty"` + Message string `json:"message,omitempty"` + License struct { + Expiry string `json:"expiry,omitempty"` + Name string `json:"name,omitempty"` + } `json:"license,omitempty"` +} + +type LicenseRequest struct { + License string `json:"license"` +} + +type Client struct { + url string +} + +const LicenseValidationURL = "https://license.testkube.io/validate" + +func NewClient() *Client { + return &Client{url: LicenseValidationURL} +} + +func (c *Client) WithURL(url string) *Client { + c.url = url + return c +} + +func (c *Client) ValidateLicense(licenseRequest LicenseRequest) (*LicenseResponse, error) { + reqBody, err := json.Marshal(licenseRequest) + if err != nil { + return nil, err + } + + resp, err := http.Post(c.url, "application/json", bytes.NewBuffer(reqBody)) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var licenseResponse LicenseResponse + b, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + if resp.StatusCode >= 400 { + return &LicenseResponse{ + Valid: false, + Message: string(b), + }, nil + } + + err = json.Unmarshal(b, &licenseResponse) + if err != nil { + return nil, err + } + + return &licenseResponse, nil +} diff --git a/pkg/diagnostics/validators/license/client_test.go b/pkg/diagnostics/validators/license/client_test.go new file mode 100644 index 0000000000..77c05e3bee --- /dev/null +++ b/pkg/diagnostics/validators/license/client_test.go @@ -0,0 +1,69 @@ +package license + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestValidateLicense(t *testing.T) { + // Test for successful validation + t.Run("Success", func(t *testing.T) { + mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + t.Errorf("Expected POST request, got %s", r.Method) + } + var reqBody LicenseRequest + err := json.NewDecoder(r.Body).Decode(&reqBody) + if err != nil || reqBody.License != "valid-license" { + http.Error(w, "Invalid request", http.StatusBadRequest) + return + } + response := LicenseResponse{Valid: true, Message: "License is valid"} + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(response) + // Corrected the response struct to match the expected fields + })) + defer mockServer.Close() + + client := NewClient().WithURL(mockServer.URL) + + resp, err := client.ValidateLicense(LicenseRequest{License: "valid-license"}) + assert.NoError(t, err) + assert.True(t, resp.Valid) + assert.Equal(t, "License is valid", resp.Message) + }) + + // Test for failure due to invalid request + t.Run("FailureInvalidRequest", func(t *testing.T) { + mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Error(w, "Invalid request", http.StatusBadRequest) + })) + defer mockServer.Close() + + client := NewClient().WithURL(mockServer.URL) + + resp, err := client.ValidateLicense(LicenseRequest{License: "invalid-license"}) + assert.NoError(t, err) + assert.False(t, resp.Valid) + }) + + t.Run("RealValidation license valid", func(t *testing.T) { + client := NewClient() + + response, err := client.ValidateLicense(LicenseRequest{License: "AB24F3-405E39-C3F657-94D113-F06C13-V3"}) + if err != nil { + t.Fatalf("Expected no error, got %v", err) + } + + if !response.Valid { + t.Errorf("Expected license to be valid, got %v", response.Valid) + } + if response.Code != "VALID" { + t.Errorf("Expected message 'VALID', got %s", response.Code) + } + }) +} diff --git a/pkg/diagnostics/validators/license/errors.go b/pkg/diagnostics/validators/license/errors.go new file mode 100644 index 0000000000..41b2c25a89 --- /dev/null +++ b/pkg/diagnostics/validators/license/errors.go @@ -0,0 +1,56 @@ +package license + +import ( + v "github.com/kubeshop/testkube/pkg/diagnostics/validators" +) + +// Errors definitions for license based logic +var ( + ErrLicenseKeyNotFound = v.Err("license key not found", v.ErrorKindKeyNotFound). + WithSuggestion("Make sure license key was correctly provided in for the testkube-cloud-api deployment"). + WithSuggestion("You can grab deployment detail with kubectl command - `kubectl get deployment testkube-cloud-api -n testkube`, check for ENTERPRISE_LICENSE_KEY value"). + WithSuggestion("Check your Helm chart installation values") + + ErrLicenseKeyInvalidFormat = v.Err("license key invalid format", v.ErrorKindInvalidKeyContent) + + ErrOnlineLicenseKeyInvalidLength = v.Err("license key invalid length", v.ErrorKindInvalidKeyContent). + WithDetails("License key should be in form XXXXXX-XXXXXX-XXXXXX-XXXXXX-XXXXXX-XX - 37 chars in length"). + WithSuggestion("Make sure license key is in valid format"). + WithSuggestion("Make sure there is no whitespaces on the begining and the end of the key") + + ErrWhitespacesAdded = v.Err("license key contains additional whitespaces", v.ErrorKindBadWhitespaces). + WithSuggestion("Make sure there is no whitespaces on the begining and the end of the key") + + ErrLicenseFileNotFound = v.Err("license file not found", v.ErrorKindFileNotFound). + WithSuggestion("Make sure license key was correctly provided in for the testkube-cloud-api deployment"). + WithSuggestion("You can grab deployment detail with kubectl command - `kubectl get deployment testkube-cloud-api -n testkube`, check for ENTERPRISE_LICENSE_FILE value") + + ErrKeygenShValidation = v.Err("license is invalid", v.ErrorKindInvalidKeyContent) + + ErrKeygenShValidationExpired = v.Err("license is expired", v.ErrorKindLicenseExpired). + WithDetails("Looks like your testkube license has expired"). + WithSuggestion("Please contact testkube team [https://testkube.io/contact] to check with your license") + + ErrOfflineLicenseKeyInvalidPrefix = v.Err("license key has invalid prefix", v.ErrorKindInvalidKeyContent). + WithDetails("License key should start with 'key/' string"). + WithSuggestion("Make sure license key is in valid format"). + WithSuggestion("Make sure you're NOT using offline keys for air-gapped (offline) installations"). + WithSuggestion("Make sure there is no whitespaces on the begining and the end of the key") + + ErrOfflineLicensePublicKeyMissing = v.Err("public key is missing", v.ErrorKindLicenseInvalid) + ErrOfflineLicenseInvalid = v.Err("offline license is invalid", v.ErrorKindLicenseInvalid) + ErrOfflineLicenseVerificationInvalid = v.Err("offline license verification error", v.ErrorKindLicenseInvalid) + ErrOfflineLicenseCertificateInvalid = v.Err("offline license certificate error", v.ErrorKindLicenseInvalid) + ErrOfflineLicenseDecodingError = v.Err("offline license decoding error", v.ErrorKindLicenseInvalid) + ErrOfflineLicenseLicenseFileIsNotGenuine = v.Err("license file is not genuine", v.ErrorKindLicenseInvalid) + ErrOfflineLicenseClockTamperingDetected = v.Err("system clock tampering detected", v.ErrorKindLicenseInvalid) + ErrOfflineLicenseFileExpired = v.Err("license file is expired", v.ErrorKindLicenseExpired). + WithDetails("Looks like your testkube license has expired"). + WithSuggestion("Please contact testkube team [https://testkube.io/contact] to check with your license") + + ErrOfflineLicenseDatasetIsMissing = v.Err("license dataset missing", v.ErrorKindLicenseInvalid) + + ErrOfflineLicenseExpired = v.Err("license is expired", v.ErrorKindLicenseExpired). + WithDetails("Looks like your testkube license has expired"). + WithSuggestion("Please contact testkube team [https://testkube.io/contact] to check with your license") +) diff --git a/pkg/diagnostics/validators/license/file_validator.go b/pkg/diagnostics/validators/license/file_validator.go new file mode 100644 index 0000000000..0b6017db02 --- /dev/null +++ b/pkg/diagnostics/validators/license/file_validator.go @@ -0,0 +1,42 @@ +package license + +import ( + "strings" + + "github.com/kubeshop/testkube/pkg/diagnostics/validators" +) + +func NewFileValidator() FileValidator { + return FileValidator{} +} + +type FileValidator struct { +} + +func (v FileValidator) Name() string { + return "License file check" +} + +// Validate validates a given license file for format / length correctness without calling external services +func (v FileValidator) Validate(subject any) (r validators.ValidationResult) { + r = r.WithValidator("License file check") + // get file + file, ok := subject.(string) + if !ok { + return r.WithError(ErrLicenseKeyInvalidFormat) + } + + if file == "" { + return r.WithError(ErrLicenseFileNotFound) + } + + // check if file doesn't contain invalid spaces + cleaned := strings.TrimSpace(file) + if file != cleaned { + return r.WithError(ErrWhitespacesAdded) + } + + // TODO use checks for file format validation + + return r.WithValidStatus() +} diff --git a/pkg/diagnostics/validators/license/keygensh_validator.go b/pkg/diagnostics/validators/license/keygensh_validator.go new file mode 100644 index 0000000000..9e17ac42ba --- /dev/null +++ b/pkg/diagnostics/validators/license/keygensh_validator.go @@ -0,0 +1,63 @@ +package license + +import ( + "fmt" + + "github.com/kubeshop/testkube/pkg/diagnostics/validators" +) + +var ( + + // Errors + ErrInvalidLicenseFormat = validators.ValidationResult{ + Status: validators.StatusInvalid, + Errors: []validators.Error{ + ErrLicenseKeyInvalidFormat, + }, + } +) + +func NewKeygenShValidator() KeygenShValidator { + return KeygenShValidator{ + Client: NewClient(), + } +} + +type KeygenShValidator struct { + Client *Client +} + +func (v KeygenShValidator) Name() string { + return "License key correctness online check" +} + +func (v KeygenShValidator) Validate(subject any) (r validators.ValidationResult) { + // get key + key, ok := subject.(string) + if !ok { + return r.WithError(ErrLicenseKeyInvalidFormat) + } + + // validate + resp, err := v.Client.ValidateLicense(LicenseRequest{License: key}) + if err != nil { + return r.WithStdError(err) + } + + return mapResponseToValidatonResult(r, resp) + +} + +func mapResponseToValidatonResult(r validators.ValidationResult, resp *LicenseResponse) validators.ValidationResult { + if resp.Valid { + return r.WithValidStatus() + } + + switch resp.Code { + case "EXPIRED": + return r.WithError(ErrKeygenShValidationExpired. + WithDetails(fmt.Sprintf("Looks like your license '%s' has expired at '%s'", resp.License.Name, resp.License.Expiry))) + } + + return r.WithError(ErrKeygenShValidation.WithDetails(resp.Message + resp.Code)) +} diff --git a/pkg/diagnostics/validators/license/offline_key_validator.go b/pkg/diagnostics/validators/license/offline_key_validator.go new file mode 100644 index 0000000000..85851b6b9c --- /dev/null +++ b/pkg/diagnostics/validators/license/offline_key_validator.go @@ -0,0 +1,39 @@ +package license + +import ( + "strings" + + "github.com/kubeshop/testkube/pkg/diagnostics/validators" +) + +func NewOfflineLicenseKeyValidator() OfflineLicenseKeyValidator { + return OfflineLicenseKeyValidator{} +} + +type OfflineLicenseKeyValidator struct { +} + +func (v OfflineLicenseKeyValidator) Name() string { + return "Offline license key check" +} + +// Validate validates a given license key for format / length correctness without calling external services +func (v OfflineLicenseKeyValidator) Validate(subject any) (r validators.ValidationResult) { + // get key + key, ok := subject.(string) + if !ok { + return r.WithError(ErrLicenseKeyInvalidFormat).WithBreak() + } + + if key == "" { + return r.WithError(ErrLicenseKeyNotFound).WithBreak() + } + + // key can be in enrypted format + if !strings.HasPrefix(key, "key/") { + return r.WithError(ErrOfflineLicenseKeyInvalidPrefix) + + } + + return r.WithValidStatus() +} diff --git a/pkg/diagnostics/validators/license/offline_license_validator.go b/pkg/diagnostics/validators/license/offline_license_validator.go new file mode 100644 index 0000000000..a61fe4c486 --- /dev/null +++ b/pkg/diagnostics/validators/license/offline_license_validator.go @@ -0,0 +1,167 @@ +package license + +import ( + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "os" + "strings" + "time" + + "github.com/keygen-sh/jsonapi-go" + "github.com/keygen-sh/keygen-go/v3" + + "github.com/kubeshop/testkube/pkg/diagnostics/validators" +) + +// KeygenOfflinePublicKey will be set through build process when changed +var KeygenOfflinePublicKey = "" + +type License struct { + License *keygen.License + Entitlements keygen.Entitlements + IsOffline bool `json:"isOffline"` +} + +func NewOfflineLicenseValidator(key, file string) OfflineLicenseValidator { + return OfflineLicenseValidator{ + LicenseKey: key, + LicenseFile: file, + } +} + +type OfflineLicenseValidator struct { + LicenseFile string + LicenseKey string +} + +func (v OfflineLicenseValidator) Name() string { + return "Offline license validation/signing check" +} + +// Validate validates a given license key for format / length correctness without calling external services +func (v OfflineLicenseValidator) Validate(_ any) (r validators.ValidationResult) { + l, err := v.ValidateOfflineLicenseCert(v.LicenseKey, v.LicenseFile) + if l == nil { + return r.WithError(err) + } + + if l.License.Expiry.Before(time.Now()) { + return r.WithError(ErrOfflineLicenseExpired. + WithDetails(fmt.Sprintf("Your license file expired in %s, please renew your license", l.License.Expiry))) + } + + left := l.License.Expiry.Sub(time.Now()) + return r.WithValidStatus().WithAdditionalInfo(fmt.Sprintf("license is still valid %d days", int(left.Hours())/24)) +} + +func (v *OfflineLicenseValidator) ValidateOfflineLicenseCert(key string, file string) (l *License, e validators.Error) { + if KeygenOfflinePublicKey == "" { + key, ok := os.LookupEnv("KEYGEN_PUBLIC_KEY") + if ok { + KeygenOfflinePublicKey = key + } + } + keygen.PublicKey = KeygenOfflinePublicKey + keygen.LicenseKey = key + + // Verify the license file's signature + lic := &keygen.LicenseFile{Certificate: file} + err := lic.Verify() + switch { + case errors.Is(err, keygen.ErrPublicKeyMissing): + return nil, ErrOfflineLicensePublicKeyMissing + case errors.Is(err, keygen.ErrLicenseFileNotGenuine): + return nil, ErrOfflineLicenseLicenseFileIsNotGenuine + case err != nil: + return nil, ErrOfflineLicenseVerificationInvalid.WithDetails(err.Error()) + } + + cert, err := certificate(lic) + if err != nil { + return nil, ErrOfflineLicenseCertificateInvalid.WithDetails(err.Error()) + } + + var dataset *keygen.LicenseFileDataset + switch { + case strings.HasPrefix(cert.Alg, "aes-256"): + // The license key is used as the key for the symmetric encryption. + dataset, err = lic.Decrypt(key) + case strings.HasPrefix(cert.Alg, "base64"): + dataset, err = decode(cert) + } + + switch { + case errors.Is(err, keygen.ErrSystemClockUnsynced): + return nil, ErrOfflineLicenseClockTamperingDetected + case errors.Is(err, keygen.ErrLicenseFileExpired): + return nil, ErrOfflineLicenseFileExpired + case err != nil: + return nil, ErrOfflineLicenseDecodingError.WithDetails(err.Error()) + } + + if dataset == nil { + return nil, ErrOfflineLicenseDatasetIsMissing + } + + return &License{ + License: &dataset.License, + Entitlements: dataset.Entitlements, + IsOffline: true, + }, e +} + +func decode(cert *keygenCertificate) (*keygen.LicenseFileDataset, error) { + if cert.Alg != "base64+ed25519" { + return nil, keygen.ErrLicenseFileNotSupported + } + + // continue here decode with base64 and json parse properly + data, err := base64.StdEncoding.DecodeString(cert.Enc) + if err != nil { + return nil, &keygen.LicenseFileError{Err: err} + } + + // Unmarshal + dataset := &keygen.LicenseFileDataset{} + + if _, err := jsonapi.Unmarshal(data, dataset); err != nil { + return nil, err + } + + if dataset.TTL != 0 && time.Now().After(dataset.Expiry) { + return dataset, keygen.ErrLicenseFileExpired + } + + return dataset, nil +} + +type keygenCertificate struct { + Enc string `json:"enc"` + Sig string `json:"sig"` + Alg string `json:"alg"` +} + +func certificate(lic *keygen.LicenseFile) (*keygenCertificate, error) { + payload := strings.TrimSpace(lic.Certificate) + + // Remove header and footer + payload = strings.TrimPrefix(payload, "-----BEGIN LICENSE FILE-----") + payload = strings.TrimSuffix(payload, "-----END LICENSE FILE-----") + payload = strings.TrimSpace(payload) + + // Decode + dec, err := base64.StdEncoding.DecodeString(payload) + if err != nil { + return nil, &keygen.LicenseFileError{Err: err} + } + + // Unmarshal + var cert *keygenCertificate + if err := json.Unmarshal(dec, &cert); err != nil { + return nil, &keygen.LicenseFileError{Err: err} + } + + return cert, nil +} diff --git a/pkg/diagnostics/validators/license/offline_license_validator_test.go b/pkg/diagnostics/validators/license/offline_license_validator_test.go new file mode 100644 index 0000000000..a4f6f523c5 --- /dev/null +++ b/pkg/diagnostics/validators/license/offline_license_validator_test.go @@ -0,0 +1,54 @@ +package license + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/kubeshop/testkube/pkg/diagnostics/validators" +) + +// Test_Licenses basic test when licenses files are provided +func Test_Licenses(t *testing.T) { + + var workingLicense = os.Getenv("TEST_WORKING_LICENSE") + var exampleWorkingFile = os.Getenv("TEST_VALID_LICENSE_FILE") + var fileTTLExpired = os.Getenv("TEST_INVALID_LICENSE_FILE_TTL") + var licenseExpired = os.Getenv("TEST_INVALID_LICENSE_FILE_EXPIRED") + + if workingLicense == "" { + t.Skip("working license env var not provided skipping test") + } + + t.Run("valid key valid file", func(t *testing.T) { + if exampleWorkingFile == "" { + t.Skip("env vars not provided skipping test") + } + v := NewOfflineLicenseValidator(workingLicense, exampleWorkingFile) + r := v.Validate("") + assert.Equal(t, r.Status, validators.StatusValid) + }) + + t.Run("valid key file ttl expired", func(t *testing.T) { + if fileTTLExpired == "" { + t.Skip("env vars not provided skipping test") + } + v := NewOfflineLicenseValidator(workingLicense, fileTTLExpired) + r := v.Validate("") + + assert.Equal(t, r.Status, validators.StatusInvalid) + assert.Equal(t, r.Errors[0].Error(), ErrOfflineLicenseFileExpired.Error()) + }) + + t.Run("valid key license expired file", func(t *testing.T) { + if licenseExpired == "" { + t.Skip("env vars not provided skipping test") + } + v := NewOfflineLicenseValidator(workingLicense, licenseExpired) + r := v.Validate("") + + assert.Equal(t, r.Status, validators.StatusInvalid) + assert.Equal(t, r.Errors[0].Error(), ErrOfflineLicenseExpired.Error()) + }) +} diff --git a/pkg/diagnostics/validators/license/online_key_validator.go b/pkg/diagnostics/validators/license/online_key_validator.go new file mode 100644 index 0000000000..4f263509e2 --- /dev/null +++ b/pkg/diagnostics/validators/license/online_key_validator.go @@ -0,0 +1,47 @@ +package license + +import ( + "fmt" + "regexp" + + "github.com/kubeshop/testkube/pkg/diagnostics/validators" +) + +func NewOnlineLicenseKeyValidator() OnlineLicenseKeyValidator { + return OnlineLicenseKeyValidator{} +} + +type OnlineLicenseKeyValidator struct { +} + +func (v OnlineLicenseKeyValidator) Name() string { + return "License key format check" +} + +// Validate validates a given license key for format / length correctness without calling external services +func (v OnlineLicenseKeyValidator) Validate(subject any) validators.ValidationResult { + r := validators.NewResult() + + // get key + key, ok := subject.(string) + if !ok { + return r.WithError(ErrLicenseKeyInvalidFormat) + } + + if key == "" { + return r.WithError(ErrLicenseKeyNotFound) + } + + // Check if the license key is the correct length and validate + if len(key) != 37 { + return r.WithError(ErrOnlineLicenseKeyInvalidLength.WithDetails(fmt.Sprintf("Passed license key length is %d and should be 37", len(key)))) + } + + // Check if the license key matches the expected format + match, _ := regexp.MatchString(`^([A-Z0-9_]{6}-){5}[^-]{2}$`, key) + if !match { + return r.WithError(ErrOnlineLicenseKeyInvalidLength) + } + + return r.WithValidStatus() +} diff --git a/pkg/diagnostics/validators/license/validator_test.go b/pkg/diagnostics/validators/license/validator_test.go new file mode 100644 index 0000000000..1da49e97ab --- /dev/null +++ b/pkg/diagnostics/validators/license/validator_test.go @@ -0,0 +1,14 @@ +package license + +import ( + "regexp" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestRegexp(t *testing.T) { + key := "8D9E74-444441-DEF387-31EE50-5F2A2E-V3" + match, _ := regexp.MatchString(`^([^-]{6}-){5}[^-]{2}$`, key) + assert.True(t, match) +} diff --git a/pkg/diagnostics/validators/mock/invalid.go b/pkg/diagnostics/validators/mock/invalid.go new file mode 100644 index 0000000000..339b150d7a --- /dev/null +++ b/pkg/diagnostics/validators/mock/invalid.go @@ -0,0 +1,11 @@ +package mock + +import v "github.com/kubeshop/testkube/pkg/diagnostics/validators" + +type AlwaysInvalidValidator struct { + Name string +} + +func (val AlwaysInvalidValidator) Validate(subject any) v.ValidationResult { + return v.NewResult().WithError(v.Err("Some error", v.ErrorKindCustom)).WithValidator("Always invalid " + val.Name) +} diff --git a/pkg/diagnostics/validators/mock/invalid_multi.go b/pkg/diagnostics/validators/mock/invalid_multi.go new file mode 100644 index 0000000000..8c08698a80 --- /dev/null +++ b/pkg/diagnostics/validators/mock/invalid_multi.go @@ -0,0 +1,15 @@ +package mock + +import v "github.com/kubeshop/testkube/pkg/diagnostics/validators" + +type AlwaysInvalidMultiValidator struct { + Name string +} + +func (val AlwaysInvalidMultiValidator) Validate(subject any) v.ValidationResult { + return v.NewResult(). + WithValidator("Always invalid " + val.Name). + WithError(v.Err("err1", v.ErrorKindCustom).WithDetails("some error occured")). + WithError(v.Err("err2", v.ErrorKindCustom).WithDetails("some error occured")). + WithError(v.Err("err3", v.ErrorKindCustom).WithDocsURI("https://docs.testkube.io/")) +} diff --git a/pkg/diagnostics/validators/mock/valid.go b/pkg/diagnostics/validators/mock/valid.go new file mode 100644 index 0000000000..cd3d7a07aa --- /dev/null +++ b/pkg/diagnostics/validators/mock/valid.go @@ -0,0 +1,11 @@ +package mock + +import v "github.com/kubeshop/testkube/pkg/diagnostics/validators" + +type AlwaysValidValidator struct { + Name string +} + +func (val AlwaysValidValidator) Validate(subject any) v.ValidationResult { + return v.NewResult().WithValidStatus().WithValidator("Always valid " + val.Name) +} diff --git a/pkg/diagnostics/validators/requirements.go b/pkg/diagnostics/validators/requirements.go new file mode 100644 index 0000000000..0d3c055dd4 --- /dev/null +++ b/pkg/diagnostics/validators/requirements.go @@ -0,0 +1,8 @@ +package validators + +const ( + // Minimal required versions + RequiredHelmVersion = "3.0.0" + RequiredKubectlVersion = "1.0.0" + RequiredKubernetesVersion = "1.0.0" +) diff --git a/pkg/diagnostics/validators/result.go b/pkg/diagnostics/validators/result.go new file mode 100644 index 0000000000..5677f63b55 --- /dev/null +++ b/pkg/diagnostics/validators/result.go @@ -0,0 +1,87 @@ +package validators + +type Status string + +const ( + StatusValid Status = "valid" + StatusInvalid Status = "invalid" +) + +func NewResult() ValidationResult { + return ValidationResult{ + Status: StatusInvalid, + } +} + +func NewErrorResponse(err error) ValidationResult { + return ValidationResult{ + Status: StatusInvalid, + Errors: []Error{ + { + Message: err.Error(), + Suggestions: []string{ + "got unexpected error, please contact Testkube team", + }, + }, + }, + } +} + +func NewValidResponse() ValidationResult { + return ValidationResult{ + Status: StatusValid, + } +} + +// ValidationResult represents the result of a validation operation +type ValidationResult struct { + BreakValidationChain bool + + Validator string + Status Status + + // Errors + Errors []Error + + // Logs + Logs map[string]string + + AdditionalInfo string +} + +func (r ValidationResult) WithValidator(v string) ValidationResult { + r.Validator = v + return r +} + +func (r ValidationResult) WithBreak() ValidationResult { + r.BreakValidationChain = true + return r +} + +func (r ValidationResult) WithValidStatus() ValidationResult { + r.Status = StatusValid + return r +} + +func (r ValidationResult) WithInvalidStatus() ValidationResult { + r.Status = StatusValid + return r +} + +func (r ValidationResult) WithAdditionalInfo(i string) ValidationResult { + r.AdditionalInfo = i + return r +} + +func (r ValidationResult) WithError(err Error) ValidationResult { + r.Status = StatusInvalid + r.Errors = append(r.Errors, err) + return r +} + +func (r ValidationResult) WithStdError(err error) ValidationResult { + r.Status = StatusInvalid + r.Errors = append(r.Errors, Error{Kind: ErrorKindCustom, Message: err.Error()}) + return r +} diff --git a/pkg/ui/printers.go b/pkg/ui/printers.go index 362c2a80c2..bf08619639 100644 --- a/pkg/ui/printers.go +++ b/pkg/ui/printers.go @@ -106,7 +106,6 @@ func (ui *UI) Print(message string, subMessages ...string) { func (ui *UI) Printf(format string, data ...any) { fmt.Fprintf(ui.Writer, format, data...) - fmt.Fprintln(ui.Writer) } func (ui *UI) PrintDot() { From 686622ae74637e1078b7612d11ab15ecde04bd8c Mon Sep 17 00:00:00 2001 From: Jacek Wysocki Date: Fri, 22 Nov 2024 08:53:22 +0100 Subject: [PATCH 15/30] fix: CI fixes for diagnostics (#6049) --- .builds-darwin.goreleaser.yml | 1 + .builds-linux.goreleaser.yml | 1 + .github/workflows/release-dev.yaml | 1 + .github/workflows/release.yaml | 1 + 4 files changed, 4 insertions(+) diff --git a/.builds-darwin.goreleaser.yml b/.builds-darwin.goreleaser.yml index 529627f7bd..67e92cc8dc 100644 --- a/.builds-darwin.goreleaser.yml +++ b/.builds-darwin.goreleaser.yml @@ -20,5 +20,6 @@ builds: - -X main.builtBy=goreleaser - -X github.com/kubeshop/testkube/pkg/telemetry.TestkubeMeasurementID={{.Env.ANALYTICS_TRACKING_ID}} - -X github.com/kubeshop/testkube/pkg/telemetry.TestkubeMeasurementSecret={{.Env.ANALYTICS_API_KEY}} + - -X github.com/kubeshop/testkube/pkg/diagnostics/validators/license.KeygenOfflinePublicKey={{.Env.KEYGEN_PUBLIC_KEY}} archives: - format: binary diff --git a/.builds-linux.goreleaser.yml b/.builds-linux.goreleaser.yml index 8d47ba50d3..82a0e40400 100644 --- a/.builds-linux.goreleaser.yml +++ b/.builds-linux.goreleaser.yml @@ -33,6 +33,7 @@ builds: - -X main.builtBy=goreleaser - -X github.com/kubeshop/testkube/pkg/telemetry.TestkubeMeasurementID={{.Env.ANALYTICS_TRACKING_ID}} - -X github.com/kubeshop/testkube/pkg/telemetry.TestkubeMeasurementSecret={{.Env.ANALYTICS_API_KEY}} + - -X github.com/kubeshop/testkube/pkg/diagnostics/validators/license.KeygenOfflinePublicKey={{.Env.KEYGEN_PUBLIC_KEY}} archives: - format: binary diff --git a/.github/workflows/release-dev.yaml b/.github/workflows/release-dev.yaml index f7070c56f5..746b46d0f3 100644 --- a/.github/workflows/release-dev.yaml +++ b/.github/workflows/release-dev.yaml @@ -79,6 +79,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.CI_BOT_TOKEN }} ANALYTICS_TRACKING_ID: "${{secrets.TESTKUBE_CLI_GA_MEASUREMENT_ID}}" ANALYTICS_API_KEY: "${{secrets.TESTKUBE_CLI_GA_MEASUREMENT_SECRET}}" + KEYGEN_PUBLIC_KEY: "${{secrets.KEYGEN_PUBLIC_KEY}}" # Your GoReleaser Pro key, if you are using the 'goreleaser-pro' distribution GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} DOCKER_BUILDX_BUILDER: "${{ steps.buildx.outputs.name }}" diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index f2a8ff1388..77b126eabd 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -80,6 +80,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.CI_BOT_TOKEN }} ANALYTICS_TRACKING_ID: "${{secrets.TESTKUBE_CLI_GA_MEASUREMENT_ID}}" ANALYTICS_API_KEY: "${{secrets.TESTKUBE_CLI_GA_MEASUREMENT_SECRET}}" + KEYGEN_PUBLIC_KEY: "${{secrets.KEYGEN_PUBLIC_KEY}}" # Your GoReleaser Pro key, if you are using the 'goreleaser-pro' distribution GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} DOCKER_BUILDX_BUILDER: "${{ steps.buildx.outputs.name }}" From 8266e56e021fd278c9a8e3ea7cb17c5285713225 Mon Sep 17 00:00:00 2001 From: Dawid Rusnak Date: Fri, 22 Nov 2024 10:03:56 +0100 Subject: [PATCH 16/30] feat(TKC-2844): add credentials support (#6045) * fix: reuse cloud connection for all operations in Toolkit * chore: isolate Init Process constants from state implementation * fix: error message for `secret()` function * chore: clean up dependencies to reduce binary size * feat(TKC-2844): add support for the `credential` function in expressions * feat(TKC-2844): add execution ID to credential request * feat(TKC-2844): handle fetching the credentials * feat(TKC-2844): allow any size of credentials * fix(TKC-2844): pass execution ID for the credentials request * feat(TKC-2844): obfuscate credentials in Test Workflows * chore: rename agent/client imports to agentclient --- cmd/api-server/main.go | 3 +- cmd/logs-server/main.go | 5 +- cmd/testworkflow-init/commands/run.go | 17 +- cmd/testworkflow-init/commands/setup.go | 9 +- cmd/testworkflow-init/constants/codes.go | 7 + cmd/testworkflow-init/constants/names.go | 5 + cmd/testworkflow-init/constants/paths.go | 15 + .../constants.go => constants/statuses.go} | 23 +- cmd/testworkflow-init/data/client.go | 39 +++ cmd/testworkflow-init/data/expressions.go | 11 +- cmd/testworkflow-init/data/global.go | 4 - cmd/testworkflow-init/data/state.go | 13 +- cmd/testworkflow-init/data/stepData.go | 39 +-- cmd/testworkflow-init/main.go | 55 ++-- .../orchestration/control.go | 2 +- .../orchestration/executions.go | 7 +- cmd/testworkflow-init/orchestration/setup.go | 17 +- cmd/testworkflow-init/runtime/machine.go | 18 ++ .../commands/artifacts.go | 4 +- cmd/testworkflow-toolkit/env/client.go | 29 +- pkg/agent/agent.go | 106 +----- pkg/agent/agent_test.go | 3 +- pkg/agent/client/client.go | 111 +++++++ pkg/agent/events.go | 3 +- pkg/agent/events_test.go | 3 +- pkg/agent/logs.go | 3 +- pkg/agent/logs_test.go | 3 +- pkg/agent/testworkflows.go | 3 +- .../model_test_workflow_result_extended.go | 8 +- pkg/cloud/data/executor/executor.go | 4 +- pkg/cloud/service.pb.go | 304 +++++++++++++----- pkg/cloud/service_grpc.pb.go | 37 ++- pkg/controlplane/server.go | 4 + pkg/credentials/expressions.go | 38 +++ pkg/credentials/mock_repository.go | 50 +++ pkg/credentials/repository.go | 37 +++ pkg/envs/bool.go | 18 -- pkg/executor/scraper/factory/factory.go | 4 +- pkg/log/log.go | 38 ++- pkg/logs/adapter/cloud_test.go | 10 +- .../executionworker/controller/notifier.go | 9 +- .../executionworker/controller/utils.go | 6 +- .../controller/watchers/commons.go | 4 +- .../controller/watchinstrumentedpod.go | 5 +- .../action/actiontypes/analysis.go | 4 +- .../testworkflowprocessor/secretmachine.go | 2 +- proto/service.proto | 10 + 47 files changed, 794 insertions(+), 355 deletions(-) create mode 100644 cmd/testworkflow-init/constants/codes.go create mode 100644 cmd/testworkflow-init/constants/names.go create mode 100644 cmd/testworkflow-init/constants/paths.go rename cmd/testworkflow-init/{data/constants.go => constants/statuses.go} (59%) create mode 100644 cmd/testworkflow-init/data/client.go create mode 100644 cmd/testworkflow-init/runtime/machine.go create mode 100644 pkg/agent/client/client.go create mode 100644 pkg/credentials/expressions.go create mode 100644 pkg/credentials/mock_repository.go create mode 100644 pkg/credentials/repository.go delete mode 100644 pkg/envs/bool.go diff --git a/cmd/api-server/main.go b/cmd/api-server/main.go index 9fe93fa512..f3bcd976f2 100644 --- a/cmd/api-server/main.go +++ b/cmd/api-server/main.go @@ -15,6 +15,7 @@ import ( "github.com/kubeshop/testkube/cmd/api-server/commons" "github.com/kubeshop/testkube/cmd/api-server/services" "github.com/kubeshop/testkube/internal/app/api/debug" + agentclient "github.com/kubeshop/testkube/pkg/agent/client" cloudartifacts "github.com/kubeshop/testkube/pkg/cloud/data/artifact" cloudtestworkflow "github.com/kubeshop/testkube/pkg/cloud/data/testworkflow" "github.com/kubeshop/testkube/pkg/event/kind/cdevent" @@ -134,7 +135,7 @@ func main() { if strings.HasPrefix(controlPlaneUrl, fmt.Sprintf("%s:%d", cfg.APIServerFullname, cfg.GRPCServerPort)) { controlPlaneUrl = fmt.Sprintf("127.0.0.1:%d", cfg.GRPCServerPort) } - grpcConn, err = agent.NewGRPCConnection( + grpcConn, err = agentclient.NewGRPCConnection( ctx, cfg.TestkubeProTLSInsecure, cfg.TestkubeProSkipVerify, diff --git a/cmd/logs-server/main.go b/cmd/logs-server/main.go index c3d5f5c0af..d64ce5aa4c 100644 --- a/cmd/logs-server/main.go +++ b/cmd/logs-server/main.go @@ -3,7 +3,6 @@ package main import ( "context" "errors" - "os" "os/signal" "syscall" @@ -14,7 +13,7 @@ import ( "google.golang.org/grpc/credentials" "github.com/kubeshop/testkube/internal/common" - "github.com/kubeshop/testkube/pkg/agent" + agentclient "github.com/kubeshop/testkube/pkg/agent/client" "github.com/kubeshop/testkube/pkg/event/bus" "github.com/kubeshop/testkube/pkg/log" "github.com/kubeshop/testkube/pkg/logs" @@ -106,7 +105,7 @@ func main() { switch mode { case common.ModeAgent: - grpcConn, err := agent.NewGRPCConnection( + grpcConn, err := agentclient.NewGRPCConnection( ctx, cfg.TestkubeProTLSInsecure, cfg.TestkubeProSkipVerify, diff --git a/cmd/testworkflow-init/commands/run.go b/cmd/testworkflow-init/commands/run.go index 9afd0ca66d..c17505b0d6 100644 --- a/cmd/testworkflow-init/commands/run.go +++ b/cmd/testworkflow-init/commands/run.go @@ -7,12 +7,13 @@ import ( "github.com/kubeshop/testkube/cmd/testworkflow-init/data" "github.com/kubeshop/testkube/cmd/testworkflow-init/orchestration" "github.com/kubeshop/testkube/cmd/testworkflow-init/output" + "github.com/kubeshop/testkube/cmd/testworkflow-init/runtime" "github.com/kubeshop/testkube/pkg/expressions" "github.com/kubeshop/testkube/pkg/testworkflows/testworkflowprocessor/action/actiontypes/lite" ) func Run(run lite.ActionExecute, container lite.LiteActionContainer) { - machine := data.GetInternalTestWorkflowMachine() + machine := runtime.GetInternalTestWorkflowMachine() state := data.GetState() step := state.GetStep(run.Ref) @@ -32,14 +33,14 @@ func Run(run lite.ActionExecute, container lite.LiteActionContainer) { // Ensure the command is not empty if len(command) == 0 { - output.ExitErrorf(data.CodeInputError, "command is required") + output.ExitErrorf(constants.CodeInputError, "command is required") } // Resolve the command to run for i := range command { value, err := expressions.CompileAndResolveTemplate(command[i], machine, expressions.FinalizerFail) if err != nil { - output.ExitErrorf(data.CodeInternal, "failed to compute argument '%d': %s", i, err.Error()) + output.ExitErrorf(constants.CodeInternal, "failed to compute argument '%d': %s", i, err.Error()) } command[i], _ = value.Static().StringValue() } @@ -48,11 +49,11 @@ func Run(run lite.ActionExecute, container lite.LiteActionContainer) { execution := orchestration.Executions.Create(command[0], command[1:]) result, err := execution.Run() if err != nil { - output.ExitErrorf(data.CodeInternal, "failed to execute: %v", err) + output.ExitErrorf(constants.CodeInternal, "failed to execute: %v", err) } // Initialize local state - var status data.StepStatus + var status constants.StepStatus success := result.ExitCode == 0 @@ -61,11 +62,11 @@ func Run(run lite.ActionExecute, container lite.LiteActionContainer) { success = !success } if result.Aborted { - status = data.StepStatusAborted + status = constants.StepStatusAborted } else if success { - status = data.StepStatusPassed + status = constants.StepStatusPassed } else { - status = data.StepStatusFailed + status = constants.StepStatusFailed } // Abandon saving execution data if the step has been finished before diff --git a/cmd/testworkflow-init/commands/setup.go b/cmd/testworkflow-init/commands/setup.go index 004b2eda41..e17243bbe2 100644 --- a/cmd/testworkflow-init/commands/setup.go +++ b/cmd/testworkflow-init/commands/setup.go @@ -6,7 +6,6 @@ import ( "strings" "github.com/kubeshop/testkube/cmd/testworkflow-init/constants" - "github.com/kubeshop/testkube/cmd/testworkflow-init/data" "github.com/kubeshop/testkube/cmd/testworkflow-init/output" "github.com/kubeshop/testkube/pkg/testworkflows/testworkflowprocessor/action/actiontypes/lite" "github.com/kubeshop/testkube/pkg/version" @@ -24,7 +23,7 @@ func Setup(config lite.ActionSetup) error { // Copy the init process stdoutUnsafe.Print("Configuring init process...") if config.CopyInit { - err := exec.Command("cp", "/init", data.InitPath).Run() + err := exec.Command("cp", "/init", constants.InitPath).Run() if err != nil { stdoutUnsafe.Error(" error\n") stdoutUnsafe.Errorf(" failed to copy the /init process: %s\n", err.Error()) @@ -38,7 +37,7 @@ func Setup(config lite.ActionSetup) error { // Copy the toolkit stdoutUnsafe.Print("Configuring toolkit...") if config.CopyToolkit { - err := exec.Command("cp", "/toolkit", data.ToolkitPath).Run() + err := exec.Command("cp", "/toolkit", constants.ToolkitPath).Run() if err != nil { stdoutUnsafe.Error(" error\n") stdoutUnsafe.Errorf(" failed to copy the /toolkit utilities: %s\n", err.Error()) @@ -54,7 +53,7 @@ func Setup(config lite.ActionSetup) error { if config.CopyBinaries { // Use `cp` on the whole directory, as it has plenty of files, which lead to the same FS block. // Copying individual files will lead to high FS usage - err := exec.Command("cp", "-rf", defaultInitImageBusyboxBinaryPath, data.InternalBinPath).Run() + err := exec.Command("cp", "-rf", defaultInitImageBusyboxBinaryPath, constants.InternalBinPath).Run() if err != nil { stdoutUnsafe.Error(" error\n") stdoutUnsafe.Errorf(" failed to copy the binaries: %s\n", err.Error()) @@ -66,7 +65,7 @@ func Setup(config lite.ActionSetup) error { } // Expose debugging Pod information - stdoutUnsafe.Output(data.InitStepName, "pod", map[string]string{ + stdoutUnsafe.Output(constants.InitStepName, "pod", map[string]string{ "name": os.Getenv(constants.EnvPodName), "nodeName": os.Getenv(constants.EnvNodeName), "namespace": os.Getenv(constants.EnvNamespaceName), diff --git a/cmd/testworkflow-init/constants/codes.go b/cmd/testworkflow-init/constants/codes.go new file mode 100644 index 0000000000..9742950ed9 --- /dev/null +++ b/cmd/testworkflow-init/constants/codes.go @@ -0,0 +1,7 @@ +package constants + +const ( + CodeAborted uint8 = 137 + CodeInputError uint8 = 155 + CodeInternal uint8 = 190 +) diff --git a/cmd/testworkflow-init/constants/names.go b/cmd/testworkflow-init/constants/names.go new file mode 100644 index 0000000000..467c71f26c --- /dev/null +++ b/cmd/testworkflow-init/constants/names.go @@ -0,0 +1,5 @@ +package constants + +const ( + InitStepName = "tktw-init" +) diff --git a/cmd/testworkflow-init/constants/paths.go b/cmd/testworkflow-init/constants/paths.go new file mode 100644 index 0000000000..ac8714c8e8 --- /dev/null +++ b/cmd/testworkflow-init/constants/paths.go @@ -0,0 +1,15 @@ +package constants + +import "path/filepath" + +const ( + InternalPath = "/.tktw" + TerminationLogPath = "/dev/termination-log" +) + +var ( + InternalBinPath = filepath.Join(InternalPath, "bin") + InitPath = filepath.Join(InternalPath, "init") + ToolkitPath = filepath.Join(InternalPath, "toolkit") + StatePath = filepath.Join(InternalPath, "state") +) diff --git a/cmd/testworkflow-init/data/constants.go b/cmd/testworkflow-init/constants/statuses.go similarity index 59% rename from cmd/testworkflow-init/data/constants.go rename to cmd/testworkflow-init/constants/statuses.go index fff1490b39..60c77dbd63 100644 --- a/cmd/testworkflow-init/data/constants.go +++ b/cmd/testworkflow-init/constants/statuses.go @@ -1,19 +1,4 @@ -package data - -import "path/filepath" - -const ( - InitStepName = "tktw-init" - InternalPath = "/.tktw" - TerminationLogPath = "/dev/termination-log" -) - -var ( - InternalBinPath = filepath.Join(InternalPath, "bin") - InitPath = filepath.Join(InternalPath, "init") - ToolkitPath = filepath.Join(InternalPath, "toolkit") - StatePath = filepath.Join(InternalPath, "state") -) +package constants type StepStatus string @@ -47,9 +32,3 @@ func StepStatusFromCode(code string) StepStatus { } return StepStatusAborted } - -const ( - CodeAborted uint8 = 137 - CodeInputError uint8 = 155 - CodeInternal uint8 = 190 -) diff --git a/cmd/testworkflow-init/data/client.go b/cmd/testworkflow-init/data/client.go new file mode 100644 index 0000000000..733b7666d1 --- /dev/null +++ b/cmd/testworkflow-init/data/client.go @@ -0,0 +1,39 @@ +package data + +import ( + "context" + "sync" + + "github.com/kubeshop/testkube/cmd/testworkflow-init/constants" + "github.com/kubeshop/testkube/cmd/testworkflow-init/output" + agentclient "github.com/kubeshop/testkube/pkg/agent/client" + "github.com/kubeshop/testkube/pkg/cloud" + "github.com/kubeshop/testkube/pkg/credentials" + "github.com/kubeshop/testkube/pkg/log" +) + +var ( + cloudMu sync.Mutex + cloudClient cloud.TestKubeCloudAPIClient +) + +func CloudClient() cloud.TestKubeCloudAPIClient { + cloudMu.Lock() + defer cloudMu.Unlock() + + if cloudClient == nil { + cfg := GetState().InternalConfig.Worker.Connection + logger := log.NewSilent() + grpcConn, err := agentclient.NewGRPCConnection(context.Background(), cfg.TlsInsecure, cfg.SkipVerify, cfg.Url, "", "", "", logger) + if err != nil { + output.ExitErrorf(constants.CodeInternal, "failed to connect with the Control Plane: %s", err.Error()) + } + cloudClient = cloud.NewTestKubeCloudAPIClient(grpcConn) + } + return cloudClient +} + +func Credentials() credentials.CredentialRepository { + cfg := GetState().InternalConfig + return credentials.NewCredentialRepository(CloudClient(), cfg.Worker.Connection.ApiKey, cfg.Execution.Id) +} diff --git a/cmd/testworkflow-init/data/expressions.go b/cmd/testworkflow-init/data/expressions.go index 5f584fc0a4..4ead38d421 100644 --- a/cmd/testworkflow-init/data/expressions.go +++ b/cmd/testworkflow-init/data/expressions.go @@ -5,6 +5,7 @@ import ( "os" "strings" + "github.com/kubeshop/testkube/cmd/testworkflow-init/constants" "github.com/kubeshop/testkube/cmd/testworkflow-init/output" "github.com/kubeshop/testkube/pkg/expressions" ) @@ -68,12 +69,12 @@ var StateMachine = expressions.NewMachine(). currentStatus := GetState().CurrentStatus expr, err := expressions.EvalExpression(currentStatus, RefNotFailedMachine, AliasMachine) if err != nil { - output.ExitErrorf(CodeInternal, "current status is invalid: %s: %v\n", currentStatus, err.Error()) + output.ExitErrorf(constants.CodeInternal, "current status is invalid: %s: %v\n", currentStatus, err.Error()) } if passed, _ := expr.BoolValue(); passed { - return string(StepStatusPassed), true + return string(constants.StepStatusPassed), true } - return string(StepStatusFailed), true + return string(constants.StepStatusFailed), true } else if name == "self.status" { state := GetState() step := state.GetStep(state.CurrentRef) @@ -123,7 +124,7 @@ var RefSuccessMachine = expressions.NewMachine(). if s.Status == nil { return nil, false } - return *s.Status == StepStatusPassed || *s.Status == StepStatusSkipped, true + return *s.Status == constants.StepStatusPassed || *s.Status == constants.StepStatusSkipped, true }) var RefNotFailedMachine = expressions.NewMachine(). @@ -135,7 +136,7 @@ var RefNotFailedMachine = expressions.NewMachine(). return exp, true } } - return s.Status == nil || *s.Status == StepStatusPassed || *s.Status == StepStatusSkipped, true + return s.Status == nil || *s.Status == constants.StepStatusPassed || *s.Status == constants.StepStatusSkipped, true }) func Expression(expr string, m ...expressions.Machine) (expressions.StaticValue, error) { diff --git a/cmd/testworkflow-init/data/global.go b/cmd/testworkflow-init/data/global.go index 8c31ac8e5e..d7a3f06fea 100644 --- a/cmd/testworkflow-init/data/global.go +++ b/cmd/testworkflow-init/data/global.go @@ -18,7 +18,3 @@ func GetBaseTestWorkflowMachine() expressions.Machine { GetState() // load state return expressions.CombinedMachines(EnvMachine, StateMachine, fileMachine) } - -func GetInternalTestWorkflowMachine() expressions.Machine { - return expressions.CombinedMachines(RefSuccessMachine, AliasMachine, GetBaseTestWorkflowMachine()) -} diff --git a/cmd/testworkflow-init/data/state.go b/cmd/testworkflow-init/data/state.go index 1bf5b5fa70..8d960c514e 100644 --- a/cmd/testworkflow-init/data/state.go +++ b/cmd/testworkflow-init/data/state.go @@ -9,6 +9,7 @@ import ( "strings" "sync" + "github.com/kubeshop/testkube/cmd/testworkflow-init/constants" "github.com/kubeshop/testkube/cmd/testworkflow-init/output" "github.com/kubeshop/testkube/pkg/expressions" "github.com/kubeshop/testkube/pkg/testworkflows/testworkflowconfig" @@ -148,14 +149,14 @@ func persistTerminationLog() { ref = *actions[i].End } if actions[i].Type() == lite.ActionTypeSetup { - ref = InitStepName + ref = constants.InitStepName } if ref == "" { continue } step := s.GetStep(ref) if step.Status == nil { - statuses = append(statuses, fmt.Sprintf("%s,%d", StepStatusAborted, CodeAborted)) + statuses = append(statuses, fmt.Sprintf("%s,%d", constants.StepStatusAborted, constants.CodeAborted)) } else { statuses = append(statuses, fmt.Sprintf("%s,%d", (*step.Status).Code(), step.ExitCode)) } @@ -168,9 +169,9 @@ func persistTerminationLog() { prevTerminationLog = statuses // Write the termination log - err := os.WriteFile(TerminationLogPath, []byte(strings.Join(statuses, "/")), 0) + err := os.WriteFile(constants.TerminationLogPath, []byte(strings.Join(statuses, "/")), 0) if err != nil { - output.UnsafeExitErrorf(CodeInternal, "failed to save the termination log: %s", err.Error()) + output.UnsafeExitErrorf(constants.CodeInternal, "failed to save the termination log: %s", err.Error()) } } @@ -181,7 +182,7 @@ func GetState() *state { defer loadStateMu.Unlock() loadStateMu.Lock() if !loadedState { - readState(StatePath) + readState(constants.StatePath) loadedState = true } return currentState @@ -192,6 +193,6 @@ func SaveTerminationLog() { } func SaveState() { - persistState(StatePath) + persistState(constants.StatePath) persistTerminationLog() } diff --git a/cmd/testworkflow-init/data/stepData.go b/cmd/testworkflow-init/data/stepData.go index 7b7371b055..8171d84103 100644 --- a/cmd/testworkflow-init/data/stepData.go +++ b/cmd/testworkflow-init/data/stepData.go @@ -6,6 +6,7 @@ import ( "sync" "time" + "github.com/kubeshop/testkube/cmd/testworkflow-init/constants" "github.com/kubeshop/testkube/cmd/testworkflow-init/output" ) @@ -15,17 +16,17 @@ type RetryPolicy struct { } type StepData struct { - Ref string `json:"_,omitempty"` - ExitCode uint8 `json:"e,omitempty"` - Status *StepStatus `json:"s,omitempty"` - StartedAt *time.Time `json:"S,omitempty"` - Condition string `json:"c,omitempty"` - Parents []string `json:"p,omitempty"` - Timeout *time.Duration `json:"t,omitempty"` - PausedOnStart bool `json:"P,omitempty"` - Retry RetryPolicy `json:"r,omitempty"` - Result string `json:"R,omitempty"` - Iteration int32 `json:"i,omitempty"` + Ref string `json:"_,omitempty"` + ExitCode uint8 `json:"e,omitempty"` + Status *constants.StepStatus `json:"s,omitempty"` + StartedAt *time.Time `json:"S,omitempty"` + Condition string `json:"c,omitempty"` + Parents []string `json:"p,omitempty"` + Timeout *time.Duration `json:"t,omitempty"` + PausedOnStart bool `json:"P,omitempty"` + Retry RetryPolicy `json:"r,omitempty"` + Result string `json:"R,omitempty"` + Iteration int32 `json:"i,omitempty"` // Pausing PausedNs int64 `json:"n,omitempty"` @@ -53,22 +54,22 @@ func (s *StepData) ResolveCondition() (bool, error) { return expr.Static().BoolValue() } -func (s *StepData) ResolveResult() (StepStatus, error) { +func (s *StepData) ResolveResult() (constants.StepStatus, error) { if s.Result == "" { - return StepStatusAborted, errors.New("missing result expression") + return constants.StepStatusAborted, errors.New("missing result expression") } expr, err := Expression(s.Result, RefSuccessMachine) if err != nil { - return StepStatusAborted, err + return constants.StepStatusAborted, err } success, err := expr.Static().BoolValue() if err != nil { - return StepStatusAborted, err + return constants.StepStatusAborted, err } if success { - return StepStatusPassed, nil + return constants.StepStatusPassed, nil } - return StepStatusFailed, nil + return constants.StepStatusFailed, nil } func (s *StepData) SetExitCode(exitCode uint8) *StepData { @@ -99,7 +100,7 @@ func (s *StepData) SetTimeout(timeout string) *StepData { } duration, err := time.ParseDuration(timeout) if err != nil { - output.ExitErrorf(CodeInputError, "invalid timeout duration: %s: %s", timeout, err.Error()) + output.ExitErrorf(constants.CodeInputError, "invalid timeout duration: %s: %s", timeout, err.Error()) } s.Timeout = &duration return s @@ -115,7 +116,7 @@ func (s *StepData) SetRetryPolicy(policy RetryPolicy) *StepData { return s } -func (s *StepData) SetStatus(status StepStatus) *StepData { +func (s *StepData) SetStatus(status constants.StepStatus) *StepData { s.Status = &status return s } diff --git a/cmd/testworkflow-init/main.go b/cmd/testworkflow-init/main.go index 75a7384e64..7ed32ecb50 100644 --- a/cmd/testworkflow-init/main.go +++ b/cmd/testworkflow-init/main.go @@ -19,6 +19,7 @@ import ( "github.com/kubeshop/testkube/cmd/testworkflow-init/obfuscator" "github.com/kubeshop/testkube/cmd/testworkflow-init/orchestration" "github.com/kubeshop/testkube/cmd/testworkflow-init/output" + "github.com/kubeshop/testkube/cmd/testworkflow-init/runtime" "github.com/kubeshop/testkube/pkg/expressions" "github.com/kubeshop/testkube/pkg/testworkflows/testworkflowprocessor/action/actiontypes/lite" ) @@ -42,22 +43,22 @@ func main() { orchestration.Setup.SetSensitiveWordMinimumLength(SensitiveMinimumLength) // Prepare empty state file if it doesn't exist - _, err := os.Stat(data.StatePath) + _, err := os.Stat(constants.StatePath) if errors.Is(err, os.ErrNotExist) { - stdout.Hint(data.InitStepName, constants.InstructionStart) + stdout.Hint(constants.InitStepName, constants.InstructionStart) stdoutUnsafe.Print("Creating state...") - err := os.WriteFile(data.StatePath, nil, 0777) + err := os.WriteFile(constants.StatePath, nil, 0777) if err != nil { stdoutUnsafe.Error(" error\n") - output.ExitErrorf(data.CodeInternal, "failed to create state file: %s", err.Error()) + output.ExitErrorf(constants.CodeInternal, "failed to create state file: %s", err.Error()) } - os.Chmod(data.StatePath, 0777) + os.Chmod(constants.StatePath, 0777) stdoutUnsafe.Print(" done\n") } else if err != nil { - stdout.Hint(data.InitStepName, constants.InstructionStart) + stdout.Hint(constants.InitStepName, constants.InstructionStart) stdoutUnsafe.Print("Accessing state...") stdoutUnsafe.Error(" error\n") - output.ExitErrorf(data.CodeInternal, "cannot access state file: %s", err.Error()) + output.ExitErrorf(constants.CodeInternal, "cannot access state file: %s", err.Error()) } // Store the instructions in the state if they are provided @@ -80,13 +81,13 @@ func main() { // Ensure there is a group index provided if len(os.Args) != 2 { - output.ExitErrorf(data.CodeInternal, "invalid arguments provided - expected only one") + output.ExitErrorf(constants.CodeInternal, "invalid arguments provided - expected only one") } // Determine group index to run groupIndex, err := strconv.ParseInt(os.Args[1], 10, 32) if err != nil { - output.ExitErrorf(data.CodeInputError, "invalid run group passed: %s", err.Error()) + output.ExitErrorf(constants.CodeInputError, "invalid run group passed: %s", err.Error()) } // Handle aborting @@ -143,7 +144,7 @@ func main() { }) _, err = controlSrv.Listen() if err != nil { - output.ExitErrorf(data.CodeInternal, "Failed to start control server at port %d: %s\n", constants.ControlServerPort, err.Error()) + output.ExitErrorf(constants.CodeInternal, "Failed to start control server at port %d: %s\n", constants.ControlServerPort, err.Error()) } // Keep a list of paused steps for execution @@ -187,7 +188,7 @@ func main() { orchestration.Start(step) break } - output.ExitErrorf(data.CodeInputError, err.Error()) + output.ExitErrorf(constants.CodeInputError, err.Error()) } stdout.SetSensitiveWords(orchestration.Setup.GetSensitiveWords()) currentContainer = *action.Container @@ -205,15 +206,15 @@ func main() { // Determine if the step should be skipped executable, err := step.ResolveCondition() if err != nil { - output.ExitErrorf(data.CodeInternal, "failed to determine condition of '%s' step: %s: %v", *action.Start, step.Condition, err.Error()) + output.ExitErrorf(constants.CodeInternal, "failed to determine condition of '%s' step: %s: %v", *action.Start, step.Condition, err.Error()) } if !executable { - step.SetStatus(data.StepStatusSkipped) + step.SetStatus(constants.StepStatusSkipped) // Skip all the children for _, v := range state.Steps { if slices.Contains(v.Parents, step.Ref) { - v.SetStatus(data.StepStatusSkipped) + v.SetStatus(constants.StepStatusSkipped) } } } @@ -231,7 +232,7 @@ func main() { if step.Status == nil { status, err := step.ResolveResult() if err != nil { - output.ExitErrorf(data.CodeInternal, "failed to determine result of '%s' step: %s: %v", *action.End, step.Result, err.Error()) + output.ExitErrorf(constants.CodeInternal, "failed to determine result of '%s' step: %s: %v", *action.End, step.Result, err.Error()) } step.SetStatus(status) } @@ -240,15 +241,15 @@ func main() { case lite.ActionTypeSetup: err := orchestration.Setup.UseEnv(constants.EnvGroupDebug) if err != nil { - output.ExitErrorf(data.CodeInputError, err.Error()) + output.ExitErrorf(constants.CodeInputError, err.Error()) } stdout.SetSensitiveWords(orchestration.Setup.GetSensitiveWords()) - step := state.GetStep(data.InitStepName) + step := state.GetStep(constants.InitStepName) err = commands.Setup(*action.Setup) if err == nil { - step.SetStatus(data.StepStatusPassed) + step.SetStatus(constants.StepStatusPassed) } else { - step.SetStatus(data.StepStatusFailed) + step.SetStatus(constants.StepStatusFailed) } orchestration.End(step) if err != nil { @@ -268,14 +269,14 @@ func main() { // Ignore when it is aborted if orchestration.Executions.IsAborted() { - step.SetStatus(data.StepStatusAborted) + step.SetStatus(constants.StepStatusAborted) continue } // Configure the environment err := orchestration.Setup.UseCurrentEnv() if err != nil { - output.ExitErrorf(data.CodeInputError, err.Error()) + output.ExitErrorf(constants.CodeInputError, err.Error()) } if action.Execute.Toolkit { serialized, _ := json.Marshal(state.InternalConfig) @@ -318,16 +319,16 @@ func main() { // Iterate over timed out step for _, r := range timedOut { - r.SetStatus(data.StepStatusTimeout) + r.SetStatus(constants.StepStatusTimeout) sub := state.GetSubSteps(r.Ref) for i := range sub { if sub[i].IsFinished() { continue } if sub[i].IsStarted() { - sub[i].SetStatus(data.StepStatusTimeout) + sub[i].SetStatus(constants.StepStatusTimeout) } else { - sub[i].SetStatus(data.StepStatusSkipped) + sub[i].SetStatus(constants.StepStatusSkipped) } } stdoutUnsafe.Println("Timed out.") @@ -352,7 +353,7 @@ func main() { // Ignore when it is aborted if orchestration.Executions.IsAborted() { - step.SetStatus(data.StepStatusAborted) + step.SetStatus(constants.StepStatusAborted) break } @@ -379,7 +380,7 @@ func main() { if until == "" { until = "passed" } - expr, err := expressions.CompileAndResolve(until, data.LocalMachine, data.GetInternalTestWorkflowMachine(), expressions.FinalizerFail) + expr, err := expressions.CompileAndResolve(until, data.LocalMachine, runtime.GetInternalTestWorkflowMachine(), expressions.FinalizerFail) if err != nil { stdout.Printf("failed to execute retry condition: %s: %s\n", until, err.Error()) break @@ -406,7 +407,7 @@ func main() { // Stop the container after all the instructions are interpret _ = orchestration.Executions.Kill() if orchestration.Executions.IsAborted() { - os.Exit(int(data.CodeAborted)) + os.Exit(int(constants.CodeAborted)) } else { os.Exit(0) } diff --git a/cmd/testworkflow-init/orchestration/control.go b/cmd/testworkflow-init/orchestration/control.go index a2c5e84dda..1701364350 100644 --- a/cmd/testworkflow-init/orchestration/control.go +++ b/cmd/testworkflow-init/orchestration/control.go @@ -35,7 +35,7 @@ func FinishExecution(step *data.StepData, result constants.ExecutionResult) { func End(step *data.StepData) { if !step.IsFinished() { v, e := json.Marshal(step) - output.ExitErrorf(data.CodeInternal, "cannot mark unfinished step as finished: %s, %v", string(v), e) + output.ExitErrorf(constants.CodeInternal, "cannot mark unfinished step as finished: %s, %v", string(v), e) } instructions.PrintHintDetails(step.Ref, constants.InstructionEnd, *step.Status) } diff --git a/cmd/testworkflow-init/orchestration/executions.go b/cmd/testworkflow-init/orchestration/executions.go index 2697ab1bc1..5bf0eadd57 100644 --- a/cmd/testworkflow-init/orchestration/executions.go +++ b/cmd/testworkflow-init/orchestration/executions.go @@ -10,6 +10,7 @@ import ( "github.com/pkg/errors" + "github.com/kubeshop/testkube/cmd/testworkflow-init/constants" "github.com/kubeshop/testkube/cmd/testworkflow-init/data" "github.com/kubeshop/testkube/cmd/testworkflow-init/output" ) @@ -150,7 +151,7 @@ type execution struct { func (e *execution) Run() (*executionResult, error) { // Immediately fail when aborted if e.group.aborted.Load() { - return &executionResult{Aborted: true, ExitCode: data.CodeAborted}, nil + return &executionResult{Aborted: true, ExitCode: constants.CodeAborted}, nil } // Ensure it's not paused @@ -163,7 +164,7 @@ func (e *execution) Run() (*executionResult, error) { if e.group.aborted.Load() { e.group.pauseMu.Unlock() e.cmdMu.Unlock() - return &executionResult{Aborted: true, ExitCode: data.CodeAborted}, nil + return &executionResult{Aborted: true, ExitCode: constants.CodeAborted}, nil } // Initialize local state @@ -208,7 +209,7 @@ func (e *execution) Run() (*executionResult, error) { // Fail when aborted if e.group.aborted.Load() { - return &executionResult{Aborted: true, ExitCode: data.CodeAborted}, nil + return &executionResult{Aborted: true, ExitCode: constants.CodeAborted}, nil } return &executionResult{ExitCode: uint8(exitCode)}, nil diff --git a/cmd/testworkflow-init/orchestration/setup.go b/cmd/testworkflow-init/orchestration/setup.go index 09ea0b497c..ed5e438ff2 100644 --- a/cmd/testworkflow-init/orchestration/setup.go +++ b/cmd/testworkflow-init/orchestration/setup.go @@ -47,6 +47,7 @@ type setup struct { envGroups map[string]map[string]string envGroupsComputed map[string]map[string]struct{} envGroupsSensitive map[string]map[string]struct{} + envAdditionalSensitive map[string]struct{} envCurrentGroup int envSelectedGroup string minSensitiveWordLength int @@ -58,6 +59,7 @@ func newSetup() *setup { envGroups: map[string]map[string]string{}, envGroupsComputed: map[string]map[string]struct{}{}, envGroupsSensitive: map[string]map[string]struct{}{}, + envAdditionalSensitive: map[string]struct{}{}, envCurrentGroup: -1, minSensitiveWordLength: 1, } @@ -106,8 +108,17 @@ func (c *setup) SetSensitiveWordMinimumLength(length int) { } } +func (c *setup) AddSensitiveWords(words ...string) { + for i := range words { + c.envAdditionalSensitive[words[i]] = struct{}{} + } +} + func (c *setup) GetSensitiveWords() []string { - words := make([]string, 0) + words := make([]string, 0, len(c.envAdditionalSensitive)) + for value := range c.envAdditionalSensitive { + words = append(words, value) + } for _, name := range commonSensitiveVariables { value := os.Getenv(name) if len(value) < c.minSensitiveWordLength { @@ -201,9 +212,9 @@ func (c *setup) UseEnv(group string) error { // Ensure the built-in binaries are available if os.Getenv("PATH") == "" { - os.Setenv("PATH", data.InternalBinPath) + os.Setenv("PATH", constants.InternalBinPath) } else { - os.Setenv("PATH", fmt.Sprintf("%s:%s", os.Getenv("PATH"), data.InternalBinPath)) + os.Setenv("PATH", fmt.Sprintf("%s:%s", os.Getenv("PATH"), constants.InternalBinPath)) } // Compute dynamic environment variables diff --git a/cmd/testworkflow-init/runtime/machine.go b/cmd/testworkflow-init/runtime/machine.go new file mode 100644 index 0000000000..81269571f5 --- /dev/null +++ b/cmd/testworkflow-init/runtime/machine.go @@ -0,0 +1,18 @@ +package runtime + +import ( + "github.com/kubeshop/testkube/cmd/testworkflow-init/data" + "github.com/kubeshop/testkube/cmd/testworkflow-init/orchestration" + "github.com/kubeshop/testkube/cmd/testworkflow-init/output" + "github.com/kubeshop/testkube/pkg/credentials" + "github.com/kubeshop/testkube/pkg/expressions" +) + +func GetInternalTestWorkflowMachine() expressions.Machine { + return expressions.CombinedMachines(data.RefSuccessMachine, data.AliasMachine, + data.GetBaseTestWorkflowMachine(), + credentials.NewCredentialMachine(data.Credentials(), func(_ string, value string) { + orchestration.Setup.AddSensitiveWords(value) + output.Std.SetSensitiveWords(orchestration.Setup.GetSensitiveWords()) + })) +} diff --git a/cmd/testworkflow-toolkit/commands/artifacts.go b/cmd/testworkflow-toolkit/commands/artifacts.go index d9f9e4926c..8ecd33c4c4 100644 --- a/cmd/testworkflow-toolkit/commands/artifacts.go +++ b/cmd/testworkflow-toolkit/commands/artifacts.go @@ -13,7 +13,7 @@ import ( "google.golang.org/protobuf/types/known/emptypb" "github.com/kubeshop/testkube/cmd/testworkflow-toolkit/env/config" - "github.com/kubeshop/testkube/pkg/agent" + agentclient "github.com/kubeshop/testkube/pkg/agent/client" "github.com/kubeshop/testkube/pkg/capabilities" "github.com/kubeshop/testkube/pkg/cloud" @@ -84,7 +84,7 @@ func NewArtifactsCmd() *cobra.Command { // Archive ctx, cancel := context.WithTimeout(cmd.Context(), 30*time.Second) defer cancel() - ctx = agent.AddAPIKeyMeta(ctx, config.Config().Worker.Connection.ApiKey) + ctx = agentclient.AddAPIKeyMeta(ctx, config.Config().Worker.Connection.ApiKey) executor, client := env.Cloud(ctx) proContext, err := client.GetProContext(ctx, &emptypb.Empty{}) var supported []*cloud.Capability diff --git a/cmd/testworkflow-toolkit/env/client.go b/cmd/testworkflow-toolkit/env/client.go index 1031cb444c..debdb35b29 100644 --- a/cmd/testworkflow-toolkit/env/client.go +++ b/cmd/testworkflow-toolkit/env/client.go @@ -6,6 +6,7 @@ import ( "math" "net/url" "strconv" + "sync" corev1 "k8s.io/api/core/v1" @@ -13,10 +14,10 @@ import ( "k8s.io/client-go/rest" config2 "github.com/kubeshop/testkube/cmd/testworkflow-toolkit/env/config" + agentclient "github.com/kubeshop/testkube/pkg/agent/client" "github.com/kubeshop/testkube/pkg/cache" "github.com/kubeshop/testkube/cmd/kubectl-testkube/config" - "github.com/kubeshop/testkube/pkg/agent" "github.com/kubeshop/testkube/pkg/api/v1/client" "github.com/kubeshop/testkube/pkg/cloud" cloudexecutor "github.com/kubeshop/testkube/pkg/cloud/data/executor" @@ -86,12 +87,26 @@ func Testkube() client.Client { return client.NewDirectAPIClient(httpClient, sseClient, fmt.Sprintf("http://%s:%d", host, port), "") } +var ( + cloudMu sync.Mutex + cloudExecutor cloudexecutor.Executor + cloudClient cloud.TestKubeCloudAPIClient +) + func Cloud(ctx context.Context) (cloudexecutor.Executor, cloud.TestKubeCloudAPIClient) { - cfg := config2.Config().Worker.Connection - grpcConn, err := agent.NewGRPCConnection(ctx, cfg.TlsInsecure, cfg.SkipVerify, cfg.Url, "", "", "", log.DefaultLogger) - if err != nil { - ui.Fail(fmt.Errorf("failed to connect with Cloud: %w", err)) + cloudMu.Lock() + defer cloudMu.Unlock() + + if cloudExecutor == nil { + cfg := config2.Config().Worker.Connection + logger := log.NewSilent() + grpcConn, err := agentclient.NewGRPCConnection(ctx, cfg.TlsInsecure, cfg.SkipVerify, cfg.Url, "", "", "", logger) + if err != nil { + ui.Fail(fmt.Errorf("failed to connect with Cloud: %w", err)) + } + cloudClient = cloud.NewTestKubeCloudAPIClient(grpcConn) + cloudExecutor = cloudexecutor.NewCloudGRPCExecutor(cloudClient, grpcConn, cfg.ApiKey) } - grpcClient := cloud.NewTestKubeCloudAPIClient(grpcConn) - return cloudexecutor.NewCloudGRPCExecutor(grpcClient, grpcConn, cfg.ApiKey), grpcClient + + return cloudExecutor, cloudClient } diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go index 116c719812..b612770d26 100644 --- a/pkg/agent/agent.go +++ b/pkg/agent/agent.go @@ -2,28 +2,21 @@ package agent import ( "context" - "crypto/tls" - "crypto/x509" "fmt" "math" - "os" "time" - "github.com/kubeshop/testkube/pkg/executor/output" - "github.com/kubeshop/testkube/pkg/version" - - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/encoding/gzip" - "google.golang.org/grpc/keepalive" - "github.com/pkg/errors" "github.com/valyala/fasthttp" "go.uber.org/zap" "golang.org/x/sync/errgroup" "google.golang.org/grpc" + "google.golang.org/grpc/encoding/gzip" "google.golang.org/grpc/metadata" + agentclient "github.com/kubeshop/testkube/pkg/agent/client" + "github.com/kubeshop/testkube/pkg/executor/output" + "github.com/kubeshop/testkube/internal/config" "github.com/kubeshop/testkube/pkg/api/v1/testkube" "github.com/kubeshop/testkube/pkg/cloud" @@ -31,8 +24,6 @@ import ( ) const ( - timeout = 10 * time.Second - apiKeyMeta = "api-key" clusterIDMeta = "cluster-id" cloudMigrateMeta = "migrate" orgIdMeta = "environment-id" @@ -44,88 +35,6 @@ const ( // buffer up to five messages per worker const bufferSizePerWorker = 5 -func NewGRPCConnection( - ctx context.Context, - isInsecure bool, - skipVerify bool, - server string, - certFile, keyFile, caFile string, - logger *zap.SugaredLogger, -) (*grpc.ClientConn, error) { - ctx, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12} - if skipVerify { - tlsConfig = &tls.Config{InsecureSkipVerify: true} - } else { - if certFile != "" && keyFile != "" { - if err := clientCert(tlsConfig, certFile, keyFile); err != nil { - return nil, err - } - } - if caFile != "" { - if err := rootCAs(tlsConfig, caFile); err != nil { - return nil, err - } - } - } - - creds := credentials.NewTLS(tlsConfig) - if isInsecure { - creds = insecure.NewCredentials() - } - - kacp := keepalive.ClientParameters{ - Time: 10 * time.Second, - Timeout: 5 * time.Second, - PermitWithoutStream: true, - } - - userAgent := version.Version + "/" + version.Commit - logger.Infow("initiating connection with control plane", "userAgent", userAgent, "server", server, "insecure", isInsecure, "skipVerify", skipVerify, "certFile", certFile, "keyFile", keyFile, "caFile", caFile) - // WithBlock, WithReturnConnectionError and FailOnNonTempDialError are recommended not to be used by gRPC go docs - // but given that Agent will not work if gRPC connection cannot be established, it is ok to use them and assert issues at dial time - return grpc.DialContext( - ctx, - server, - grpc.WithBlock(), - grpc.WithReturnConnectionError(), - grpc.FailOnNonTempDialError(true), - grpc.WithUserAgent(userAgent), - grpc.WithTransportCredentials(creds), - grpc.WithKeepaliveParams(kacp), - ) -} - -func rootCAs(tlsConfig *tls.Config, file ...string) error { - pool := x509.NewCertPool() - for _, f := range file { - rootPEM, err := os.ReadFile(f) - if err != nil || rootPEM == nil { - return fmt.Errorf("agent: error loading or parsing rootCA file: %v", err) - } - ok := pool.AppendCertsFromPEM(rootPEM) - if !ok { - return fmt.Errorf("agent: failed to parse root certificate from %q", f) - } - } - tlsConfig.RootCAs = pool - return nil -} - -func clientCert(tlsConfig *tls.Config, certFile, keyFile string) error { - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return fmt.Errorf("agent: error loading client certificate: %v", err) - } - cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0]) - if err != nil { - return fmt.Errorf("agent: error parsing client certificate: %v", err) - } - tlsConfig.Certificates = []tls.Certificate{cert} - return nil -} - type Agent struct { client cloud.TestKubeCloudAPIClient handler fasthttp.RequestHandler @@ -309,7 +218,7 @@ func (ag *Agent) receiveCommand(ctx context.Context, stream cloud.TestKubeCloudA func (ag *Agent) runCommandLoop(ctx context.Context) error { if ag.proContext.APIKey != "" { - ctx = AddAPIKeyMeta(ctx, ag.proContext.APIKey) + ctx = agentclient.AddAPIKeyMeta(ctx, ag.proContext.APIKey) } ctx = metadata.AppendToOutgoingContext(ctx, clusterIDMeta, ag.clusterID) @@ -429,11 +338,6 @@ func (ag *Agent) executeCommand(_ context.Context, cmd *cloud.ExecuteRequest) *c } } -func AddAPIKeyMeta(ctx context.Context, apiKey string) context.Context { - md := metadata.Pairs(apiKeyMeta, apiKey) - return metadata.NewOutgoingContext(ctx, md) -} - type cloudResponse struct { resp *cloud.ExecuteRequest err error diff --git a/pkg/agent/agent_test.go b/pkg/agent/agent_test.go index 8cf5c15688..fb6c6d1741 100644 --- a/pkg/agent/agent_test.go +++ b/pkg/agent/agent_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + agentclient "github.com/kubeshop/testkube/pkg/agent/client" "github.com/kubeshop/testkube/pkg/api/v1/testkube" "github.com/kubeshop/testkube/pkg/executor/output" "github.com/kubeshop/testkube/pkg/log" @@ -50,7 +51,7 @@ func TestCommandExecution(t *testing.T) { atomic.AddInt32(&msgCnt, 1) } - grpcConn, err := agent.NewGRPCConnection(context.Background(), true, false, url, "", "", "", log.DefaultLogger) + grpcConn, err := agentclient.NewGRPCConnection(context.Background(), true, false, url, "", "", "", log.DefaultLogger) ui.ExitOnError("error creating gRPC connection", err) defer grpcConn.Close() diff --git a/pkg/agent/client/client.go b/pkg/agent/client/client.go new file mode 100644 index 0000000000..eb863f2cd3 --- /dev/null +++ b/pkg/agent/client/client.go @@ -0,0 +1,111 @@ +package client + +import ( + "context" + "crypto/tls" + "crypto/x509" + "fmt" + "os" + "time" + + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/keepalive" + "google.golang.org/grpc/metadata" + + "github.com/kubeshop/testkube/pkg/version" +) + +const ( + timeout = 10 * time.Second + apiKeyMeta = "api-key" +) + +func NewGRPCConnection( + ctx context.Context, + isInsecure bool, + skipVerify bool, + server string, + certFile, keyFile, caFile string, + logger *zap.SugaredLogger, +) (*grpc.ClientConn, error) { + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12} + if skipVerify { + tlsConfig = &tls.Config{InsecureSkipVerify: true} + } else { + if certFile != "" && keyFile != "" { + if err := clientCert(tlsConfig, certFile, keyFile); err != nil { + return nil, err + } + } + if caFile != "" { + if err := rootCAs(tlsConfig, caFile); err != nil { + return nil, err + } + } + } + + creds := credentials.NewTLS(tlsConfig) + if isInsecure { + creds = insecure.NewCredentials() + } + + kacp := keepalive.ClientParameters{ + Time: 10 * time.Second, + Timeout: 5 * time.Second, + PermitWithoutStream: true, + } + + userAgent := version.Version + "/" + version.Commit + logger.Infow("initiating connection with control plane", "userAgent", userAgent, "server", server, "insecure", isInsecure, "skipVerify", skipVerify, "certFile", certFile, "keyFile", keyFile, "caFile", caFile) + // WithBlock, WithReturnConnectionError and FailOnNonTempDialError are recommended not to be used by gRPC go docs + // but given that Agent will not work if gRPC connection cannot be established, it is ok to use them and assert issues at dial time + return grpc.DialContext( + ctx, + server, + grpc.WithBlock(), + grpc.WithReturnConnectionError(), + grpc.FailOnNonTempDialError(true), + grpc.WithUserAgent(userAgent), + grpc.WithTransportCredentials(creds), + grpc.WithKeepaliveParams(kacp), + ) +} + +func rootCAs(tlsConfig *tls.Config, file ...string) error { + pool := x509.NewCertPool() + for _, f := range file { + rootPEM, err := os.ReadFile(f) + if err != nil || rootPEM == nil { + return fmt.Errorf("agent: error loading or parsing rootCA file: %v", err) + } + ok := pool.AppendCertsFromPEM(rootPEM) + if !ok { + return fmt.Errorf("agent: failed to parse root certificate from %q", f) + } + } + tlsConfig.RootCAs = pool + return nil +} + +func clientCert(tlsConfig *tls.Config, certFile, keyFile string) error { + cert, err := tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + return fmt.Errorf("agent: error loading client certificate: %v", err) + } + cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0]) + if err != nil { + return fmt.Errorf("agent: error parsing client certificate: %v", err) + } + tlsConfig.Certificates = []tls.Certificate{cert} + return nil +} + +func AddAPIKeyMeta(ctx context.Context, apiKey string) context.Context { + md := metadata.Pairs(apiKeyMeta, apiKey) + return metadata.NewOutgoingContext(ctx, md) +} diff --git a/pkg/agent/events.go b/pkg/agent/events.go index 957bfcdae9..c0ad490bd4 100644 --- a/pkg/agent/events.go +++ b/pkg/agent/events.go @@ -10,6 +10,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/encoding/gzip" + agentclient "github.com/kubeshop/testkube/pkg/agent/client" "github.com/kubeshop/testkube/pkg/api/v1/testkube" "github.com/kubeshop/testkube/pkg/cloud" "github.com/kubeshop/testkube/pkg/event/kind/common" @@ -62,7 +63,7 @@ func (ag *Agent) Notify(event testkube.Event) (result testkube.EventResult) { func (ag *Agent) runEventLoop(ctx context.Context) error { opts := []grpc.CallOption{grpc.UseCompressor(gzip.Name)} if ag.apiKey != "" { - ctx = AddAPIKeyMeta(ctx, ag.apiKey) + ctx = agentclient.AddAPIKeyMeta(ctx, ag.apiKey) } stream, err := ag.client.Send(ctx, opts...) diff --git a/pkg/agent/events_test.go b/pkg/agent/events_test.go index eeaab03be9..663e06ac19 100644 --- a/pkg/agent/events_test.go +++ b/pkg/agent/events_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + agentclient "github.com/kubeshop/testkube/pkg/agent/client" "github.com/kubeshop/testkube/pkg/executor/output" "github.com/kubeshop/testkube/pkg/log" "github.com/kubeshop/testkube/pkg/ui" @@ -47,7 +48,7 @@ func TestEventLoop(t *testing.T) { logger, _ := zap.NewDevelopment() - grpcConn, err := agent.NewGRPCConnection(context.Background(), true, false, url, "", "", "", log.DefaultLogger) + grpcConn, err := agentclient.NewGRPCConnection(context.Background(), true, false, url, "", "", "", log.DefaultLogger) ui.ExitOnError("error creating gRPC connection", err) defer grpcConn.Close() diff --git a/pkg/agent/logs.go b/pkg/agent/logs.go index 4040e6f6f6..6fdc81e2ae 100644 --- a/pkg/agent/logs.go +++ b/pkg/agent/logs.go @@ -6,6 +6,7 @@ import ( "math" "time" + agentclient "github.com/kubeshop/testkube/pkg/agent/client" "github.com/kubeshop/testkube/pkg/cloud" "github.com/kubeshop/testkube/pkg/log" @@ -18,7 +19,7 @@ import ( const logStreamRetryCount = 10 func (ag *Agent) runLogStreamLoop(ctx context.Context) error { - ctx = AddAPIKeyMeta(ctx, ag.apiKey) + ctx = agentclient.AddAPIKeyMeta(ctx, ag.apiKey) ag.logger.Infow("initiating log streaming connection with control plane") // creates a new Stream from the client side. ctx is used for the lifetime of the stream. diff --git a/pkg/agent/logs_test.go b/pkg/agent/logs_test.go index 11fc9bfc79..0491467415 100644 --- a/pkg/agent/logs_test.go +++ b/pkg/agent/logs_test.go @@ -9,6 +9,7 @@ import ( "github.com/kubeshop/testkube/internal/config" "github.com/kubeshop/testkube/pkg/agent" + agentclient "github.com/kubeshop/testkube/pkg/agent/client" "github.com/kubeshop/testkube/pkg/api/v1/testkube" "github.com/kubeshop/testkube/pkg/cloud" "github.com/kubeshop/testkube/pkg/executor/output" @@ -46,7 +47,7 @@ func TestLogStream(t *testing.T) { fmt.Fprintf(ctx, "Hi there! RequestURI is %q", ctx.RequestURI()) } - grpcConn, err := agent.NewGRPCConnection(context.Background(), true, false, url, "", "", "", log.DefaultLogger) + grpcConn, err := agentclient.NewGRPCConnection(context.Background(), true, false, url, "", "", "", log.DefaultLogger) ui.ExitOnError("error creating gRPC connection", err) defer grpcConn.Close() diff --git a/pkg/agent/testworkflows.go b/pkg/agent/testworkflows.go index 510a627a47..2001fa9deb 100644 --- a/pkg/agent/testworkflows.go +++ b/pkg/agent/testworkflows.go @@ -12,6 +12,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/encoding/gzip" + agentclient "github.com/kubeshop/testkube/pkg/agent/client" "github.com/kubeshop/testkube/pkg/api/v1/testkube" "github.com/kubeshop/testkube/pkg/cloud" "github.com/kubeshop/testkube/pkg/testworkflows/executionworker/controller" @@ -29,7 +30,7 @@ func getTestWorkflowNotificationType(n testkube.TestWorkflowExecutionNotificatio } func (ag *Agent) runTestWorkflowNotificationsLoop(ctx context.Context) error { - ctx = AddAPIKeyMeta(ctx, ag.apiKey) + ctx = agentclient.AddAPIKeyMeta(ctx, ag.apiKey) ag.logger.Infow("initiating workflow notifications streaming connection with Cloud API") // creates a new Stream from the client side. ctx is used for the lifetime of the stream. diff --git a/pkg/api/v1/testkube/model_test_workflow_result_extended.go b/pkg/api/v1/testkube/model_test_workflow_result_extended.go index ff56259d0d..0738c637ce 100644 --- a/pkg/api/v1/testkube/model_test_workflow_result_extended.go +++ b/pkg/api/v1/testkube/model_test_workflow_result_extended.go @@ -9,7 +9,7 @@ import ( "github.com/gookit/color" - "github.com/kubeshop/testkube/cmd/testworkflow-init/data" + "github.com/kubeshop/testkube/cmd/testworkflow-init/constants" "github.com/kubeshop/testkube/internal/common" ) @@ -152,7 +152,7 @@ func (r *TestWorkflowResult) IsAnyStepPaused() bool { } func (r *TestWorkflowResult) IsKnownStep(ref string) bool { - if ref == data.InitStepName { + if ref == constants.InitStepName { return true } _, ok := r.Steps[ref] @@ -485,7 +485,7 @@ func (r *TestWorkflowResult) HealAborted(sigSequence []TestWorkflowSignature, er // Check all the executable steps in the sequence for i := range sigSequence { ref := sigSequence[i].Ref - if ref == data.InitStepName || !r.IsKnownStep(ref) || len(sigSequence[i].Children) > 0 { + if ref == constants.InitStepName || !r.IsKnownStep(ref) || len(sigSequence[i].Children) > 0 { continue } step := r.Steps[ref] @@ -510,7 +510,7 @@ func (r *TestWorkflowResult) HealAborted(sigSequence []TestWorkflowSignature, er // Do it from end, so we can handle nested groups for i := len(sigSequence) - 1; i >= 0; i-- { ref := sigSequence[i].Ref - if ref == data.InitStepName || !r.IsKnownStep(ref) || len(sigSequence[i].Children) == 0 { + if ref == constants.InitStepName || !r.IsKnownStep(ref) || len(sigSequence[i].Children) == 0 { continue } step := r.Steps[ref] diff --git a/pkg/cloud/data/executor/executor.go b/pkg/cloud/data/executor/executor.go index 0ef9c5c733..47531d443e 100644 --- a/pkg/cloud/data/executor/executor.go +++ b/pkg/cloud/data/executor/executor.go @@ -9,7 +9,7 @@ import ( "google.golang.org/grpc/encoding/gzip" "google.golang.org/protobuf/types/known/structpb" - "github.com/kubeshop/testkube/pkg/agent" + agentclient "github.com/kubeshop/testkube/pkg/agent/client" "github.com/kubeshop/testkube/pkg/cloud" ) @@ -44,7 +44,7 @@ func (e *CloudGRPCExecutor) Execute(ctx context.Context, command Command, payloa Command: string(command), Payload: &s, } - ctx = agent.AddAPIKeyMeta(ctx, e.apiKey) + ctx = agentclient.AddAPIKeyMeta(ctx, e.apiKey) opts := []grpc.CallOption{grpc.UseCompressor(gzip.Name), grpc.MaxCallRecvMsgSize(math.MaxInt32)} cmdResponse, err := e.client.Call(ctx, &req, opts...) if err != nil { diff --git a/pkg/cloud/service.pb.go b/pkg/cloud/service.pb.go index 7a140af6a5..12eabcdee4 100644 --- a/pkg/cloud/service.pb.go +++ b/pkg/cloud/service.pb.go @@ -7,13 +7,12 @@ package cloud import ( - reflect "reflect" - sync "sync" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" emptypb "google.golang.org/protobuf/types/known/emptypb" structpb "google.golang.org/protobuf/types/known/structpb" + reflect "reflect" + sync "sync" ) const ( @@ -975,6 +974,108 @@ func (x *WebsocketData) GetBody() []byte { return nil } +type CredentialRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + ExecutionId string `protobuf:"bytes,2,opt,name=execution_id,json=executionId,proto3" json:"execution_id,omitempty"` +} + +func (x *CredentialRequest) Reset() { + *x = CredentialRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_service_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CredentialRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CredentialRequest) ProtoMessage() {} + +func (x *CredentialRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_service_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CredentialRequest.ProtoReflect.Descriptor instead. +func (*CredentialRequest) Descriptor() ([]byte, []int) { + return file_proto_service_proto_rawDescGZIP(), []int{12} +} + +func (x *CredentialRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *CredentialRequest) GetExecutionId() string { + if x != nil { + return x.ExecutionId + } + return "" +} + +type CredentialResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Content []byte `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` +} + +func (x *CredentialResponse) Reset() { + *x = CredentialResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_service_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CredentialResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CredentialResponse) ProtoMessage() {} + +func (x *CredentialResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_service_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CredentialResponse.ProtoReflect.Descriptor instead. +func (*CredentialResponse) Descriptor() ([]byte, []int) { + return file_proto_service_proto_rawDescGZIP(), []int{13} +} + +func (x *CredentialResponse) GetContent() []byte { + if x != nil { + return x.Content + } + return nil +} + var File_proto_service_proto protoreflect.FileDescriptor var file_proto_service_proto_rawDesc = []byte{ @@ -1084,66 +1185,79 @@ var file_proto_service_proto_rawDesc = []byte{ 0x12, 0x25, 0x0a, 0x06, 0x6f, 0x70, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x4f, 0x70, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x6f, 0x70, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x2a, 0x48, 0x0a, 0x15, 0x4c, - 0x6f, 0x67, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x4c, - 0x4f, 0x47, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, - 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x43, 0x48, - 0x45, 0x43, 0x4b, 0x10, 0x01, 0x2a, 0x69, 0x0a, 0x24, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, - 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, - 0x1b, 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, - 0x5f, 0x4c, 0x4f, 0x47, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x00, 0x12, 0x20, - 0x0a, 0x1c, 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, 0x52, 0x45, 0x41, - 0x4d, 0x5f, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x01, - 0x2a, 0x8a, 0x01, 0x0a, 0x1c, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, - 0x77, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x19, 0x0a, 0x15, 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, - 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, - 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, - 0x4c, 0x4f, 0x47, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, - 0x57, 0x5f, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x10, - 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, - 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x4f, 0x55, 0x54, 0x50, 0x55, 0x54, 0x10, 0x03, 0x2a, 0x4c, 0x0a, - 0x06, 0x4f, 0x70, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x0e, 0x0a, 0x0a, 0x55, 0x4e, 0x53, 0x50, 0x45, - 0x43, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x45, 0x58, 0x54, 0x5f, - 0x46, 0x52, 0x41, 0x4d, 0x45, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x42, 0x49, 0x4e, 0x41, 0x52, - 0x59, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x48, 0x45, 0x41, - 0x4c, 0x54, 0x48, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x03, 0x32, 0x8d, 0x04, 0x0a, 0x10, - 0x54, 0x65, 0x73, 0x74, 0x4b, 0x75, 0x62, 0x65, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x41, 0x50, 0x49, - 0x12, 0x3c, 0x0a, 0x07, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x16, 0x2e, 0x63, 0x6c, - 0x6f, 0x75, 0x64, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x1a, 0x15, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x45, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x36, - 0x0a, 0x04, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x14, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x57, - 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x16, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x28, 0x01, 0x12, 0x35, 0x0a, 0x04, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x15, - 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x43, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, - 0x0c, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x16, 0x2e, - 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x15, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x45, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, - 0x12, 0x48, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x12, 0x19, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x4c, 0x6f, 0x67, 0x73, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x18, 0x2e, 0x63, - 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x4c, 0x6f, 0x67, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x7b, 0x0a, 0x22, 0x47, 0x65, - 0x74, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x12, 0x28, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, - 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x27, 0x2e, 0x63, 0x6c, 0x6f, - 0x75, 0x64, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4e, - 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x50, 0x72, - 0x6f, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x4a, 0x0a, 0x11, 0x43, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x65, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x2e, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2a, 0x48, 0x0a, 0x15, 0x4c, 0x6f, 0x67, 0x73, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x16, 0x0a, 0x12, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x4c, 0x4f, 0x47, 0x5f, 0x4d, + 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x53, 0x54, 0x52, 0x45, + 0x41, 0x4d, 0x5f, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, + 0x01, 0x2a, 0x69, 0x0a, 0x24, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, + 0x77, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x57, 0x4f, 0x52, + 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x4c, 0x4f, 0x47, + 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x00, 0x12, 0x20, 0x0a, 0x1c, 0x57, 0x4f, + 0x52, 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x48, 0x45, + 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x01, 0x2a, 0x8a, 0x01, 0x0a, + 0x1c, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, + 0x15, 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, + 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x57, 0x4f, 0x52, 0x4b, + 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x4c, 0x4f, 0x47, 0x10, + 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, + 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x10, 0x02, 0x12, 0x1a, 0x0a, + 0x16, 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, + 0x5f, 0x4f, 0x55, 0x54, 0x50, 0x55, 0x54, 0x10, 0x03, 0x2a, 0x4c, 0x0a, 0x06, 0x4f, 0x70, 0x63, + 0x6f, 0x64, 0x65, 0x12, 0x0e, 0x0a, 0x0a, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x46, 0x49, 0x45, + 0x44, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x45, 0x58, 0x54, 0x5f, 0x46, 0x52, 0x41, 0x4d, + 0x45, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x42, 0x49, 0x4e, 0x41, 0x52, 0x59, 0x5f, 0x46, 0x52, + 0x41, 0x4d, 0x45, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, + 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x03, 0x32, 0xd3, 0x04, 0x0a, 0x10, 0x54, 0x65, 0x73, 0x74, + 0x4b, 0x75, 0x62, 0x65, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x41, 0x50, 0x49, 0x12, 0x3c, 0x0a, 0x07, + 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x16, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, + 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, + 0x15, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x36, 0x0a, 0x04, 0x53, 0x65, + 0x6e, 0x64, 0x12, 0x14, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x1a, 0x19, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0b, 0x5a, 0x09, 0x70, - 0x6b, 0x67, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x28, 0x01, 0x12, 0x35, 0x0a, 0x04, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x15, 0x2e, 0x63, 0x6c, 0x6f, + 0x75, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x16, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, + 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x45, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x65, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x16, 0x2e, 0x63, 0x6c, 0x6f, 0x75, + 0x64, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x1a, 0x15, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x48, 0x0a, 0x0d, + 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x19, 0x2e, + 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x4c, 0x6f, 0x67, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x18, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, + 0x2e, 0x4c, 0x6f, 0x67, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x7b, 0x0a, 0x22, 0x47, 0x65, 0x74, 0x54, 0x65, 0x73, + 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x28, 0x2e, 0x63, + 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, + 0x77, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x27, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x54, + 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, + 0x01, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x63, + 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x18, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, + 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0b, 0x5a, + 0x09, 0x70, 0x6b, 0x67, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -1159,7 +1273,7 @@ func file_proto_service_proto_rawDescGZIP() []byte { } var file_proto_service_proto_enumTypes = make([]protoimpl.EnumInfo, 4) -var file_proto_service_proto_msgTypes = make([]protoimpl.MessageInfo, 14) +var file_proto_service_proto_msgTypes = make([]protoimpl.MessageInfo, 16) var file_proto_service_proto_goTypes = []interface{}{ (LogsStreamRequestType)(0), // 0: cloud.LogsStreamRequestType (TestWorkflowNotificationsRequestType)(0), // 1: cloud.TestWorkflowNotificationsRequestType @@ -1177,19 +1291,21 @@ var file_proto_service_proto_goTypes = []interface{}{ (*HeaderValue)(nil), // 13: cloud.HeaderValue (*ExecuteResponse)(nil), // 14: cloud.ExecuteResponse (*WebsocketData)(nil), // 15: cloud.WebsocketData - nil, // 16: cloud.ExecuteRequest.HeadersEntry - nil, // 17: cloud.ExecuteResponse.HeadersEntry - (*structpb.Struct)(nil), // 18: google.protobuf.Struct - (*emptypb.Empty)(nil), // 19: google.protobuf.Empty + (*CredentialRequest)(nil), // 16: cloud.CredentialRequest + (*CredentialResponse)(nil), // 17: cloud.CredentialResponse + nil, // 18: cloud.ExecuteRequest.HeadersEntry + nil, // 19: cloud.ExecuteResponse.HeadersEntry + (*structpb.Struct)(nil), // 20: google.protobuf.Struct + (*emptypb.Empty)(nil), // 21: google.protobuf.Empty } var file_proto_service_proto_depIdxs = []int32{ 0, // 0: cloud.LogsStreamRequest.request_type:type_name -> cloud.LogsStreamRequestType - 18, // 1: cloud.CommandRequest.payload:type_name -> google.protobuf.Struct - 16, // 2: cloud.ExecuteRequest.headers:type_name -> cloud.ExecuteRequest.HeadersEntry + 20, // 1: cloud.CommandRequest.payload:type_name -> google.protobuf.Struct + 18, // 2: cloud.ExecuteRequest.headers:type_name -> cloud.ExecuteRequest.HeadersEntry 1, // 3: cloud.TestWorkflowNotificationsRequest.request_type:type_name -> cloud.TestWorkflowNotificationsRequestType 2, // 4: cloud.TestWorkflowNotificationsResponse.type:type_name -> cloud.TestWorkflowNotificationType 12, // 5: cloud.ProContextResponse.capabilities:type_name -> cloud.Capability - 17, // 6: cloud.ExecuteResponse.headers:type_name -> cloud.ExecuteResponse.HeadersEntry + 19, // 6: cloud.ExecuteResponse.headers:type_name -> cloud.ExecuteResponse.HeadersEntry 3, // 7: cloud.WebsocketData.opcode:type_name -> cloud.Opcode 13, // 8: cloud.ExecuteRequest.HeadersEntry.value:type_name -> cloud.HeaderValue 13, // 9: cloud.ExecuteResponse.HeadersEntry.value:type_name -> cloud.HeaderValue @@ -1199,16 +1315,18 @@ var file_proto_service_proto_depIdxs = []int32{ 14, // 13: cloud.TestKubeCloudAPI.ExecuteAsync:input_type -> cloud.ExecuteResponse 5, // 14: cloud.TestKubeCloudAPI.GetLogsStream:input_type -> cloud.LogsStreamResponse 10, // 15: cloud.TestKubeCloudAPI.GetTestWorkflowNotificationsStream:input_type -> cloud.TestWorkflowNotificationsResponse - 19, // 16: cloud.TestKubeCloudAPI.GetProContext:input_type -> google.protobuf.Empty - 8, // 17: cloud.TestKubeCloudAPI.Execute:output_type -> cloud.ExecuteRequest - 19, // 18: cloud.TestKubeCloudAPI.Send:output_type -> google.protobuf.Empty - 7, // 19: cloud.TestKubeCloudAPI.Call:output_type -> cloud.CommandResponse - 8, // 20: cloud.TestKubeCloudAPI.ExecuteAsync:output_type -> cloud.ExecuteRequest - 4, // 21: cloud.TestKubeCloudAPI.GetLogsStream:output_type -> cloud.LogsStreamRequest - 9, // 22: cloud.TestKubeCloudAPI.GetTestWorkflowNotificationsStream:output_type -> cloud.TestWorkflowNotificationsRequest - 11, // 23: cloud.TestKubeCloudAPI.GetProContext:output_type -> cloud.ProContextResponse - 17, // [17:24] is the sub-list for method output_type - 10, // [10:17] is the sub-list for method input_type + 21, // 16: cloud.TestKubeCloudAPI.GetProContext:input_type -> google.protobuf.Empty + 16, // 17: cloud.TestKubeCloudAPI.GetCredential:input_type -> cloud.CredentialRequest + 8, // 18: cloud.TestKubeCloudAPI.Execute:output_type -> cloud.ExecuteRequest + 21, // 19: cloud.TestKubeCloudAPI.Send:output_type -> google.protobuf.Empty + 7, // 20: cloud.TestKubeCloudAPI.Call:output_type -> cloud.CommandResponse + 8, // 21: cloud.TestKubeCloudAPI.ExecuteAsync:output_type -> cloud.ExecuteRequest + 4, // 22: cloud.TestKubeCloudAPI.GetLogsStream:output_type -> cloud.LogsStreamRequest + 9, // 23: cloud.TestKubeCloudAPI.GetTestWorkflowNotificationsStream:output_type -> cloud.TestWorkflowNotificationsRequest + 11, // 24: cloud.TestKubeCloudAPI.GetProContext:output_type -> cloud.ProContextResponse + 17, // 25: cloud.TestKubeCloudAPI.GetCredential:output_type -> cloud.CredentialResponse + 18, // [18:26] is the sub-list for method output_type + 10, // [10:18] is the sub-list for method input_type 10, // [10:10] is the sub-list for extension type_name 10, // [10:10] is the sub-list for extension extendee 0, // [0:10] is the sub-list for field type_name @@ -1364,6 +1482,30 @@ func file_proto_service_proto_init() { return nil } } + file_proto_service_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CredentialRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_service_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CredentialResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -1371,7 +1513,7 @@ func file_proto_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_service_proto_rawDesc, NumEnums: 4, - NumMessages: 14, + NumMessages: 16, NumExtensions: 0, NumServices: 1, }, diff --git a/pkg/cloud/service_grpc.pb.go b/pkg/cloud/service_grpc.pb.go index 8aab7a2119..5b329874fe 100644 --- a/pkg/cloud/service_grpc.pb.go +++ b/pkg/cloud/service_grpc.pb.go @@ -8,7 +8,6 @@ package cloud import ( context "context" - grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" @@ -33,6 +32,7 @@ type TestKubeCloudAPIClient interface { GetLogsStream(ctx context.Context, opts ...grpc.CallOption) (TestKubeCloudAPI_GetLogsStreamClient, error) GetTestWorkflowNotificationsStream(ctx context.Context, opts ...grpc.CallOption) (TestKubeCloudAPI_GetTestWorkflowNotificationsStreamClient, error) GetProContext(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ProContextResponse, error) + GetCredential(ctx context.Context, in *CredentialRequest, opts ...grpc.CallOption) (*CredentialResponse, error) } type testKubeCloudAPIClient struct { @@ -219,6 +219,15 @@ func (c *testKubeCloudAPIClient) GetProContext(ctx context.Context, in *emptypb. return out, nil } +func (c *testKubeCloudAPIClient) GetCredential(ctx context.Context, in *CredentialRequest, opts ...grpc.CallOption) (*CredentialResponse, error) { + out := new(CredentialResponse) + err := c.cc.Invoke(ctx, "/cloud.TestKubeCloudAPI/GetCredential", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // TestKubeCloudAPIServer is the server API for TestKubeCloudAPI service. // All implementations must embed UnimplementedTestKubeCloudAPIServer // for forward compatibility @@ -232,6 +241,7 @@ type TestKubeCloudAPIServer interface { GetLogsStream(TestKubeCloudAPI_GetLogsStreamServer) error GetTestWorkflowNotificationsStream(TestKubeCloudAPI_GetTestWorkflowNotificationsStreamServer) error GetProContext(context.Context, *emptypb.Empty) (*ProContextResponse, error) + GetCredential(context.Context, *CredentialRequest) (*CredentialResponse, error) mustEmbedUnimplementedTestKubeCloudAPIServer() } @@ -260,6 +270,9 @@ func (UnimplementedTestKubeCloudAPIServer) GetTestWorkflowNotificationsStream(Te func (UnimplementedTestKubeCloudAPIServer) GetProContext(context.Context, *emptypb.Empty) (*ProContextResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetProContext not implemented") } +func (UnimplementedTestKubeCloudAPIServer) GetCredential(context.Context, *CredentialRequest) (*CredentialResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetCredential not implemented") +} func (UnimplementedTestKubeCloudAPIServer) mustEmbedUnimplementedTestKubeCloudAPIServer() {} // UnsafeTestKubeCloudAPIServer may be embedded to opt out of forward compatibility for this service. @@ -439,6 +452,24 @@ func _TestKubeCloudAPI_GetProContext_Handler(srv interface{}, ctx context.Contex return interceptor(ctx, in, info, handler) } +func _TestKubeCloudAPI_GetCredential_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CredentialRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TestKubeCloudAPIServer).GetCredential(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cloud.TestKubeCloudAPI/GetCredential", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TestKubeCloudAPIServer).GetCredential(ctx, req.(*CredentialRequest)) + } + return interceptor(ctx, in, info, handler) +} + // TestKubeCloudAPI_ServiceDesc is the grpc.ServiceDesc for TestKubeCloudAPI service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -454,6 +485,10 @@ var TestKubeCloudAPI_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetProContext", Handler: _TestKubeCloudAPI_GetProContext_Handler, }, + { + MethodName: "GetCredential", + Handler: _TestKubeCloudAPI_GetCredential_Handler, + }, }, Streams: []grpc.StreamDesc{ { diff --git a/pkg/controlplane/server.go b/pkg/controlplane/server.go index 8589571a22..a88a744c98 100644 --- a/pkg/controlplane/server.go +++ b/pkg/controlplane/server.go @@ -62,6 +62,10 @@ func (s *Server) GetProContext(_ context.Context, _ *emptypb.Empty) (*cloud.ProC return nil, status.Error(codes.Unimplemented, "not supported in the standalone version") } +func (s *Server) GetCredential(_ context.Context, _ *cloud.CredentialRequest) (*cloud.CredentialResponse, error) { + return nil, status.Error(codes.Unimplemented, "not supported in the standalone version") +} + func (s *Server) ExecuteAsync(srv cloud.TestKubeCloudAPI_ExecuteAsyncServer) error { ctx, cancel := context.WithCancel(srv.Context()) g, _ := errgroup.WithContext(ctx) diff --git a/pkg/credentials/expressions.go b/pkg/credentials/expressions.go new file mode 100644 index 0000000000..8ca52ada84 --- /dev/null +++ b/pkg/credentials/expressions.go @@ -0,0 +1,38 @@ +package credentials + +import ( + "context" + "fmt" + + "github.com/kubeshop/testkube/pkg/expressions" +) + +func NewCredentialMachine(repository CredentialRepository, observers ...func(name string, value string)) expressions.Machine { + return expressions.NewMachine().RegisterFunction("credential", func(values ...expressions.StaticValue) (interface{}, bool, error) { + computed := false + if len(values) == 2 { + if values[1].IsBool() { + computed, _ = values[1].BoolValue() + } else { + return nil, true, fmt.Errorf(`"credential" function expects 2nd argument to be boolean, %s provided`, values[1].String()) + } + } else if len(values) != 1 { + return nil, true, fmt.Errorf(`"credential" function expects 1-2 arguments, %d provided`, len(values)) + } + + name, _ := values[0].StringValue() + value, err := repository.Get(context.Background(), name) + if err != nil { + return nil, true, err + } + if computed { + expr, err := expressions.CompileAndResolveTemplate(string(value)) + return expr, true, err + } + valueStr := string(value) + for i := range observers { + observers[i](name, valueStr) + } + return valueStr, true, nil + }) +} diff --git a/pkg/credentials/mock_repository.go b/pkg/credentials/mock_repository.go new file mode 100644 index 0000000000..ec907f03a9 --- /dev/null +++ b/pkg/credentials/mock_repository.go @@ -0,0 +1,50 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/kubeshop/testkube/pkg/credentials (interfaces: CredentialRepository) + +// Package credentials is a generated GoMock package. +package credentials + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockCredentialRepository is a mock of CredentialRepository interface. +type MockCredentialRepository struct { + ctrl *gomock.Controller + recorder *MockCredentialRepositoryMockRecorder +} + +// MockCredentialRepositoryMockRecorder is the mock recorder for MockCredentialRepository. +type MockCredentialRepositoryMockRecorder struct { + mock *MockCredentialRepository +} + +// NewMockCredentialRepository creates a new mock instance. +func NewMockCredentialRepository(ctrl *gomock.Controller) *MockCredentialRepository { + mock := &MockCredentialRepository{ctrl: ctrl} + mock.recorder = &MockCredentialRepositoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCredentialRepository) EXPECT() *MockCredentialRepositoryMockRecorder { + return m.recorder +} + +// Get mocks base method. +func (m *MockCredentialRepository) Get(arg0 context.Context, arg1 string) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0, arg1) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockCredentialRepositoryMockRecorder) Get(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockCredentialRepository)(nil).Get), arg0, arg1) +} diff --git a/pkg/credentials/repository.go b/pkg/credentials/repository.go new file mode 100644 index 0000000000..65f568a2c3 --- /dev/null +++ b/pkg/credentials/repository.go @@ -0,0 +1,37 @@ +package credentials + +import ( + "context" + "math" + + "google.golang.org/grpc" + "google.golang.org/grpc/encoding/gzip" + + agentclient "github.com/kubeshop/testkube/pkg/agent/client" + "github.com/kubeshop/testkube/pkg/cloud" +) + +//go:generate mockgen -destination=./mock_repository.go -package=credentials "github.com/kubeshop/testkube/pkg/credentials" CredentialRepository +type CredentialRepository interface { + Get(ctx context.Context, name string) ([]byte, error) +} + +type credentialRepository struct { + client cloud.TestKubeCloudAPIClient + apiKey string + executionId string +} + +func NewCredentialRepository(client cloud.TestKubeCloudAPIClient, apiKey, executionId string) CredentialRepository { + return &credentialRepository{client: client, apiKey: apiKey, executionId: executionId} +} + +func (c *credentialRepository) Get(ctx context.Context, name string) ([]byte, error) { + ctx = agentclient.AddAPIKeyMeta(ctx, c.apiKey) + opts := []grpc.CallOption{grpc.UseCompressor(gzip.Name), grpc.MaxCallRecvMsgSize(math.MaxInt32)} + result, err := c.client.GetCredential(ctx, &cloud.CredentialRequest{Name: name, ExecutionId: c.executionId}, opts...) + if err != nil { + return nil, err + } + return result.Content, nil +} diff --git a/pkg/envs/bool.go b/pkg/envs/bool.go deleted file mode 100644 index 28ada15038..0000000000 --- a/pkg/envs/bool.go +++ /dev/null @@ -1,18 +0,0 @@ -package envs - -import ( - "os" - "strconv" -) - -func IsTrue(name string) (is bool) { - var err error - if val, ok := os.LookupEnv(name); ok { - is, err = strconv.ParseBool(val) - if err != nil { - return false - } - } - - return is -} diff --git a/pkg/executor/scraper/factory/factory.go b/pkg/executor/scraper/factory/factory.go index 1b8d89bff0..bf4b1d6d46 100644 --- a/pkg/executor/scraper/factory/factory.go +++ b/pkg/executor/scraper/factory/factory.go @@ -8,7 +8,7 @@ import ( cloudevents "github.com/cloudevents/sdk-go/v2" "github.com/pkg/errors" - "github.com/kubeshop/testkube/pkg/agent" + agentclient "github.com/kubeshop/testkube/pkg/agent/client" "github.com/kubeshop/testkube/pkg/cloud" cloudscraper "github.com/kubeshop/testkube/pkg/cloud/data/artifact" cloudexecutor "github.com/kubeshop/testkube/pkg/cloud/data/executor" @@ -102,7 +102,7 @@ func getRemoteStorageUploader(ctx context.Context, params envs.Params) (uploader output.PrintLogf( "%s Uploading artifacts using Remote Storage Uploader (timeout:%ds, agentInsecure:%v, agentSkipVerify: %v, url: %s, scraperSkipVerify: %v)", ui.IconCheckMark, params.ProConnectionTimeoutSec, params.ProAPITLSInsecure, params.ProAPISkipVerify, params.ProAPIURL, params.SkipVerify) - grpcConn, err := agent.NewGRPCConnection( + grpcConn, err := agentclient.NewGRPCConnection( ctxTimeout, params.ProAPITLSInsecure, params.ProAPISkipVerify, diff --git a/pkg/log/log.go b/pkg/log/log.go index 0806edb835..73dbd52473 100644 --- a/pkg/log/log.go +++ b/pkg/log/log.go @@ -2,19 +2,51 @@ package log import ( "log" + "os" + "strconv" "go.uber.org/zap" "go.uber.org/zap/zapcore" - - "github.com/kubeshop/testkube/pkg/envs" ) +func IsTrue(name string) (is bool) { + var err error + if val, ok := os.LookupEnv(name); ok { + is, err = strconv.ParseBool(val) + if err != nil { + return false + } + } + + return is +} + // New returns new logger instance func New() *zap.SugaredLogger { atomicLevel := zap.NewAtomicLevel() atomicLevel.SetLevel(zap.InfoLevel) - if envs.IsTrue("DEBUG") { + if IsTrue("DEBUG") { + atomicLevel.SetLevel(zap.DebugLevel) + } + + zapCfg := zap.NewProductionConfig() + zapCfg.Level = atomicLevel + zapCfg.EncoderConfig.EncodeTime = zapcore.RFC3339TimeEncoder + + z, err := zapCfg.Build() + if err != nil { + log.Fatalf("can't initialize zap logger: %v", err) + } + logger := z.Sugar() + return logger +} + +func NewSilent() *zap.SugaredLogger { + atomicLevel := zap.NewAtomicLevel() + + atomicLevel.SetLevel(zap.WarnLevel) + if IsTrue("DEBUG") { atomicLevel.SetLevel(zap.DebugLevel) } diff --git a/pkg/logs/adapter/cloud_test.go b/pkg/logs/adapter/cloud_test.go index badd6c6caa..c087d5b05c 100644 --- a/pkg/logs/adapter/cloud_test.go +++ b/pkg/logs/adapter/cloud_test.go @@ -19,7 +19,7 @@ import ( "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" - "github.com/kubeshop/testkube/pkg/agent" + agentclient "github.com/kubeshop/testkube/pkg/agent/client" "github.com/kubeshop/testkube/pkg/log" "github.com/kubeshop/testkube/pkg/logs/events" "github.com/kubeshop/testkube/pkg/logs/pb" @@ -38,7 +38,7 @@ func TestCloudAdapter(t *testing.T) { id := "id1" // and connection - grpcConn, err := agent.NewGRPCConnection(ctx, true, true, ts.Url, "", "", "", log.DefaultLogger) + grpcConn, err := agentclient.NewGRPCConnection(ctx, true, true, ts.Url, "", "", "", log.DefaultLogger) assert.NoError(t, err) defer grpcConn.Close() @@ -80,7 +80,7 @@ func TestCloudAdapter(t *testing.T) { id3 := "id3" // and connection - grpcConn, err := agent.NewGRPCConnection(ctx, true, true, ts.Url, "", "", "", log.DefaultLogger) + grpcConn, err := agentclient.NewGRPCConnection(ctx, true, true, ts.Url, "", "", "", log.DefaultLogger) assert.NoError(t, err) defer grpcConn.Close() grpcClient := pb.NewCloudLogsServiceClient(grpcConn) @@ -129,7 +129,7 @@ func TestCloudAdapter(t *testing.T) { id := "id1M" // and grpc connetion to the server - grpcConn, err := agent.NewGRPCConnection(ctx, true, true, ts.Url, "", "", "", log.DefaultLogger) + grpcConn, err := agentclient.NewGRPCConnection(ctx, true, true, ts.Url, "", "", "", log.DefaultLogger) assert.NoError(t, err) defer grpcConn.Close() @@ -163,7 +163,7 @@ func TestCloudAdapter(t *testing.T) { ctx := context.Background() // and grpc connetion to the server - grpcConn, err := agent.NewGRPCConnection(ctx, true, true, ts.Url, "", "", "", log.DefaultLogger) + grpcConn, err := agentclient.NewGRPCConnection(ctx, true, true, ts.Url, "", "", "", log.DefaultLogger) assert.NoError(t, err) defer grpcConn.Close() diff --git a/pkg/testworkflows/executionworker/controller/notifier.go b/pkg/testworkflows/executionworker/controller/notifier.go index b28a5fa225..ea76a2f1ce 100644 --- a/pkg/testworkflows/executionworker/controller/notifier.go +++ b/pkg/testworkflows/executionworker/controller/notifier.go @@ -7,7 +7,6 @@ import ( "time" "github.com/kubeshop/testkube/cmd/testworkflow-init/constants" - "github.com/kubeshop/testkube/cmd/testworkflow-init/data" "github.com/kubeshop/testkube/cmd/testworkflow-init/instructions" "github.com/kubeshop/testkube/internal/common" "github.com/kubeshop/testkube/pkg/api/v1/testkube" @@ -75,7 +74,7 @@ func (n *notifier) Raw(ref string, ts time.Time, message string, temporary bool) n.lastTs = ts } if message != "" { - if ref == data.InitStepName { + if ref == constants.InitStepName { ref = "" } n.send(Notification{ @@ -107,7 +106,7 @@ func (n *notifier) Event(ref string, ts time.Time, level, reason, message string } func (n *notifier) Output(ref string, ts time.Time, output *instructions.Instruction) { - if ref == data.InitStepName { + if ref == constants.InitStepName { ref = "" } else if ref != "" { if _, ok := n.result.Steps[ref]; !ok { @@ -169,7 +168,7 @@ func (n *notifier) Instruction(ts time.Time, hint instructions.Instruction) { ts = ts.UTC() // Load the current step information - init := hint.Ref == data.InitStepName + init := hint.Ref == constants.InitStepName step, ok := n.result.Steps[hint.Ref] if init { step = *n.result.Initialization @@ -322,7 +321,7 @@ func (n *notifier) fillGaps(force bool) { // TODO: estimate startedAt/finishedAt too? - if ref == data.InitStepName { + if ref == constants.InitStepName { n.result.Initialization.Status = common.Ptr(container.Statuses[refIndexes[ref]].Status) n.result.Initialization.ExitCode = float64(container.Statuses[refIndexes[ref]].ExitCode) } else { diff --git a/pkg/testworkflows/executionworker/controller/utils.go b/pkg/testworkflows/executionworker/controller/utils.go index 4232a561f0..5eb97bdb21 100644 --- a/pkg/testworkflows/executionworker/controller/utils.go +++ b/pkg/testworkflows/executionworker/controller/utils.go @@ -6,7 +6,7 @@ import ( corev1 "k8s.io/api/core/v1" - "github.com/kubeshop/testkube/cmd/testworkflow-init/data" + "github.com/kubeshop/testkube/cmd/testworkflow-init/constants" "github.com/kubeshop/testkube/pkg/api/v1/testkube" "github.com/kubeshop/testkube/pkg/testworkflows/testworkflowprocessor/action/actiontypes" "github.com/kubeshop/testkube/pkg/testworkflows/testworkflowprocessor/action/actiontypes/lite" @@ -48,8 +48,8 @@ func ExtractRefsFromActionList(list actiontypes.ActionList) (started []string, f for i := range list { switch list[i].Type() { case lite.ActionTypeSetup: - started = append(started, data.InitStepName) - finished = append(finished, data.InitStepName) + started = append(started, constants.InitStepName) + finished = append(finished, constants.InitStepName) case lite.ActionTypeStart: started = append(started, *list[i].Start) case lite.ActionTypeEnd: diff --git a/pkg/testworkflows/executionworker/controller/watchers/commons.go b/pkg/testworkflows/executionworker/controller/watchers/commons.go index 044012e517..4c8378177d 100644 --- a/pkg/testworkflows/executionworker/controller/watchers/commons.go +++ b/pkg/testworkflows/executionworker/controller/watchers/commons.go @@ -14,7 +14,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/watch" - "github.com/kubeshop/testkube/cmd/testworkflow-init/data" + "github.com/kubeshop/testkube/cmd/testworkflow-init/constants" "github.com/kubeshop/testkube/pkg/api/v1/testkube" ) @@ -325,7 +325,7 @@ func ReadContainerResult(status *corev1.ContainerStatus, errorFallback string) C } // Gather information - stepStatus := testkube.TestWorkflowStepStatus(data.StepStatusFromCode(match[1])) + stepStatus := testkube.TestWorkflowStepStatus(constants.StepStatusFromCode(match[1])) exitCode, _ := strconv.Atoi(match[2]) // Don't trust after there is `aborted` status detected diff --git a/pkg/testworkflows/executionworker/controller/watchinstrumentedpod.go b/pkg/testworkflows/executionworker/controller/watchinstrumentedpod.go index a5de517ec6..e050805a0d 100644 --- a/pkg/testworkflows/executionworker/controller/watchinstrumentedpod.go +++ b/pkg/testworkflows/executionworker/controller/watchinstrumentedpod.go @@ -9,7 +9,6 @@ import ( "k8s.io/client-go/kubernetes" "github.com/kubeshop/testkube/cmd/testworkflow-init/constants" - "github.com/kubeshop/testkube/cmd/testworkflow-init/data" "github.com/kubeshop/testkube/cmd/testworkflow-init/instructions" "github.com/kubeshop/testkube/pkg/api/v1/testkube" watchers2 "github.com/kubeshop/testkube/pkg/testworkflows/executionworker/controller/watchers" @@ -101,7 +100,7 @@ func WatchInstrumentedPod(parentCtx context.Context, clientSet kubernetes.Interf initialRefs := make([]string, len(actions)) for i := range refs { for j := range refs[i] { - if refs[i][j] == data.InitStepName { + if refs[i][j] == constants.InitStepName { initialRefs[i] = "" break } @@ -115,7 +114,7 @@ func WatchInstrumentedPod(parentCtx context.Context, clientSet kubernetes.Interf } // Iterate over containers - lastStarted := data.InitStepName + lastStarted := constants.InitStepName containersReady := false for containerIndex := 0; containerIndex < len(refs); containerIndex++ { aborted := false diff --git a/pkg/testworkflows/testworkflowprocessor/action/actiontypes/analysis.go b/pkg/testworkflows/testworkflowprocessor/action/actiontypes/analysis.go index 97f527085c..db1c7af376 100644 --- a/pkg/testworkflows/testworkflowprocessor/action/actiontypes/analysis.go +++ b/pkg/testworkflows/testworkflowprocessor/action/actiontypes/analysis.go @@ -1,7 +1,7 @@ package actiontypes import ( - "github.com/kubeshop/testkube/cmd/testworkflow-init/data" + "github.com/kubeshop/testkube/cmd/testworkflow-init/constants" "github.com/kubeshop/testkube/pkg/expressions" "github.com/kubeshop/testkube/pkg/testworkflows/testworkflowprocessor/action/actiontypes/lite" ) @@ -14,7 +14,7 @@ func (a ActionList) GetLastRef() string { case lite.ActionTypeStart: return *a[i].Start case lite.ActionTypeSetup: - return data.InitStepName + return constants.InitStepName } } return "" diff --git a/pkg/testworkflows/testworkflowprocessor/secretmachine.go b/pkg/testworkflows/testworkflowprocessor/secretmachine.go index 84ba56943f..5784f8e94a 100644 --- a/pkg/testworkflows/testworkflowprocessor/secretmachine.go +++ b/pkg/testworkflows/testworkflowprocessor/secretmachine.go @@ -19,7 +19,7 @@ func createSecretMachine(mapEnvs map[string]corev1.EnvVarSource) expressions.Mac if values[2].IsBool() { computed, _ = values[2].BoolValue() } else { - return nil, true, fmt.Errorf(`"secret" function expects 3rd argument to be boolean, %s provided`, values[3].String()) + return nil, true, fmt.Errorf(`"secret" function expects 3rd argument to be boolean, %s provided`, values[2].String()) } } else if len(values) != 2 { return nil, true, fmt.Errorf(`"secret" function expects 2-3 arguments, %d provided`, len(values)) diff --git a/proto/service.proto b/proto/service.proto index 6911fb50b5..7ed7f0db95 100644 --- a/proto/service.proto +++ b/proto/service.proto @@ -17,6 +17,7 @@ service TestKubeCloudAPI { rpc GetLogsStream(stream LogsStreamResponse) returns (stream LogsStreamRequest); rpc GetTestWorkflowNotificationsStream(stream TestWorkflowNotificationsResponse) returns (stream TestWorkflowNotificationsRequest); rpc GetProContext(google.protobuf.Empty) returns (ProContextResponse); + rpc GetCredential(CredentialRequest) returns (CredentialResponse); } enum LogsStreamRequestType { @@ -114,3 +115,12 @@ message WebsocketData { Opcode opcode = 1; bytes body = 2; } + +message CredentialRequest { + string name = 1; + string execution_id = 2; +} + +message CredentialResponse { + bytes content = 1; +} From bd39964e09973c67bf90e2b8ef1f13944c8522f2 Mon Sep 17 00:00:00 2001 From: Vladislav Sukhin Date: Mon, 25 Nov 2024 18:12:59 +0300 Subject: [PATCH 17/30] feat: api methods for service logs Signed-off-by: Vladislav Sukhin --- internal/app/api/v1/server.go | 2 + internal/app/api/v1/testworkflowexecutions.go | 133 ++++++++++++++---- 2 files changed, 107 insertions(+), 28 deletions(-) diff --git a/internal/app/api/v1/server.go b/internal/app/api/v1/server.go index 615f1e52c7..7b6890c605 100644 --- a/internal/app/api/v1/server.go +++ b/internal/app/api/v1/server.go @@ -150,7 +150,9 @@ func (s *TestkubeAPI) Init(server server.HTTPServer) { testWorkflowExecutions.Post("/", s.ExecuteTestWorkflowHandler()) testWorkflowExecutions.Get("/:executionID", s.GetTestWorkflowExecutionHandler()) testWorkflowExecutions.Get("/:executionID/notifications", s.StreamTestWorkflowExecutionNotificationsHandler()) + testWorkflowExecutions.Get("/:executionID/notifications/:serviceName/:serviceIndex", s.StreamTestWorkflowExecutionServiceNotificationsHandler()) testWorkflowExecutions.Get("/:executionID/notifications/stream", s.StreamTestWorkflowExecutionNotificationsWebSocketHandler()) + testWorkflowExecutions.Get("/:executionID/notifications/stream:serviceName/:serviceIndex", s.StreamTestWorkflowExecutionServiceNotificationsWebSocketHandler()) testWorkflowExecutions.Post("/:executionID/abort", s.AbortTestWorkflowExecutionHandler()) testWorkflowExecutions.Post("/:executionID/pause", s.PauseTestWorkflowExecutionHandler()) testWorkflowExecutions.Post("/:executionID/resume", s.ResumeTestWorkflowExecutionHandler()) diff --git a/internal/app/api/v1/testworkflowexecutions.go b/internal/app/api/v1/testworkflowexecutions.go index 637ee3609f..99a4288e90 100644 --- a/internal/app/api/v1/testworkflowexecutions.go +++ b/internal/app/api/v1/testworkflowexecutions.go @@ -14,6 +14,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/websocket/v2" "github.com/pkg/errors" + "github.com/valyala/fasthttp" "github.com/kubeshop/testkube/internal/app/api/apiutils" "github.com/kubeshop/testkube/internal/common" @@ -23,6 +24,41 @@ import ( "github.com/kubeshop/testkube/pkg/testworkflows/executionworker/executionworkertypes" ) +func (s *TestkubeAPI) streamNotifications(ctx *fasthttp.RequestCtx, id string, notifications executionworkertypes.NotificationsWatcher) { + // Initiate processing event stream + ctx.SetContentType("text/event-stream") + ctx.Response.Header.Set("Cache-Control", "no-cache") + ctx.Response.Header.Set("Connection", "keep-alive") + ctx.Response.Header.Set("Transfer-Encoding", "chunked") + + // Stream the notifications + ctx.SetBodyStreamWriter(func(w *bufio.Writer) { + err := w.Flush() + if err != nil { + s.Log.Errorw("could not flush stream body", "error", err, "id", id) + } + + enc := json.NewEncoder(w) + + for n := range notifications.Channel() { + err := enc.Encode(n) + if err != nil { + s.Log.Errorw("could not encode value", "error", err, "id", id) + } + + _, err = fmt.Fprintf(w, "\n") + if err != nil { + s.Log.Errorw("could not print new line", "error", err, "id", id) + } + + err = w.Flush() + if err != nil { + s.Log.Errorw("could not flush stream body", "error", err, "id", id) + } + } + }) +} + func (s *TestkubeAPI) StreamTestWorkflowExecutionNotificationsHandler() fiber.Handler { return func(c *fiber.Ctx) error { ctx := c.Context() @@ -47,39 +83,40 @@ func (s *TestkubeAPI) StreamTestWorkflowExecutionNotificationsHandler() fiber.Ha return s.BadRequest(c, errPrefix, "fetching notifications", notifications.Err()) } - // Initiate processing event stream - ctx.SetContentType("text/event-stream") - ctx.Response.Header.Set("Cache-Control", "no-cache") - ctx.Response.Header.Set("Connection", "keep-alive") - ctx.Response.Header.Set("Transfer-Encoding", "chunked") - - // Stream the notifications - ctx.SetBodyStreamWriter(func(w *bufio.Writer) { - err := w.Flush() - if err != nil { - s.Log.Errorw("could not flush stream body", "error", err, "id", id) - } - - enc := json.NewEncoder(w) + s.streamNotifications(ctx, id, notifications) + return nil + } +} - for n := range notifications.Channel() { - err := enc.Encode(n) - if err != nil { - s.Log.Errorw("could not encode value", "error", err, "id", id) - } +func (s *TestkubeAPI) StreamTestWorkflowExecutionServiceNotificationsHandler() fiber.Handler { + return func(c *fiber.Ctx) error { + ctx := c.Context() + executionID := c.Params("executionID") + serviceName := c.Params("serviceName") + serviceIndex := c.Params("serviceIndex") + errPrefix := fmt.Sprintf("failed to stream test workflow execution service '%s' instance '%s' notifications '%s'", + serviceName, serviceIndex, executionID) - _, err = fmt.Fprintf(w, "\n") - if err != nil { - s.Log.Errorw("could not print new line", "error", err, "id", id) - } + // Fetch execution from database + execution, err := s.TestWorkflowResults.Get(ctx, executionID) + if err != nil { + return s.ClientError(c, errPrefix, err) + } - err = w.Flush() - if err != nil { - s.Log.Errorw("could not flush stream body", "error", err, "id", id) - } - } + // Check for the logs + id := fmt.Sprintf("%s-%s-%s", execution.Id, serviceName, serviceIndex) + notifications := s.ExecutionWorkerClient.Notifications(ctx, id, executionworkertypes.NotificationsOptions{ + Hints: executionworkertypes.Hints{ + Namespace: execution.Namespace, + ScheduledAt: common.Ptr(execution.ScheduledAt), + Signature: execution.Signature, + }, }) + if notifications.Err() != nil { + return s.BadRequest(c, errPrefix, "fetching notifications", notifications.Err()) + } + s.streamNotifications(ctx, id, notifications) return nil } } @@ -121,6 +158,46 @@ func (s *TestkubeAPI) StreamTestWorkflowExecutionNotificationsWebSocketHandler() }) } +func (s *TestkubeAPI) StreamTestWorkflowExecutionServiceNotificationsWebSocketHandler() fiber.Handler { + return websocket.New(func(c *websocket.Conn) { + ctx, ctxCancel := context.WithCancel(context.Background()) + executionID := c.Params("executionID") + serviceName := c.Params("serviceName") + serviceIndex := c.Params("serviceIndex") + + // Stop reading when the WebSocket connection is already closed + originalClose := c.CloseHandler() + c.SetCloseHandler(func(code int, text string) error { + ctxCancel() + return originalClose(code, text) + }) + defer c.Conn.Close() + + // Fetch execution from database + execution, err := s.TestWorkflowResults.Get(ctx, executionID) + if err != nil { + return + } + + // Check for the logs + id := fmt.Sprintf("%s-%s-%s", execution.Id, serviceName, serviceIndex) + notifications := s.ExecutionWorkerClient.Notifications(ctx, id, executionworkertypes.NotificationsOptions{ + Hints: executionworkertypes.Hints{ + Namespace: execution.Namespace, + Signature: execution.Signature, + ScheduledAt: common.Ptr(execution.ScheduledAt), + }, + }) + if notifications.Err() != nil { + return + } + + for n := range notifications.Channel() { + _ = c.WriteJSON(n) + } + }) +} + func (s *TestkubeAPI) ListTestWorkflowExecutionsHandler() fiber.Handler { return func(c *fiber.Ctx) error { errPrefix := "failed to list test workflow executions" From 2c7796249dd1cc81b76fca5a05a1e69aba282293 Mon Sep 17 00:00:00 2001 From: Vladislav Sukhin Date: Mon, 25 Nov 2024 19:11:58 +0300 Subject: [PATCH 18/30] fix: client for get service logs Signed-off-by: Vladislav Sukhin --- .../commands/testworkflows/run.go | 47 +++++++++++++++++-- .../commands/testworkflows/watch.go | 10 +++- internal/app/api/v1/server.go | 4 +- pkg/api/v1/client/interface.go | 1 + pkg/api/v1/client/testworkflow.go | 8 ++++ 5 files changed, 64 insertions(+), 6 deletions(-) diff --git a/cmd/kubectl-testkube/commands/testworkflows/run.go b/cmd/kubectl-testkube/commands/testworkflows/run.go index 5c96c690d3..573553c4a1 100644 --- a/cmd/kubectl-testkube/commands/testworkflows/run.go +++ b/cmd/kubectl-testkube/commands/testworkflows/run.go @@ -47,6 +47,8 @@ func NewRunTestWorkflowCmd() *cobra.Command { masks []string tags map[string]string selectors []string + serviceName string + serviceIndex int ) cmd := &cobra.Command{ @@ -146,7 +148,7 @@ func NewRunTestWorkflowCmd() *cobra.Command { ui.NL() if !execution.FailedToInitialize() { if watchEnabled && len(args) > 0 { - exitCode = uiWatch(execution, client) + exitCode = uiWatch(execution, serviceName, serviceIndex, client) ui.NL() if downloadArtifactsEnabled { tests.DownloadTestWorkflowArtifacts(execution.Id, downloadDir, format, masks, client, outputPretty) @@ -181,12 +183,21 @@ func NewRunTestWorkflowCmd() *cobra.Command { cmd.Flags().StringArrayVarP(&masks, "mask", "", []string{}, "regexp to filter downloaded files, single or comma separated, like report/.* or .*\\.json,.*\\.js$") cmd.Flags().StringToStringVarP(&tags, "tag", "", map[string]string{}, "execution tags in a form of name1=val1 passed to executor") cmd.Flags().StringSliceVarP(&selectors, "label", "l", nil, "label key value pair: --label key1=value1 or label expression") + cmd.Flags().StringVar(&serviceName, "service-name", "", "test workflow service name") + cmd.Flags().IntVar(&serviceIndex, "service-index", 0, "test workflow service index") return cmd } -func uiWatch(execution testkube.TestWorkflowExecution, client apiclientv1.Client) int { - result, err := watchTestWorkflowLogs(execution.Id, execution.Signature, client) +func uiWatch(execution testkube.TestWorkflowExecution, serviceName string, serviceIndex int, client apiclientv1.Client) int { + var result *testkube.TestWorkflowResult + var err error + + if serviceName == "" { + result, err = watchTestWorkflowLogs(execution.Id, execution.Signature, client) + } else { + result, err = watchTestWorkflowServiceLogs(execution.Id, serviceName, serviceIndex, execution.Signature, client) + } ui.ExitOnError("reading test workflow execution logs", err) // Apply the result in the execution @@ -313,6 +324,36 @@ func watchTestWorkflowLogs(id string, signature []testkube.TestWorkflowSignature return result, err } +func watchTestWorkflowServiceLogs(id, serviceName string, serviceIndex int, signature []testkube.TestWorkflowSignature, client apiclientv1.Client) (*testkube.TestWorkflowResult, error) { + ui.Info("Getting logs from test workflow service pod", fmt.Sprintf("%s-%s-%d", id, serviceName, serviceIndex)) + + notifications, err := client.GetTestWorkflowExecutionServiceNotifications(id, serviceName, serviceIndex) + ui.ExitOnError("getting logs from executor", err) + + steps := flattenSignatures(signature) + + var result *testkube.TestWorkflowResult + var isLineBeginning = true + for l := range notifications { + if l.Output != nil { + continue + } + if l.Result != nil { + if printResultDifference(result, l.Result, steps) { + isLineBeginning = true + } + result = l.Result + continue + } + + printStructuredLogLines(l.Log, &isLineBeginning) + } + + ui.NL() + + return result, err +} + func printStatusHeader(i, n int, name string) { if i == -1 { fmt.Println("\n" + ui.LightCyan(fmt.Sprintf("• %s", name))) diff --git a/cmd/kubectl-testkube/commands/testworkflows/watch.go b/cmd/kubectl-testkube/commands/testworkflows/watch.go index 2387810b6e..3992ec3337 100644 --- a/cmd/kubectl-testkube/commands/testworkflows/watch.go +++ b/cmd/kubectl-testkube/commands/testworkflows/watch.go @@ -13,6 +13,11 @@ import ( ) func NewWatchTestWorkflowExecutionCmd() *cobra.Command { + var ( + serviceName string + serviceIndex int + ) + cmd := &cobra.Command{ Use: "testworkflowexecution ", Aliases: []string{"testworkflowexecutions", "twe", "tw"}, @@ -31,7 +36,7 @@ func NewWatchTestWorkflowExecutionCmd() *cobra.Command { ui.ExitOnError("render test workflow execution", err) ui.NL() - exitCode := uiWatch(execution, client) + exitCode := uiWatch(execution, serviceName, serviceIndex, client) ui.NL() execution, err = client.GetTestWorkflowExecution(execution.Id) @@ -43,5 +48,8 @@ func NewWatchTestWorkflowExecutionCmd() *cobra.Command { }, } + cmd.Flags().StringVar(&serviceName, "service-name", "", "test workflow service name") + cmd.Flags().IntVar(&serviceIndex, "service-index", 0, "test workflow service index") + return cmd } diff --git a/internal/app/api/v1/server.go b/internal/app/api/v1/server.go index 7b6890c605..d7aa19993b 100644 --- a/internal/app/api/v1/server.go +++ b/internal/app/api/v1/server.go @@ -150,9 +150,9 @@ func (s *TestkubeAPI) Init(server server.HTTPServer) { testWorkflowExecutions.Post("/", s.ExecuteTestWorkflowHandler()) testWorkflowExecutions.Get("/:executionID", s.GetTestWorkflowExecutionHandler()) testWorkflowExecutions.Get("/:executionID/notifications", s.StreamTestWorkflowExecutionNotificationsHandler()) - testWorkflowExecutions.Get("/:executionID/notifications/:serviceName/:serviceIndex", s.StreamTestWorkflowExecutionServiceNotificationsHandler()) + testWorkflowExecutions.Get("/:executionID/notifications/:serviceName/:serviceIndex", s.StreamTestWorkflowExecutionServiceNotificationsHandler()) testWorkflowExecutions.Get("/:executionID/notifications/stream", s.StreamTestWorkflowExecutionNotificationsWebSocketHandler()) - testWorkflowExecutions.Get("/:executionID/notifications/stream:serviceName/:serviceIndex", s.StreamTestWorkflowExecutionServiceNotificationsWebSocketHandler()) + testWorkflowExecutions.Get("/:executionID/notifications/stream:serviceName/:serviceIndex", s.StreamTestWorkflowExecutionServiceNotificationsWebSocketHandler()) testWorkflowExecutions.Post("/:executionID/abort", s.AbortTestWorkflowExecutionHandler()) testWorkflowExecutions.Post("/:executionID/pause", s.PauseTestWorkflowExecutionHandler()) testWorkflowExecutions.Post("/:executionID/resume", s.ResumeTestWorkflowExecutionHandler()) diff --git a/pkg/api/v1/client/interface.go b/pkg/api/v1/client/interface.go index 82f1357377..73a3f6c48a 100644 --- a/pkg/api/v1/client/interface.go +++ b/pkg/api/v1/client/interface.go @@ -154,6 +154,7 @@ type TestWorkflowAPI interface { ExecuteTestWorkflows(selector string, request testkube.TestWorkflowExecutionRequest) ([]testkube.TestWorkflowExecution, error) GetTestWorkflowExecutionNotifications(id string) (chan testkube.TestWorkflowExecutionNotification, error) GetTestWorkflowExecutionLogs(id string) ([]byte, error) + GetTestWorkflowExecutionServiceNotifications(id, serviceName string, serviceIndex int) (chan testkube.TestWorkflowExecutionNotification, error) } // TestWorkflowExecutionAPI describes test workflow api methods diff --git a/pkg/api/v1/client/testworkflow.go b/pkg/api/v1/client/testworkflow.go index d878f93565..8bffb81d28 100644 --- a/pkg/api/v1/client/testworkflow.go +++ b/pkg/api/v1/client/testworkflow.go @@ -146,6 +146,14 @@ func (c TestWorkflowClient) GetTestWorkflowExecutionNotifications(id string) (no return notifications, err } +// GetTestWorkflowExecutionServiceNotifications returns events stream from job pods, based on job pods logs +func (c TestWorkflowClient) GetTestWorkflowExecutionServiceNotifications(id, serviceName string, serviceIndex int) (notifications chan testkube.TestWorkflowExecutionNotification, err error) { + notifications = make(chan testkube.TestWorkflowExecutionNotification) + uri := c.testWorkflowTransport.GetURI("/test-workflow-executions/%s/notifications/%s/%d", id, serviceName, serviceIndex) + err = c.testWorkflowTransport.GetTestWorkflowExecutionNotifications(uri, notifications) + return notifications, err +} + // GetTestWorkflowExecution returns single test workflow execution by id func (c TestWorkflowClient) GetTestWorkflowExecution(id string) (testkube.TestWorkflowExecution, error) { uri := c.testWorkflowExecutionTransport.GetURI("/test-workflow-executions/%s", id) From c71f5c810dc52e6246d183ad99f64b6046a4653d Mon Sep 17 00:00:00 2001 From: Vladislav Sukhin Date: Mon, 25 Nov 2024 19:26:38 +0300 Subject: [PATCH 19/30] fix: change log Signed-off-by: Vladislav Sukhin --- cmd/kubectl-testkube/commands/testworkflows/executions.go | 2 +- cmd/kubectl-testkube/commands/testworkflows/run.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/kubectl-testkube/commands/testworkflows/executions.go b/cmd/kubectl-testkube/commands/testworkflows/executions.go index 66e297df84..bdd328da32 100644 --- a/cmd/kubectl-testkube/commands/testworkflows/executions.go +++ b/cmd/kubectl-testkube/commands/testworkflows/executions.go @@ -75,7 +75,7 @@ func NewGetTestWorkflowExecutionsCmd() *cobra.Command { ui.Info("Getting logs for test workflow execution", executionID) logs, err := client.GetTestWorkflowExecutionLogs(executionID) - ui.ExitOnError("getting logs from executor", err) + ui.ExitOnError("getting logs from test workflow", err) sigs := flattenSignatures(execution.Signature) diff --git a/cmd/kubectl-testkube/commands/testworkflows/run.go b/cmd/kubectl-testkube/commands/testworkflows/run.go index 573553c4a1..0116fedd83 100644 --- a/cmd/kubectl-testkube/commands/testworkflows/run.go +++ b/cmd/kubectl-testkube/commands/testworkflows/run.go @@ -298,7 +298,7 @@ func watchTestWorkflowLogs(id string, signature []testkube.TestWorkflowSignature ui.Info("Getting logs from test workflow job", id) notifications, err := client.GetTestWorkflowExecutionNotifications(id) - ui.ExitOnError("getting logs from executor", err) + ui.ExitOnError("getting logs from test workflow", err) steps := flattenSignatures(signature) @@ -328,7 +328,7 @@ func watchTestWorkflowServiceLogs(id, serviceName string, serviceIndex int, sign ui.Info("Getting logs from test workflow service pod", fmt.Sprintf("%s-%s-%d", id, serviceName, serviceIndex)) notifications, err := client.GetTestWorkflowExecutionServiceNotifications(id, serviceName, serviceIndex) - ui.ExitOnError("getting logs from executor", err) + ui.ExitOnError("getting logs from service", err) steps := flattenSignatures(signature) From 3e70a83712b5a9bf3fab7ad34a43cf18ccde21d2 Mon Sep 17 00:00:00 2001 From: Vladislav Sukhin Date: Mon, 25 Nov 2024 23:07:35 +0300 Subject: [PATCH 20/30] fix: disable hints Signed-off-by: Vladislav Sukhin --- internal/app/api/v1/testworkflowexecutions.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/app/api/v1/testworkflowexecutions.go b/internal/app/api/v1/testworkflowexecutions.go index 99a4288e90..9e56997857 100644 --- a/internal/app/api/v1/testworkflowexecutions.go +++ b/internal/app/api/v1/testworkflowexecutions.go @@ -107,9 +107,9 @@ func (s *TestkubeAPI) StreamTestWorkflowExecutionServiceNotificationsHandler() f id := fmt.Sprintf("%s-%s-%s", execution.Id, serviceName, serviceIndex) notifications := s.ExecutionWorkerClient.Notifications(ctx, id, executionworkertypes.NotificationsOptions{ Hints: executionworkertypes.Hints{ - Namespace: execution.Namespace, - ScheduledAt: common.Ptr(execution.ScheduledAt), - Signature: execution.Signature, + Namespace: execution.Namespace, + // ScheduledAt: common.Ptr(execution.ScheduledAt), + // Signature: execution.Signature, }, }) if notifications.Err() != nil { @@ -183,9 +183,9 @@ func (s *TestkubeAPI) StreamTestWorkflowExecutionServiceNotificationsWebSocketHa id := fmt.Sprintf("%s-%s-%s", execution.Id, serviceName, serviceIndex) notifications := s.ExecutionWorkerClient.Notifications(ctx, id, executionworkertypes.NotificationsOptions{ Hints: executionworkertypes.Hints{ - Namespace: execution.Namespace, - Signature: execution.Signature, - ScheduledAt: common.Ptr(execution.ScheduledAt), + Namespace: execution.Namespace, + // Signature: execution.Signature, + // ScheduledAt: common.Ptr(execution.ScheduledAt), }, }) if notifications.Err() != nil { From aa9acb3f7918717243b098abdb9eb6b912d640fa Mon Sep 17 00:00:00 2001 From: Vladislav Sukhin Date: Tue, 26 Nov 2024 15:26:59 +0300 Subject: [PATCH 21/30] fix: routing Signed-off-by: Vladislav Sukhin --- internal/app/api/v1/server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/app/api/v1/server.go b/internal/app/api/v1/server.go index d7aa19993b..139f8e9863 100644 --- a/internal/app/api/v1/server.go +++ b/internal/app/api/v1/server.go @@ -150,9 +150,9 @@ func (s *TestkubeAPI) Init(server server.HTTPServer) { testWorkflowExecutions.Post("/", s.ExecuteTestWorkflowHandler()) testWorkflowExecutions.Get("/:executionID", s.GetTestWorkflowExecutionHandler()) testWorkflowExecutions.Get("/:executionID/notifications", s.StreamTestWorkflowExecutionNotificationsHandler()) - testWorkflowExecutions.Get("/:executionID/notifications/:serviceName/:serviceIndex", s.StreamTestWorkflowExecutionServiceNotificationsHandler()) + testWorkflowExecutions.Get("/:executionID/notifications/:serviceName/:serviceIndex", s.StreamTestWorkflowExecutionServiceNotificationsHandler()) testWorkflowExecutions.Get("/:executionID/notifications/stream", s.StreamTestWorkflowExecutionNotificationsWebSocketHandler()) - testWorkflowExecutions.Get("/:executionID/notifications/stream:serviceName/:serviceIndex", s.StreamTestWorkflowExecutionServiceNotificationsWebSocketHandler()) + testWorkflowExecutions.Get("/:executionID/notifications/stream/:serviceName/:serviceIndex", s.StreamTestWorkflowExecutionServiceNotificationsWebSocketHandler()) testWorkflowExecutions.Post("/:executionID/abort", s.AbortTestWorkflowExecutionHandler()) testWorkflowExecutions.Post("/:executionID/pause", s.PauseTestWorkflowExecutionHandler()) testWorkflowExecutions.Post("/:executionID/resume", s.ResumeTestWorkflowExecutionHandler()) From 68fe09fcdbcd900ff2d58d36103afeeadad17903 Mon Sep 17 00:00:00 2001 From: Vladislav Sukhin Date: Tue, 26 Nov 2024 17:08:09 +0300 Subject: [PATCH 22/30] fix: show service logs Signed-off-by: Vladislav Sukhin --- .../commands/testworkflows/run.go | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/cmd/kubectl-testkube/commands/testworkflows/run.go b/cmd/kubectl-testkube/commands/testworkflows/run.go index 0116fedd83..b1fd46adc2 100644 --- a/cmd/kubectl-testkube/commands/testworkflows/run.go +++ b/cmd/kubectl-testkube/commands/testworkflows/run.go @@ -27,8 +27,9 @@ import ( ) const ( - LogTimestampLength = 30 // time.RFC3339Nano without 00:00 timezone - apiErrorMessage = "processing error:" + LogTimestampLength = 30 // time.RFC3339Nano without 00:00 timezone + apiErrorMessage = "processing error:" + serviceLogsCheckDelay = 100 * time.Millisecond ) var ( @@ -298,7 +299,9 @@ func watchTestWorkflowLogs(id string, signature []testkube.TestWorkflowSignature ui.Info("Getting logs from test workflow job", id) notifications, err := client.GetTestWorkflowExecutionNotifications(id) - ui.ExitOnError("getting logs from test workflow", err) + if err != nil { + return nil, err + } steps := flattenSignatures(signature) @@ -321,14 +324,38 @@ func watchTestWorkflowLogs(id string, signature []testkube.TestWorkflowSignature ui.NL() - return result, err + return result, nil } -func watchTestWorkflowServiceLogs(id, serviceName string, serviceIndex int, signature []testkube.TestWorkflowSignature, client apiclientv1.Client) (*testkube.TestWorkflowResult, error) { +func watchTestWorkflowServiceLogs(id, serviceName string, serviceIndex int, + signature []testkube.TestWorkflowSignature, client apiclientv1.Client) (*testkube.TestWorkflowResult, error) { ui.Info("Getting logs from test workflow service pod", fmt.Sprintf("%s-%s-%d", id, serviceName, serviceIndex)) - notifications, err := client.GetTestWorkflowExecutionServiceNotifications(id, serviceName, serviceIndex) - ui.ExitOnError("getting logs from service", err) + var ( + notifications chan testkube.TestWorkflowExecutionNotification + err error + ) + + for { + notifications, err = client.GetTestWorkflowExecutionServiceNotifications(id, serviceName, serviceIndex) + if err != nil { + execution, err := client.GetTestWorkflowExecution(id) + if err != nil { + return nil, err + } + + if execution.Result != nil && !execution.Result.IsFinished() { + time.Sleep(serviceLogsCheckDelay) + continue + } + } + + if err != nil { + return nil, err + } + + break + } steps := flattenSignatures(signature) @@ -351,7 +378,7 @@ func watchTestWorkflowServiceLogs(id, serviceName string, serviceIndex int, sign ui.NL() - return result, err + return result, nil } func printStatusHeader(i, n int, name string) { From c55187f29aa6ae351e85c9db40cacfb2e3030dca Mon Sep 17 00:00:00 2001 From: Vladislav Sukhin Date: Wed, 27 Nov 2024 13:08:27 +0300 Subject: [PATCH 23/30] fix: check service name Signed-off-by: Vladislav Sukhin --- .../commands/testworkflows/run.go | 53 ++++++++----------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/cmd/kubectl-testkube/commands/testworkflows/run.go b/cmd/kubectl-testkube/commands/testworkflows/run.go index b1fd46adc2..663924d2ac 100644 --- a/cmd/kubectl-testkube/commands/testworkflows/run.go +++ b/cmd/kubectl-testkube/commands/testworkflows/run.go @@ -197,6 +197,15 @@ func uiWatch(execution testkube.TestWorkflowExecution, serviceName string, servi if serviceName == "" { result, err = watchTestWorkflowLogs(execution.Id, execution.Signature, client) } else { + found := false + if execution.Workflow != nil && execution.Workflow.Spec != nil { + _, found = execution.Workflow.Spec.Services[serviceName] + } + + if !found { + ui.Failf("unknown service '%s' for test workflow execution %s", serviceName, execution.Id) + } + result, err = watchTestWorkflowServiceLogs(execution.Id, serviceName, serviceIndex, execution.Signature, client) } ui.ExitOnError("reading test workflow execution logs", err) @@ -295,17 +304,10 @@ func getTimestampLength(line string) int { return 0 } -func watchTestWorkflowLogs(id string, signature []testkube.TestWorkflowSignature, client apiclientv1.Client) (*testkube.TestWorkflowResult, error) { - ui.Info("Getting logs from test workflow job", id) - - notifications, err := client.GetTestWorkflowExecutionNotifications(id) - if err != nil { - return nil, err - } - +func printTestWorkflowLogs(signature []testkube.TestWorkflowSignature, + notifications chan testkube.TestWorkflowExecutionNotification) (result *testkube.TestWorkflowResult) { steps := flattenSignatures(signature) - var result *testkube.TestWorkflowResult var isLineBeginning = true for l := range notifications { if l.Output != nil { @@ -323,7 +325,18 @@ func watchTestWorkflowLogs(id string, signature []testkube.TestWorkflowSignature } ui.NL() + return result +} + +func watchTestWorkflowLogs(id string, signature []testkube.TestWorkflowSignature, client apiclientv1.Client) (*testkube.TestWorkflowResult, error) { + ui.Info("Getting logs from test workflow job", id) + + notifications, err := client.GetTestWorkflowExecutionNotifications(id) + if err != nil { + return nil, err + } + result := printTestWorkflowLogs(signature, notifications) return result, nil } @@ -357,27 +370,7 @@ func watchTestWorkflowServiceLogs(id, serviceName string, serviceIndex int, break } - steps := flattenSignatures(signature) - - var result *testkube.TestWorkflowResult - var isLineBeginning = true - for l := range notifications { - if l.Output != nil { - continue - } - if l.Result != nil { - if printResultDifference(result, l.Result, steps) { - isLineBeginning = true - } - result = l.Result - continue - } - - printStructuredLogLines(l.Log, &isLineBeginning) - } - - ui.NL() - + result := printTestWorkflowLogs(signature, notifications) return result, nil } From 93b7ac62ade920d8f1e45f8a78607368e46fd6dd Mon Sep 17 00:00:00 2001 From: Vladislav Sukhin Date: Wed, 27 Nov 2024 13:11:54 +0300 Subject: [PATCH 24/30] fix: check service name Signed-off-by: Vladislav Sukhin --- cmd/kubectl-testkube/commands/testworkflows/run.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cmd/kubectl-testkube/commands/testworkflows/run.go b/cmd/kubectl-testkube/commands/testworkflows/run.go index 663924d2ac..dfd1185230 100644 --- a/cmd/kubectl-testkube/commands/testworkflows/run.go +++ b/cmd/kubectl-testkube/commands/testworkflows/run.go @@ -336,8 +336,7 @@ func watchTestWorkflowLogs(id string, signature []testkube.TestWorkflowSignature return nil, err } - result := printTestWorkflowLogs(signature, notifications) - return result, nil + return printTestWorkflowLogs(signature, notifications), nil } func watchTestWorkflowServiceLogs(id, serviceName string, serviceIndex int, @@ -370,8 +369,7 @@ func watchTestWorkflowServiceLogs(id, serviceName string, serviceIndex int, break } - result := printTestWorkflowLogs(signature, notifications) - return result, nil + return printTestWorkflowLogs(signature, notifications), nil } func printStatusHeader(i, n int, name string) { From cf2dd76d944cdb30426f13ec72c1dc96f37d34d1 Mon Sep 17 00:00:00 2001 From: Vladislav Sukhin Date: Wed, 27 Nov 2024 13:13:20 +0300 Subject: [PATCH 25/30] fix: log comment Signed-off-by: Vladislav Sukhin --- cmd/kubectl-testkube/commands/testworkflows/run.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/kubectl-testkube/commands/testworkflows/run.go b/cmd/kubectl-testkube/commands/testworkflows/run.go index dfd1185230..c5e4c45f70 100644 --- a/cmd/kubectl-testkube/commands/testworkflows/run.go +++ b/cmd/kubectl-testkube/commands/testworkflows/run.go @@ -341,7 +341,7 @@ func watchTestWorkflowLogs(id string, signature []testkube.TestWorkflowSignature func watchTestWorkflowServiceLogs(id, serviceName string, serviceIndex int, signature []testkube.TestWorkflowSignature, client apiclientv1.Client) (*testkube.TestWorkflowResult, error) { - ui.Info("Getting logs from test workflow service pod", fmt.Sprintf("%s-%s-%d", id, serviceName, serviceIndex)) + ui.Info("Getting logs from test workflow service job", fmt.Sprintf("%s-%s-%d", id, serviceName, serviceIndex)) var ( notifications chan testkube.TestWorkflowExecutionNotification From fff63c0cf82d65be0a0bb804ada14000dc54ba22 Mon Sep 17 00:00:00 2001 From: Vladislav Sukhin Date: Wed, 27 Nov 2024 13:27:50 +0300 Subject: [PATCH 26/30] fix: check for testworkflow service Signed-off-by: Vladislav Sukhin --- internal/app/api/v1/server.go | 4 +-- internal/app/api/v1/testworkflowexecutions.go | 28 +++++++++++++++---- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/internal/app/api/v1/server.go b/internal/app/api/v1/server.go index 139f8e9863..816783fee4 100644 --- a/internal/app/api/v1/server.go +++ b/internal/app/api/v1/server.go @@ -150,9 +150,9 @@ func (s *TestkubeAPI) Init(server server.HTTPServer) { testWorkflowExecutions.Post("/", s.ExecuteTestWorkflowHandler()) testWorkflowExecutions.Get("/:executionID", s.GetTestWorkflowExecutionHandler()) testWorkflowExecutions.Get("/:executionID/notifications", s.StreamTestWorkflowExecutionNotificationsHandler()) - testWorkflowExecutions.Get("/:executionID/notifications/:serviceName/:serviceIndex", s.StreamTestWorkflowExecutionServiceNotificationsHandler()) + testWorkflowExecutions.Get("/:executionID/notifications/:serviceName/:serviceIndex", s.StreamTestWorkflowExecutionServiceNotificationsHandler()) testWorkflowExecutions.Get("/:executionID/notifications/stream", s.StreamTestWorkflowExecutionNotificationsWebSocketHandler()) - testWorkflowExecutions.Get("/:executionID/notifications/stream/:serviceName/:serviceIndex", s.StreamTestWorkflowExecutionServiceNotificationsWebSocketHandler()) + testWorkflowExecutions.Get("/:executionID/notifications/stream/:serviceName/:serviceIndex", s.StreamTestWorkflowExecutionServiceNotificationsWebSocketHandler()) testWorkflowExecutions.Post("/:executionID/abort", s.AbortTestWorkflowExecutionHandler()) testWorkflowExecutions.Post("/:executionID/pause", s.PauseTestWorkflowExecutionHandler()) testWorkflowExecutions.Post("/:executionID/resume", s.ResumeTestWorkflowExecutionHandler()) diff --git a/internal/app/api/v1/testworkflowexecutions.go b/internal/app/api/v1/testworkflowexecutions.go index 9e56997857..a806588161 100644 --- a/internal/app/api/v1/testworkflowexecutions.go +++ b/internal/app/api/v1/testworkflowexecutions.go @@ -103,13 +103,21 @@ func (s *TestkubeAPI) StreamTestWorkflowExecutionServiceNotificationsHandler() f return s.ClientError(c, errPrefix, err) } + found := false + if execution.Workflow != nil && execution.Workflow.Spec != nil { + _, found = execution.Workflow.Spec.Services[serviceName] + } + + if !found { + return s.ClientError(c, errPrefix, errors.New("unknown service for test workflow execution")) + } + // Check for the logs id := fmt.Sprintf("%s-%s-%s", execution.Id, serviceName, serviceIndex) notifications := s.ExecutionWorkerClient.Notifications(ctx, id, executionworkertypes.NotificationsOptions{ Hints: executionworkertypes.Hints{ - Namespace: execution.Namespace, - // ScheduledAt: common.Ptr(execution.ScheduledAt), - // Signature: execution.Signature, + Namespace: execution.Namespace, + ScheduledAt: common.Ptr(execution.ScheduledAt), }, }) if notifications.Err() != nil { @@ -179,13 +187,21 @@ func (s *TestkubeAPI) StreamTestWorkflowExecutionServiceNotificationsWebSocketHa return } + found := false + if execution.Workflow != nil && execution.Workflow.Spec != nil { + _, found = execution.Workflow.Spec.Services[serviceName] + } + + if !found { + return + } + // Check for the logs id := fmt.Sprintf("%s-%s-%s", execution.Id, serviceName, serviceIndex) notifications := s.ExecutionWorkerClient.Notifications(ctx, id, executionworkertypes.NotificationsOptions{ Hints: executionworkertypes.Hints{ - Namespace: execution.Namespace, - // Signature: execution.Signature, - // ScheduledAt: common.Ptr(execution.ScheduledAt), + Namespace: execution.Namespace, + ScheduledAt: common.Ptr(execution.ScheduledAt), }, }) if notifications.Err() != nil { From c7bed19ac731bd0f33986f7eee8e93f53f03e155 Mon Sep 17 00:00:00 2001 From: Vladislav Sukhin Date: Wed, 27 Nov 2024 14:44:59 +0300 Subject: [PATCH 27/30] fix: friendly error Signed-off-by: Vladislav Sukhin --- .../commands/testworkflows/run.go | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/cmd/kubectl-testkube/commands/testworkflows/run.go b/cmd/kubectl-testkube/commands/testworkflows/run.go index c5e4c45f70..1e5b30bf00 100644 --- a/cmd/kubectl-testkube/commands/testworkflows/run.go +++ b/cmd/kubectl-testkube/commands/testworkflows/run.go @@ -345,25 +345,29 @@ func watchTestWorkflowServiceLogs(id, serviceName string, serviceIndex int, var ( notifications chan testkube.TestWorkflowExecutionNotification - err error + nErr error ) for { - notifications, err = client.GetTestWorkflowExecutionServiceNotifications(id, serviceName, serviceIndex) - if err != nil { - execution, err := client.GetTestWorkflowExecution(id) - if err != nil { - return nil, err + notifications, nErr = client.GetTestWorkflowExecutionServiceNotifications(id, serviceName, serviceIndex) + if nErr != nil { + execution, cErr := client.GetTestWorkflowExecution(id) + if cErr != nil { + return nil, cErr } - if execution.Result != nil && !execution.Result.IsFinished() { - time.Sleep(serviceLogsCheckDelay) - continue + if execution.Result != nil { + if execution.Result.IsFinished() { + nErr = errors.New("test workflow execution is finished") + } else { + time.Sleep(serviceLogsCheckDelay) + continue + } } } - if err != nil { - return nil, err + if nErr != nil { + return nil, nErr } break From f34e5d1f27a74e1fbc81a1ff34615570b5f05617 Mon Sep 17 00:00:00 2001 From: Vladislav Sukhin Date: Wed, 27 Nov 2024 14:52:58 +0300 Subject: [PATCH 28/30] fix: add spinner Signed-off-by: Vladislav Sukhin --- cmd/kubectl-testkube/commands/testworkflows/run.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/kubectl-testkube/commands/testworkflows/run.go b/cmd/kubectl-testkube/commands/testworkflows/run.go index 1e5b30bf00..cbe7061a6b 100644 --- a/cmd/kubectl-testkube/commands/testworkflows/run.go +++ b/cmd/kubectl-testkube/commands/testworkflows/run.go @@ -348,11 +348,13 @@ func watchTestWorkflowServiceLogs(id, serviceName string, serviceIndex int, nErr error ) + spinner := ui.NewSpinner("Waiting for service logs") for { notifications, nErr = client.GetTestWorkflowExecutionServiceNotifications(id, serviceName, serviceIndex) if nErr != nil { execution, cErr := client.GetTestWorkflowExecution(id) if cErr != nil { + spinner.Fail() return nil, cErr } @@ -367,12 +369,14 @@ func watchTestWorkflowServiceLogs(id, serviceName string, serviceIndex int, } if nErr != nil { + spinner.Fail() return nil, nErr } break } + spinner.Success() return printTestWorkflowLogs(signature, notifications), nil } From 07ce30817b7cfe8eb919afde392f080e18b0b839 Mon Sep 17 00:00:00 2001 From: Vladislav Sukhin Date: Wed, 27 Nov 2024 18:01:35 +0300 Subject: [PATCH 29/30] feat: proto for service notifications Signed-off-by: Vladislav Sukhin --- pkg/cloud/service.pb.go | 463 ++++++++++++++++++++++++++--------- pkg/cloud/service_grpc.pb.go | 68 +++++ proto/service.proto | 18 ++ 3 files changed, 435 insertions(+), 114 deletions(-) diff --git a/pkg/cloud/service.pb.go b/pkg/cloud/service.pb.go index 12eabcdee4..5d4252ebfd 100644 --- a/pkg/cloud/service.pb.go +++ b/pkg/cloud/service.pb.go @@ -1076,6 +1076,172 @@ func (x *CredentialResponse) GetContent() []byte { return nil } +type TestWorkflowServiceNotificationsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + StreamId string `protobuf:"bytes,1,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` + ExecutionId string `protobuf:"bytes,2,opt,name=execution_id,json=executionId,proto3" json:"execution_id,omitempty"` + ServiceName string `protobuf:"bytes,3,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"` + ServiceIndex int32 `protobuf:"varint,4,opt,name=service_index,json=serviceIndex,proto3" json:"service_index,omitempty"` + RequestType TestWorkflowNotificationsRequestType `protobuf:"varint,5,opt,name=request_type,json=requestType,proto3,enum=cloud.TestWorkflowNotificationsRequestType" json:"request_type,omitempty"` +} + +func (x *TestWorkflowServiceNotificationsRequest) Reset() { + *x = TestWorkflowServiceNotificationsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_service_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TestWorkflowServiceNotificationsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TestWorkflowServiceNotificationsRequest) ProtoMessage() {} + +func (x *TestWorkflowServiceNotificationsRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_service_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TestWorkflowServiceNotificationsRequest.ProtoReflect.Descriptor instead. +func (*TestWorkflowServiceNotificationsRequest) Descriptor() ([]byte, []int) { + return file_proto_service_proto_rawDescGZIP(), []int{14} +} + +func (x *TestWorkflowServiceNotificationsRequest) GetStreamId() string { + if x != nil { + return x.StreamId + } + return "" +} + +func (x *TestWorkflowServiceNotificationsRequest) GetExecutionId() string { + if x != nil { + return x.ExecutionId + } + return "" +} + +func (x *TestWorkflowServiceNotificationsRequest) GetServiceName() string { + if x != nil { + return x.ServiceName + } + return "" +} + +func (x *TestWorkflowServiceNotificationsRequest) GetServiceIndex() int32 { + if x != nil { + return x.ServiceIndex + } + return 0 +} + +func (x *TestWorkflowServiceNotificationsRequest) GetRequestType() TestWorkflowNotificationsRequestType { + if x != nil { + return x.RequestType + } + return TestWorkflowNotificationsRequestType_WORKFLOW_STREAM_LOG_MESSAGE +} + +type TestWorkflowServiceNotificationsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + StreamId string `protobuf:"bytes,1,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` + SeqNo uint32 `protobuf:"varint,2,opt,name=seq_no,json=seqNo,proto3" json:"seq_no,omitempty"` + Timestamp string `protobuf:"bytes,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Ref string `protobuf:"bytes,4,opt,name=ref,proto3" json:"ref,omitempty"` + Type TestWorkflowNotificationType `protobuf:"varint,5,opt,name=type,proto3,enum=cloud.TestWorkflowNotificationType" json:"type,omitempty"` + Message string `protobuf:"bytes,6,opt,name=message,proto3" json:"message,omitempty"` // based on type: log/error = inline, others = serialized to JSON +} + +func (x *TestWorkflowServiceNotificationsResponse) Reset() { + *x = TestWorkflowServiceNotificationsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_service_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TestWorkflowServiceNotificationsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TestWorkflowServiceNotificationsResponse) ProtoMessage() {} + +func (x *TestWorkflowServiceNotificationsResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_service_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TestWorkflowServiceNotificationsResponse.ProtoReflect.Descriptor instead. +func (*TestWorkflowServiceNotificationsResponse) Descriptor() ([]byte, []int) { + return file_proto_service_proto_rawDescGZIP(), []int{15} +} + +func (x *TestWorkflowServiceNotificationsResponse) GetStreamId() string { + if x != nil { + return x.StreamId + } + return "" +} + +func (x *TestWorkflowServiceNotificationsResponse) GetSeqNo() uint32 { + if x != nil { + return x.SeqNo + } + return 0 +} + +func (x *TestWorkflowServiceNotificationsResponse) GetTimestamp() string { + if x != nil { + return x.Timestamp + } + return "" +} + +func (x *TestWorkflowServiceNotificationsResponse) GetRef() string { + if x != nil { + return x.Ref + } + return "" +} + +func (x *TestWorkflowServiceNotificationsResponse) GetType() TestWorkflowNotificationType { + if x != nil { + return x.Type + } + return TestWorkflowNotificationType_WORKFLOW_STREAM_ERROR +} + +func (x *TestWorkflowServiceNotificationsResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + var File_proto_service_proto protoreflect.FileDescriptor var file_proto_service_proto_rawDesc = []byte{ @@ -1193,71 +1359,110 @@ var file_proto_service_proto_rawDesc = []byte{ 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x2e, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2a, 0x48, 0x0a, 0x15, 0x4c, 0x6f, 0x67, 0x73, 0x53, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x16, 0x0a, 0x12, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x4c, 0x4f, 0x47, 0x5f, 0x4d, - 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x53, 0x54, 0x52, 0x45, - 0x41, 0x4d, 0x5f, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, - 0x01, 0x2a, 0x69, 0x0a, 0x24, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, - 0x77, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x57, 0x4f, 0x52, - 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x4c, 0x4f, 0x47, - 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x00, 0x12, 0x20, 0x0a, 0x1c, 0x57, 0x4f, - 0x52, 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x48, 0x45, - 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x01, 0x2a, 0x8a, 0x01, 0x0a, - 0x1c, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, - 0x15, 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, - 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x57, 0x4f, 0x52, 0x4b, - 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x4c, 0x4f, 0x47, 0x10, - 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, - 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x10, 0x02, 0x12, 0x1a, 0x0a, - 0x16, 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, - 0x5f, 0x4f, 0x55, 0x54, 0x50, 0x55, 0x54, 0x10, 0x03, 0x2a, 0x4c, 0x0a, 0x06, 0x4f, 0x70, 0x63, - 0x6f, 0x64, 0x65, 0x12, 0x0e, 0x0a, 0x0a, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x46, 0x49, 0x45, - 0x44, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x45, 0x58, 0x54, 0x5f, 0x46, 0x52, 0x41, 0x4d, - 0x45, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x42, 0x49, 0x4e, 0x41, 0x52, 0x59, 0x5f, 0x46, 0x52, - 0x41, 0x4d, 0x45, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, - 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x03, 0x32, 0xd3, 0x04, 0x0a, 0x10, 0x54, 0x65, 0x73, 0x74, - 0x4b, 0x75, 0x62, 0x65, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x41, 0x50, 0x49, 0x12, 0x3c, 0x0a, 0x07, - 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x16, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, - 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, - 0x15, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x36, 0x0a, 0x04, 0x53, 0x65, - 0x6e, 0x64, 0x12, 0x14, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, - 0x63, 0x6b, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x28, 0x01, 0x12, 0x35, 0x0a, 0x04, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x15, 0x2e, 0x63, 0x6c, 0x6f, - 0x75, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x16, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x45, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x65, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x16, 0x2e, 0x63, 0x6c, 0x6f, 0x75, - 0x64, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x1a, 0x15, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x48, 0x0a, 0x0d, - 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x19, 0x2e, - 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x4c, 0x6f, 0x67, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x18, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, - 0x2e, 0x4c, 0x6f, 0x67, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x7b, 0x0a, 0x22, 0x47, 0x65, 0x74, 0x54, 0x65, 0x73, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x81, 0x02, 0x0a, 0x27, 0x54, 0x65, 0x73, 0x74, + 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, + 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x64, + 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x4e, 0x0a, 0x0c, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x2b, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0xe1, 0x01, 0x0a, 0x28, + 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x65, 0x71, 0x5f, 0x6e, 0x6f, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x65, 0x71, 0x4e, 0x6f, 0x12, 0x1c, 0x0a, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, + 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12, 0x37, 0x0a, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x63, 0x6c, 0x6f, + 0x75, 0x64, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4e, + 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2a, + 0x48, 0x0a, 0x15, 0x4c, 0x6f, 0x67, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x54, 0x52, 0x45, + 0x41, 0x4d, 0x5f, 0x4c, 0x4f, 0x47, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x00, + 0x12, 0x17, 0x0a, 0x13, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x48, 0x45, 0x41, 0x4c, 0x54, + 0x48, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x01, 0x2a, 0x69, 0x0a, 0x24, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x28, 0x2e, 0x63, - 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, - 0x77, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x27, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x54, - 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, - 0x01, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x43, 0x6f, 0x6e, - 0x74, 0x65, 0x78, 0x74, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x63, - 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x72, - 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x18, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, - 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0b, 0x5a, - 0x09, 0x70, 0x6b, 0x67, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, + 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x4c, 0x4f, 0x47, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, + 0x10, 0x00, 0x12, 0x20, 0x0a, 0x1c, 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, + 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x43, 0x48, 0x45, + 0x43, 0x4b, 0x10, 0x01, 0x2a, 0x8a, 0x01, 0x0a, 0x1c, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, + 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x15, 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, + 0x57, 0x5f, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x00, + 0x12, 0x17, 0x0a, 0x13, 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, 0x52, + 0x45, 0x41, 0x4d, 0x5f, 0x4c, 0x4f, 0x47, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x57, 0x4f, 0x52, + 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x52, 0x45, 0x53, + 0x55, 0x4c, 0x54, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, + 0x57, 0x5f, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x4f, 0x55, 0x54, 0x50, 0x55, 0x54, 0x10, + 0x03, 0x2a, 0x4c, 0x0a, 0x06, 0x4f, 0x70, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x0e, 0x0a, 0x0a, 0x55, + 0x4e, 0x53, 0x50, 0x45, 0x43, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x54, + 0x45, 0x58, 0x54, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x42, + 0x49, 0x4e, 0x41, 0x52, 0x59, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x10, 0x02, 0x12, 0x10, 0x0a, + 0x0c, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x03, 0x32, + 0xe6, 0x05, 0x0a, 0x10, 0x54, 0x65, 0x73, 0x74, 0x4b, 0x75, 0x62, 0x65, 0x43, 0x6c, 0x6f, 0x75, + 0x64, 0x41, 0x50, 0x49, 0x12, 0x3c, 0x0a, 0x07, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, + 0x16, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x15, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, + 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, + 0x30, 0x01, 0x12, 0x36, 0x0a, 0x04, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x14, 0x2e, 0x63, 0x6c, 0x6f, + 0x75, 0x64, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, + 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, 0x01, 0x12, 0x35, 0x0a, 0x04, 0x43, 0x61, + 0x6c, 0x6c, 0x12, 0x15, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, + 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x63, 0x6c, 0x6f, 0x75, + 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x41, 0x73, 0x79, 0x6e, + 0x63, 0x12, 0x16, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x15, 0x2e, 0x63, 0x6c, 0x6f, 0x75, + 0x64, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x28, 0x01, 0x30, 0x01, 0x12, 0x48, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x19, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x4c, 0x6f, + 0x67, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x1a, 0x18, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x4c, 0x6f, 0x67, 0x73, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x7b, + 0x0a, 0x22, 0x47, 0x65, 0x74, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, + 0x77, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x12, 0x28, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x54, 0x65, 0x73, + 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x27, + 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, + 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x90, 0x01, 0x0a, 0x29, + 0x47, 0x65, 0x74, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x2f, 0x2e, 0x63, 0x6c, 0x6f, 0x75, + 0x64, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x2e, 0x2e, 0x63, 0x6c, 0x6f, + 0x75, 0x64, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x42, + 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, + 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, + 0x50, 0x72, 0x6f, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x12, 0x18, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x43, 0x72, 0x65, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, + 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0b, 0x5a, 0x09, 0x70, 0x6b, 0x67, 0x2f, + 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1273,63 +1478,69 @@ func file_proto_service_proto_rawDescGZIP() []byte { } var file_proto_service_proto_enumTypes = make([]protoimpl.EnumInfo, 4) -var file_proto_service_proto_msgTypes = make([]protoimpl.MessageInfo, 16) +var file_proto_service_proto_msgTypes = make([]protoimpl.MessageInfo, 18) var file_proto_service_proto_goTypes = []interface{}{ - (LogsStreamRequestType)(0), // 0: cloud.LogsStreamRequestType - (TestWorkflowNotificationsRequestType)(0), // 1: cloud.TestWorkflowNotificationsRequestType - (TestWorkflowNotificationType)(0), // 2: cloud.TestWorkflowNotificationType - (Opcode)(0), // 3: cloud.Opcode - (*LogsStreamRequest)(nil), // 4: cloud.LogsStreamRequest - (*LogsStreamResponse)(nil), // 5: cloud.LogsStreamResponse - (*CommandRequest)(nil), // 6: cloud.CommandRequest - (*CommandResponse)(nil), // 7: cloud.CommandResponse - (*ExecuteRequest)(nil), // 8: cloud.ExecuteRequest - (*TestWorkflowNotificationsRequest)(nil), // 9: cloud.TestWorkflowNotificationsRequest - (*TestWorkflowNotificationsResponse)(nil), // 10: cloud.TestWorkflowNotificationsResponse - (*ProContextResponse)(nil), // 11: cloud.ProContextResponse - (*Capability)(nil), // 12: cloud.Capability - (*HeaderValue)(nil), // 13: cloud.HeaderValue - (*ExecuteResponse)(nil), // 14: cloud.ExecuteResponse - (*WebsocketData)(nil), // 15: cloud.WebsocketData - (*CredentialRequest)(nil), // 16: cloud.CredentialRequest - (*CredentialResponse)(nil), // 17: cloud.CredentialResponse - nil, // 18: cloud.ExecuteRequest.HeadersEntry - nil, // 19: cloud.ExecuteResponse.HeadersEntry - (*structpb.Struct)(nil), // 20: google.protobuf.Struct - (*emptypb.Empty)(nil), // 21: google.protobuf.Empty + (LogsStreamRequestType)(0), // 0: cloud.LogsStreamRequestType + (TestWorkflowNotificationsRequestType)(0), // 1: cloud.TestWorkflowNotificationsRequestType + (TestWorkflowNotificationType)(0), // 2: cloud.TestWorkflowNotificationType + (Opcode)(0), // 3: cloud.Opcode + (*LogsStreamRequest)(nil), // 4: cloud.LogsStreamRequest + (*LogsStreamResponse)(nil), // 5: cloud.LogsStreamResponse + (*CommandRequest)(nil), // 6: cloud.CommandRequest + (*CommandResponse)(nil), // 7: cloud.CommandResponse + (*ExecuteRequest)(nil), // 8: cloud.ExecuteRequest + (*TestWorkflowNotificationsRequest)(nil), // 9: cloud.TestWorkflowNotificationsRequest + (*TestWorkflowNotificationsResponse)(nil), // 10: cloud.TestWorkflowNotificationsResponse + (*ProContextResponse)(nil), // 11: cloud.ProContextResponse + (*Capability)(nil), // 12: cloud.Capability + (*HeaderValue)(nil), // 13: cloud.HeaderValue + (*ExecuteResponse)(nil), // 14: cloud.ExecuteResponse + (*WebsocketData)(nil), // 15: cloud.WebsocketData + (*CredentialRequest)(nil), // 16: cloud.CredentialRequest + (*CredentialResponse)(nil), // 17: cloud.CredentialResponse + (*TestWorkflowServiceNotificationsRequest)(nil), // 18: cloud.TestWorkflowServiceNotificationsRequest + (*TestWorkflowServiceNotificationsResponse)(nil), // 19: cloud.TestWorkflowServiceNotificationsResponse + nil, // 20: cloud.ExecuteRequest.HeadersEntry + nil, // 21: cloud.ExecuteResponse.HeadersEntry + (*structpb.Struct)(nil), // 22: google.protobuf.Struct + (*emptypb.Empty)(nil), // 23: google.protobuf.Empty } var file_proto_service_proto_depIdxs = []int32{ 0, // 0: cloud.LogsStreamRequest.request_type:type_name -> cloud.LogsStreamRequestType - 20, // 1: cloud.CommandRequest.payload:type_name -> google.protobuf.Struct - 18, // 2: cloud.ExecuteRequest.headers:type_name -> cloud.ExecuteRequest.HeadersEntry + 22, // 1: cloud.CommandRequest.payload:type_name -> google.protobuf.Struct + 20, // 2: cloud.ExecuteRequest.headers:type_name -> cloud.ExecuteRequest.HeadersEntry 1, // 3: cloud.TestWorkflowNotificationsRequest.request_type:type_name -> cloud.TestWorkflowNotificationsRequestType 2, // 4: cloud.TestWorkflowNotificationsResponse.type:type_name -> cloud.TestWorkflowNotificationType 12, // 5: cloud.ProContextResponse.capabilities:type_name -> cloud.Capability - 19, // 6: cloud.ExecuteResponse.headers:type_name -> cloud.ExecuteResponse.HeadersEntry + 21, // 6: cloud.ExecuteResponse.headers:type_name -> cloud.ExecuteResponse.HeadersEntry 3, // 7: cloud.WebsocketData.opcode:type_name -> cloud.Opcode - 13, // 8: cloud.ExecuteRequest.HeadersEntry.value:type_name -> cloud.HeaderValue - 13, // 9: cloud.ExecuteResponse.HeadersEntry.value:type_name -> cloud.HeaderValue - 14, // 10: cloud.TestKubeCloudAPI.Execute:input_type -> cloud.ExecuteResponse - 15, // 11: cloud.TestKubeCloudAPI.Send:input_type -> cloud.WebsocketData - 6, // 12: cloud.TestKubeCloudAPI.Call:input_type -> cloud.CommandRequest - 14, // 13: cloud.TestKubeCloudAPI.ExecuteAsync:input_type -> cloud.ExecuteResponse - 5, // 14: cloud.TestKubeCloudAPI.GetLogsStream:input_type -> cloud.LogsStreamResponse - 10, // 15: cloud.TestKubeCloudAPI.GetTestWorkflowNotificationsStream:input_type -> cloud.TestWorkflowNotificationsResponse - 21, // 16: cloud.TestKubeCloudAPI.GetProContext:input_type -> google.protobuf.Empty - 16, // 17: cloud.TestKubeCloudAPI.GetCredential:input_type -> cloud.CredentialRequest - 8, // 18: cloud.TestKubeCloudAPI.Execute:output_type -> cloud.ExecuteRequest - 21, // 19: cloud.TestKubeCloudAPI.Send:output_type -> google.protobuf.Empty - 7, // 20: cloud.TestKubeCloudAPI.Call:output_type -> cloud.CommandResponse - 8, // 21: cloud.TestKubeCloudAPI.ExecuteAsync:output_type -> cloud.ExecuteRequest - 4, // 22: cloud.TestKubeCloudAPI.GetLogsStream:output_type -> cloud.LogsStreamRequest - 9, // 23: cloud.TestKubeCloudAPI.GetTestWorkflowNotificationsStream:output_type -> cloud.TestWorkflowNotificationsRequest - 11, // 24: cloud.TestKubeCloudAPI.GetProContext:output_type -> cloud.ProContextResponse - 17, // 25: cloud.TestKubeCloudAPI.GetCredential:output_type -> cloud.CredentialResponse - 18, // [18:26] is the sub-list for method output_type - 10, // [10:18] is the sub-list for method input_type - 10, // [10:10] is the sub-list for extension type_name - 10, // [10:10] is the sub-list for extension extendee - 0, // [0:10] is the sub-list for field type_name + 1, // 8: cloud.TestWorkflowServiceNotificationsRequest.request_type:type_name -> cloud.TestWorkflowNotificationsRequestType + 2, // 9: cloud.TestWorkflowServiceNotificationsResponse.type:type_name -> cloud.TestWorkflowNotificationType + 13, // 10: cloud.ExecuteRequest.HeadersEntry.value:type_name -> cloud.HeaderValue + 13, // 11: cloud.ExecuteResponse.HeadersEntry.value:type_name -> cloud.HeaderValue + 14, // 12: cloud.TestKubeCloudAPI.Execute:input_type -> cloud.ExecuteResponse + 15, // 13: cloud.TestKubeCloudAPI.Send:input_type -> cloud.WebsocketData + 6, // 14: cloud.TestKubeCloudAPI.Call:input_type -> cloud.CommandRequest + 14, // 15: cloud.TestKubeCloudAPI.ExecuteAsync:input_type -> cloud.ExecuteResponse + 5, // 16: cloud.TestKubeCloudAPI.GetLogsStream:input_type -> cloud.LogsStreamResponse + 10, // 17: cloud.TestKubeCloudAPI.GetTestWorkflowNotificationsStream:input_type -> cloud.TestWorkflowNotificationsResponse + 19, // 18: cloud.TestKubeCloudAPI.GetTestWorkflowServiceNotificationsStream:input_type -> cloud.TestWorkflowServiceNotificationsResponse + 23, // 19: cloud.TestKubeCloudAPI.GetProContext:input_type -> google.protobuf.Empty + 16, // 20: cloud.TestKubeCloudAPI.GetCredential:input_type -> cloud.CredentialRequest + 8, // 21: cloud.TestKubeCloudAPI.Execute:output_type -> cloud.ExecuteRequest + 23, // 22: cloud.TestKubeCloudAPI.Send:output_type -> google.protobuf.Empty + 7, // 23: cloud.TestKubeCloudAPI.Call:output_type -> cloud.CommandResponse + 8, // 24: cloud.TestKubeCloudAPI.ExecuteAsync:output_type -> cloud.ExecuteRequest + 4, // 25: cloud.TestKubeCloudAPI.GetLogsStream:output_type -> cloud.LogsStreamRequest + 9, // 26: cloud.TestKubeCloudAPI.GetTestWorkflowNotificationsStream:output_type -> cloud.TestWorkflowNotificationsRequest + 18, // 27: cloud.TestKubeCloudAPI.GetTestWorkflowServiceNotificationsStream:output_type -> cloud.TestWorkflowServiceNotificationsRequest + 11, // 28: cloud.TestKubeCloudAPI.GetProContext:output_type -> cloud.ProContextResponse + 17, // 29: cloud.TestKubeCloudAPI.GetCredential:output_type -> cloud.CredentialResponse + 21, // [21:30] is the sub-list for method output_type + 12, // [12:21] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name } func init() { file_proto_service_proto_init() } @@ -1506,6 +1717,30 @@ func file_proto_service_proto_init() { return nil } } + file_proto_service_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TestWorkflowServiceNotificationsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_service_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TestWorkflowServiceNotificationsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -1513,7 +1748,7 @@ func file_proto_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_service_proto_rawDesc, NumEnums: 4, - NumMessages: 16, + NumMessages: 18, NumExtensions: 0, NumServices: 1, }, diff --git a/pkg/cloud/service_grpc.pb.go b/pkg/cloud/service_grpc.pb.go index 5b329874fe..01464de17a 100644 --- a/pkg/cloud/service_grpc.pb.go +++ b/pkg/cloud/service_grpc.pb.go @@ -31,6 +31,7 @@ type TestKubeCloudAPIClient interface { ExecuteAsync(ctx context.Context, opts ...grpc.CallOption) (TestKubeCloudAPI_ExecuteAsyncClient, error) GetLogsStream(ctx context.Context, opts ...grpc.CallOption) (TestKubeCloudAPI_GetLogsStreamClient, error) GetTestWorkflowNotificationsStream(ctx context.Context, opts ...grpc.CallOption) (TestKubeCloudAPI_GetTestWorkflowNotificationsStreamClient, error) + GetTestWorkflowServiceNotificationsStream(ctx context.Context, opts ...grpc.CallOption) (TestKubeCloudAPI_GetTestWorkflowServiceNotificationsStreamClient, error) GetProContext(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ProContextResponse, error) GetCredential(ctx context.Context, in *CredentialRequest, opts ...grpc.CallOption) (*CredentialResponse, error) } @@ -210,6 +211,37 @@ func (x *testKubeCloudAPIGetTestWorkflowNotificationsStreamClient) Recv() (*Test return m, nil } +func (c *testKubeCloudAPIClient) GetTestWorkflowServiceNotificationsStream(ctx context.Context, opts ...grpc.CallOption) (TestKubeCloudAPI_GetTestWorkflowServiceNotificationsStreamClient, error) { + stream, err := c.cc.NewStream(ctx, &TestKubeCloudAPI_ServiceDesc.Streams[5], "/cloud.TestKubeCloudAPI/GetTestWorkflowServiceNotificationsStream", opts...) + if err != nil { + return nil, err + } + x := &testKubeCloudAPIGetTestWorkflowServiceNotificationsStreamClient{stream} + return x, nil +} + +type TestKubeCloudAPI_GetTestWorkflowServiceNotificationsStreamClient interface { + Send(*TestWorkflowServiceNotificationsResponse) error + Recv() (*TestWorkflowServiceNotificationsRequest, error) + grpc.ClientStream +} + +type testKubeCloudAPIGetTestWorkflowServiceNotificationsStreamClient struct { + grpc.ClientStream +} + +func (x *testKubeCloudAPIGetTestWorkflowServiceNotificationsStreamClient) Send(m *TestWorkflowServiceNotificationsResponse) error { + return x.ClientStream.SendMsg(m) +} + +func (x *testKubeCloudAPIGetTestWorkflowServiceNotificationsStreamClient) Recv() (*TestWorkflowServiceNotificationsRequest, error) { + m := new(TestWorkflowServiceNotificationsRequest) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + func (c *testKubeCloudAPIClient) GetProContext(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ProContextResponse, error) { out := new(ProContextResponse) err := c.cc.Invoke(ctx, "/cloud.TestKubeCloudAPI/GetProContext", in, out, opts...) @@ -240,6 +272,7 @@ type TestKubeCloudAPIServer interface { ExecuteAsync(TestKubeCloudAPI_ExecuteAsyncServer) error GetLogsStream(TestKubeCloudAPI_GetLogsStreamServer) error GetTestWorkflowNotificationsStream(TestKubeCloudAPI_GetTestWorkflowNotificationsStreamServer) error + GetTestWorkflowServiceNotificationsStream(TestKubeCloudAPI_GetTestWorkflowServiceNotificationsStreamServer) error GetProContext(context.Context, *emptypb.Empty) (*ProContextResponse, error) GetCredential(context.Context, *CredentialRequest) (*CredentialResponse, error) mustEmbedUnimplementedTestKubeCloudAPIServer() @@ -267,6 +300,9 @@ func (UnimplementedTestKubeCloudAPIServer) GetLogsStream(TestKubeCloudAPI_GetLog func (UnimplementedTestKubeCloudAPIServer) GetTestWorkflowNotificationsStream(TestKubeCloudAPI_GetTestWorkflowNotificationsStreamServer) error { return status.Errorf(codes.Unimplemented, "method GetTestWorkflowNotificationsStream not implemented") } +func (UnimplementedTestKubeCloudAPIServer) GetTestWorkflowServiceNotificationsStream(TestKubeCloudAPI_GetTestWorkflowServiceNotificationsStreamServer) error { + return status.Errorf(codes.Unimplemented, "method GetTestWorkflowServiceNotificationsStream not implemented") +} func (UnimplementedTestKubeCloudAPIServer) GetProContext(context.Context, *emptypb.Empty) (*ProContextResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetProContext not implemented") } @@ -434,6 +470,32 @@ func (x *testKubeCloudAPIGetTestWorkflowNotificationsStreamServer) Recv() (*Test return m, nil } +func _TestKubeCloudAPI_GetTestWorkflowServiceNotificationsStream_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(TestKubeCloudAPIServer).GetTestWorkflowServiceNotificationsStream(&testKubeCloudAPIGetTestWorkflowServiceNotificationsStreamServer{stream}) +} + +type TestKubeCloudAPI_GetTestWorkflowServiceNotificationsStreamServer interface { + Send(*TestWorkflowServiceNotificationsRequest) error + Recv() (*TestWorkflowServiceNotificationsResponse, error) + grpc.ServerStream +} + +type testKubeCloudAPIGetTestWorkflowServiceNotificationsStreamServer struct { + grpc.ServerStream +} + +func (x *testKubeCloudAPIGetTestWorkflowServiceNotificationsStreamServer) Send(m *TestWorkflowServiceNotificationsRequest) error { + return x.ServerStream.SendMsg(m) +} + +func (x *testKubeCloudAPIGetTestWorkflowServiceNotificationsStreamServer) Recv() (*TestWorkflowServiceNotificationsResponse, error) { + m := new(TestWorkflowServiceNotificationsResponse) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + func _TestKubeCloudAPI_GetProContext_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { @@ -520,6 +582,12 @@ var TestKubeCloudAPI_ServiceDesc = grpc.ServiceDesc{ ServerStreams: true, ClientStreams: true, }, + { + StreamName: "GetTestWorkflowServiceNotificationsStream", + Handler: _TestKubeCloudAPI_GetTestWorkflowServiceNotificationsStream_Handler, + ServerStreams: true, + ClientStreams: true, + }, }, Metadata: "proto/service.proto", } diff --git a/proto/service.proto b/proto/service.proto index 7ed7f0db95..9d56e88d6c 100644 --- a/proto/service.proto +++ b/proto/service.proto @@ -16,6 +16,7 @@ service TestKubeCloudAPI { rpc ExecuteAsync(stream ExecuteResponse) returns (stream ExecuteRequest); rpc GetLogsStream(stream LogsStreamResponse) returns (stream LogsStreamRequest); rpc GetTestWorkflowNotificationsStream(stream TestWorkflowNotificationsResponse) returns (stream TestWorkflowNotificationsRequest); + rpc GetTestWorkflowServiceNotificationsStream(stream TestWorkflowServiceNotificationsResponse) returns (stream TestWorkflowServiceNotificationsRequest); rpc GetProContext(google.protobuf.Empty) returns (ProContextResponse); rpc GetCredential(CredentialRequest) returns (CredentialResponse); } @@ -124,3 +125,20 @@ message CredentialRequest { message CredentialResponse { bytes content = 1; } + +message TestWorkflowServiceNotificationsRequest { + string stream_id = 1; + string execution_id = 2; + string service_name = 3; + int32 service_index = 4; + TestWorkflowNotificationsRequestType request_type = 5; +} + +message TestWorkflowServiceNotificationsResponse { + string stream_id = 1; + uint32 seq_no = 2; + string timestamp = 3; + string ref = 4; + TestWorkflowNotificationType type = 5; + string message = 6; // based on type: log/error = inline, others = serialized to JSON +} From 9c03fe4c1a54ede2d11bb573aa6b49ca567aea64 Mon Sep 17 00:00:00 2001 From: Vladislav Sukhin Date: Wed, 27 Nov 2024 20:56:29 +0300 Subject: [PATCH 30/30] feat: add cloud grpc method for server notifications Signed-off-by: Vladislav Sukhin --- cmd/api-server/commons/commons.go | 25 ++-- cmd/api-server/main.go | 17 +++ internal/config/config.go | 85 +++++++------- internal/config/procontext.go | 25 ++-- pkg/agent/agent.go | 28 ++++- pkg/agent/agent_test.go | 9 +- pkg/agent/events_test.go | 9 +- pkg/agent/logs_test.go | 8 +- pkg/agent/testworkflows.go | 187 ++++++++++++++++++++++++++++++ 9 files changed, 319 insertions(+), 74 deletions(-) diff --git a/cmd/api-server/commons/commons.go b/cmd/api-server/commons/commons.go index 2754634026..b689d51935 100644 --- a/cmd/api-server/commons/commons.go +++ b/cmd/api-server/commons/commons.go @@ -282,18 +282,19 @@ func ReadDefaultExecutors(cfg *config.Config) (executors []testkube.ExecutorDeta func ReadProContext(ctx context.Context, cfg *config.Config, grpcClient cloud.TestKubeCloudAPIClient) config.ProContext { proContext := config.ProContext{ - APIKey: cfg.TestkubeProAPIKey, - URL: cfg.TestkubeProURL, - TLSInsecure: cfg.TestkubeProTLSInsecure, - WorkerCount: cfg.TestkubeProWorkerCount, - LogStreamWorkerCount: cfg.TestkubeProLogStreamWorkerCount, - WorkflowNotificationsWorkerCount: cfg.TestkubeProWorkflowNotificationsWorkerCount, - SkipVerify: cfg.TestkubeProSkipVerify, - EnvID: cfg.TestkubeProEnvID, - OrgID: cfg.TestkubeProOrgID, - Migrate: cfg.TestkubeProMigrate, - ConnectionTimeout: cfg.TestkubeProConnectionTimeout, - DashboardURI: cfg.TestkubeDashboardURI, + APIKey: cfg.TestkubeProAPIKey, + URL: cfg.TestkubeProURL, + TLSInsecure: cfg.TestkubeProTLSInsecure, + WorkerCount: cfg.TestkubeProWorkerCount, + LogStreamWorkerCount: cfg.TestkubeProLogStreamWorkerCount, + WorkflowNotificationsWorkerCount: cfg.TestkubeProWorkflowNotificationsWorkerCount, + WorkflowServiceNotificationsWorkerCount: cfg.TestkubeProWorkflowServiceNotificationsWorkerCount, + SkipVerify: cfg.TestkubeProSkipVerify, + EnvID: cfg.TestkubeProEnvID, + OrgID: cfg.TestkubeProOrgID, + Migrate: cfg.TestkubeProMigrate, + ConnectionTimeout: cfg.TestkubeProConnectionTimeout, + DashboardURI: cfg.TestkubeDashboardURI, } if cfg.TestkubeProAPIKey == "" || grpcClient == nil { diff --git a/cmd/api-server/main.go b/cmd/api-server/main.go index f3bcd976f2..8a349b80f5 100644 --- a/cmd/api-server/main.go +++ b/cmd/api-server/main.go @@ -325,6 +325,22 @@ func main() { } return notifications.Channel(), nil } + getTestWorkflowServiceNotificationsStream := func(ctx context.Context, executionID, serviceName string, serviceIndex int) (<-chan testkube.TestWorkflowExecutionNotification, error) { + execution, err := testWorkflowResultsRepository.Get(ctx, executionID) + if err != nil { + return nil, err + } + notifications := executionWorker.Notifications(ctx, fmt.Sprintf("%s-%s-%d", execution.Id, serviceName, serviceIndex), executionworkertypes.NotificationsOptions{ + Hints: executionworkertypes.Hints{ + Namespace: execution.Namespace, + ScheduledAt: common.Ptr(execution.ScheduledAt), + }, + }) + if notifications.Err() != nil { + return nil, notifications.Err() + } + return notifications.Channel(), nil + } getDeprecatedLogStream := func(ctx context.Context, executionID string) (chan output.Output, error) { return nil, errors.New("deprecated features have been disabled") } @@ -337,6 +353,7 @@ func main() { grpcClient, getDeprecatedLogStream, getTestWorkflowNotificationsStream, + getTestWorkflowServiceNotificationsStream, clusterId, cfg.TestkubeClusterName, features, diff --git a/internal/config/config.go b/internal/config/config.go index 9c2d6fc83f..0394c8d981 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -40,48 +40,49 @@ type Config struct { LogsStorage string `envconfig:"LOGS_STORAGE" default:""` WorkflowStorage string `envconfig:"WORKFLOW_STORAGE" default:"crd"` // WhitelistedContainers is a list of containers from which logs should be collected. - WhitelistedContainers []string `envconfig:"WHITELISTED_CONTAINERS" default:"init,logs,scraper"` - NatsEmbedded bool `envconfig:"NATS_EMBEDDED" default:"false"` - NatsEmbeddedStoreDir string `envconfig:"NATS_EMBEDDED_STORE_DIR" default:"/app/nats"` - NatsURI string `envconfig:"NATS_URI" default:"nats://localhost:4222"` - NatsSecure bool `envconfig:"NATS_SECURE" default:"false"` - NatsSkipVerify bool `envconfig:"NATS_SKIP_VERIFY" default:"false"` - NatsCertFile string `envconfig:"NATS_CERT_FILE" default:""` - NatsKeyFile string `envconfig:"NATS_KEY_FILE" default:""` - NatsCAFile string `envconfig:"NATS_CA_FILE" default:""` - NatsConnectTimeout time.Duration `envconfig:"NATS_CONNECT_TIMEOUT" default:"5s"` - JobServiceAccountName string `envconfig:"JOB_SERVICE_ACCOUNT_NAME" default:""` - JobTemplateFile string `envconfig:"JOB_TEMPLATE_FILE" default:""` - DisableTestTriggers bool `envconfig:"DISABLE_TEST_TRIGGERS" default:"false"` - TestkubeDefaultExecutors string `envconfig:"TESTKUBE_DEFAULT_EXECUTORS" default:""` - TestkubeEnabledExecutors string `envconfig:"TESTKUBE_ENABLED_EXECUTORS" default:""` - TestkubeTemplateJob string `envconfig:"TESTKUBE_TEMPLATE_JOB" default:""` - TestkubeContainerTemplateJob string `envconfig:"TESTKUBE_CONTAINER_TEMPLATE_JOB" default:""` - TestkubeContainerTemplateScraper string `envconfig:"TESTKUBE_CONTAINER_TEMPLATE_SCRAPER" default:""` - TestkubeContainerTemplatePVC string `envconfig:"TESTKUBE_CONTAINER_TEMPLATE_PVC" default:""` - TestkubeTemplateSlavePod string `envconfig:"TESTKUBE_TEMPLATE_SLAVE_POD" default:""` - TestkubeConfigDir string `envconfig:"TESTKUBE_CONFIG_DIR" default:"config"` - TestkubeAnalyticsEnabled bool `envconfig:"TESTKUBE_ANALYTICS_ENABLED" default:"false"` - TestkubeReadonlyExecutors bool `envconfig:"TESTKUBE_READONLY_EXECUTORS" default:"false"` - TestkubeNamespace string `envconfig:"TESTKUBE_NAMESPACE" default:"testkube"` - TestkubeProAPIKey string `envconfig:"TESTKUBE_PRO_API_KEY" default:""` - TestkubeProURL string `envconfig:"TESTKUBE_PRO_URL" default:""` - TestkubeProTLSInsecure bool `envconfig:"TESTKUBE_PRO_TLS_INSECURE" default:"false"` - TestkubeProWorkerCount int `envconfig:"TESTKUBE_PRO_WORKER_COUNT" default:"50"` - TestkubeProLogStreamWorkerCount int `envconfig:"TESTKUBE_PRO_LOG_STREAM_WORKER_COUNT" default:"25"` - TestkubeProWorkflowNotificationsWorkerCount int `envconfig:"TESTKUBE_PRO_WORKFLOW_NOTIFICATIONS_STREAM_WORKER_COUNT" default:"25"` - TestkubeProSkipVerify bool `envconfig:"TESTKUBE_PRO_SKIP_VERIFY" default:"false"` - TestkubeProEnvID string `envconfig:"TESTKUBE_PRO_ENV_ID" default:""` - TestkubeProOrgID string `envconfig:"TESTKUBE_PRO_ORG_ID" default:""` - TestkubeProMigrate string `envconfig:"TESTKUBE_PRO_MIGRATE" default:"false"` - TestkubeProConnectionTimeout int `envconfig:"TESTKUBE_PRO_CONNECTION_TIMEOUT" default:"10"` - TestkubeProCertFile string `envconfig:"TESTKUBE_PRO_CERT_FILE" default:""` - TestkubeProKeyFile string `envconfig:"TESTKUBE_PRO_KEY_FILE" default:""` - TestkubeProTLSSecret string `envconfig:"TESTKUBE_PRO_TLS_SECRET" default:""` - TestkubeProRunnerCustomCASecret string `envconfig:"TESTKUBE_PRO_RUNNER_CUSTOM_CA_SECRET" default:""` - TestkubeWatcherNamespaces string `envconfig:"TESTKUBE_WATCHER_NAMESPACES" default:""` - TestkubeRegistry string `envconfig:"TESTKUBE_REGISTRY" default:""` - TestkubePodStartTimeout time.Duration `envconfig:"TESTKUBE_POD_START_TIMEOUT" default:"30m"` + WhitelistedContainers []string `envconfig:"WHITELISTED_CONTAINERS" default:"init,logs,scraper"` + NatsEmbedded bool `envconfig:"NATS_EMBEDDED" default:"false"` + NatsEmbeddedStoreDir string `envconfig:"NATS_EMBEDDED_STORE_DIR" default:"/app/nats"` + NatsURI string `envconfig:"NATS_URI" default:"nats://localhost:4222"` + NatsSecure bool `envconfig:"NATS_SECURE" default:"false"` + NatsSkipVerify bool `envconfig:"NATS_SKIP_VERIFY" default:"false"` + NatsCertFile string `envconfig:"NATS_CERT_FILE" default:""` + NatsKeyFile string `envconfig:"NATS_KEY_FILE" default:""` + NatsCAFile string `envconfig:"NATS_CA_FILE" default:""` + NatsConnectTimeout time.Duration `envconfig:"NATS_CONNECT_TIMEOUT" default:"5s"` + JobServiceAccountName string `envconfig:"JOB_SERVICE_ACCOUNT_NAME" default:""` + JobTemplateFile string `envconfig:"JOB_TEMPLATE_FILE" default:""` + DisableTestTriggers bool `envconfig:"DISABLE_TEST_TRIGGERS" default:"false"` + TestkubeDefaultExecutors string `envconfig:"TESTKUBE_DEFAULT_EXECUTORS" default:""` + TestkubeEnabledExecutors string `envconfig:"TESTKUBE_ENABLED_EXECUTORS" default:""` + TestkubeTemplateJob string `envconfig:"TESTKUBE_TEMPLATE_JOB" default:""` + TestkubeContainerTemplateJob string `envconfig:"TESTKUBE_CONTAINER_TEMPLATE_JOB" default:""` + TestkubeContainerTemplateScraper string `envconfig:"TESTKUBE_CONTAINER_TEMPLATE_SCRAPER" default:""` + TestkubeContainerTemplatePVC string `envconfig:"TESTKUBE_CONTAINER_TEMPLATE_PVC" default:""` + TestkubeTemplateSlavePod string `envconfig:"TESTKUBE_TEMPLATE_SLAVE_POD" default:""` + TestkubeConfigDir string `envconfig:"TESTKUBE_CONFIG_DIR" default:"config"` + TestkubeAnalyticsEnabled bool `envconfig:"TESTKUBE_ANALYTICS_ENABLED" default:"false"` + TestkubeReadonlyExecutors bool `envconfig:"TESTKUBE_READONLY_EXECUTORS" default:"false"` + TestkubeNamespace string `envconfig:"TESTKUBE_NAMESPACE" default:"testkube"` + TestkubeProAPIKey string `envconfig:"TESTKUBE_PRO_API_KEY" default:""` + TestkubeProURL string `envconfig:"TESTKUBE_PRO_URL" default:""` + TestkubeProTLSInsecure bool `envconfig:"TESTKUBE_PRO_TLS_INSECURE" default:"false"` + TestkubeProWorkerCount int `envconfig:"TESTKUBE_PRO_WORKER_COUNT" default:"50"` + TestkubeProLogStreamWorkerCount int `envconfig:"TESTKUBE_PRO_LOG_STREAM_WORKER_COUNT" default:"25"` + TestkubeProWorkflowNotificationsWorkerCount int `envconfig:"TESTKUBE_PRO_WORKFLOW_NOTIFICATIONS_STREAM_WORKER_COUNT" default:"25"` + TestkubeProWorkflowServiceNotificationsWorkerCount int `envconfig:"TESTKUBE_PRO_WORKFLOW_SERVICE_NOTIFICATIONS_STREAM_WORKER_COUNT" default:"25"` + TestkubeProSkipVerify bool `envconfig:"TESTKUBE_PRO_SKIP_VERIFY" default:"false"` + TestkubeProEnvID string `envconfig:"TESTKUBE_PRO_ENV_ID" default:""` + TestkubeProOrgID string `envconfig:"TESTKUBE_PRO_ORG_ID" default:""` + TestkubeProMigrate string `envconfig:"TESTKUBE_PRO_MIGRATE" default:"false"` + TestkubeProConnectionTimeout int `envconfig:"TESTKUBE_PRO_CONNECTION_TIMEOUT" default:"10"` + TestkubeProCertFile string `envconfig:"TESTKUBE_PRO_CERT_FILE" default:""` + TestkubeProKeyFile string `envconfig:"TESTKUBE_PRO_KEY_FILE" default:""` + TestkubeProTLSSecret string `envconfig:"TESTKUBE_PRO_TLS_SECRET" default:""` + TestkubeProRunnerCustomCASecret string `envconfig:"TESTKUBE_PRO_RUNNER_CUSTOM_CA_SECRET" default:""` + TestkubeWatcherNamespaces string `envconfig:"TESTKUBE_WATCHER_NAMESPACES" default:""` + TestkubeRegistry string `envconfig:"TESTKUBE_REGISTRY" default:""` + TestkubePodStartTimeout time.Duration `envconfig:"TESTKUBE_POD_START_TIMEOUT" default:"30m"` // TestkubeImageCredentialsCacheTTL is the duration for which the image pull credentials should be cached provided as a Go duration string. // If set to 0, the cache is disabled. TestkubeImageCredentialsCacheTTL time.Duration `envconfig:"TESTKUBE_IMAGE_CREDENTIALS_CACHE_TTL" default:"30m"` diff --git a/internal/config/procontext.go b/internal/config/procontext.go index aa3b090be8..4bfcb41acf 100644 --- a/internal/config/procontext.go +++ b/internal/config/procontext.go @@ -1,16 +1,17 @@ package config type ProContext struct { - APIKey string - URL string - TLSInsecure bool - WorkerCount int - LogStreamWorkerCount int - WorkflowNotificationsWorkerCount int - SkipVerify bool - EnvID string - OrgID string - Migrate string - ConnectionTimeout int - DashboardURI string + APIKey string + URL string + TLSInsecure bool + WorkerCount int + LogStreamWorkerCount int + WorkflowNotificationsWorkerCount int + WorkflowServiceNotificationsWorkerCount int + SkipVerify bool + EnvID string + OrgID string + Migrate string + ConnectionTimeout int + DashboardURI string } diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go index b612770d26..e11fb03983 100644 --- a/pkg/agent/agent.go +++ b/pkg/agent/agent.go @@ -55,6 +55,11 @@ type Agent struct { testWorkflowNotificationsResponseBuffer chan *cloud.TestWorkflowNotificationsResponse testWorkflowNotificationsFunc func(ctx context.Context, executionID string) (<-chan testkube.TestWorkflowExecutionNotification, error) + testWorkflowServiceNotificationsWorkerCount int + testWorkflowServiceNotificationsRequestBuffer chan *cloud.TestWorkflowServiceNotificationsRequest + testWorkflowServiceNotificationsResponseBuffer chan *cloud.TestWorkflowServiceNotificationsResponse + testWorkflowServiceNotificationsFunc func(ctx context.Context, executionID, serviceName string, serviceIndex int) (<-chan testkube.TestWorkflowExecutionNotification, error) + events chan testkube.Event sendTimeout time.Duration receiveTimeout time.Duration @@ -73,6 +78,7 @@ func NewAgent(logger *zap.SugaredLogger, client cloud.TestKubeCloudAPIClient, logStreamFunc func(ctx context.Context, executionID string) (chan output.Output, error), workflowNotificationsFunc func(ctx context.Context, executionID string) (<-chan testkube.TestWorkflowExecutionNotification, error), + workflowServiceNotificationsFunc func(ctx context.Context, executionID, serviceName string, serviceIndex int) (<-chan testkube.TestWorkflowExecutionNotification, error), clusterID string, clusterName string, features featureflags.FeatureFlags, @@ -99,11 +105,16 @@ func NewAgent(logger *zap.SugaredLogger, testWorkflowNotificationsRequestBuffer: make(chan *cloud.TestWorkflowNotificationsRequest, bufferSizePerWorker*proContext.WorkflowNotificationsWorkerCount), testWorkflowNotificationsResponseBuffer: make(chan *cloud.TestWorkflowNotificationsResponse, bufferSizePerWorker*proContext.WorkflowNotificationsWorkerCount), testWorkflowNotificationsFunc: workflowNotificationsFunc, - clusterID: clusterID, - clusterName: clusterName, - features: features, - proContext: proContext, - dockerImageVersion: dockerImageVersion, + testWorkflowServiceNotificationsWorkerCount: proContext.WorkflowServiceNotificationsWorkerCount, + testWorkflowServiceNotificationsRequestBuffer: make(chan *cloud.TestWorkflowServiceNotificationsRequest, bufferSizePerWorker*proContext.WorkflowServiceNotificationsWorkerCount), + testWorkflowServiceNotificationsResponseBuffer: make(chan *cloud.TestWorkflowServiceNotificationsResponse, bufferSizePerWorker*proContext.WorkflowServiceNotificationsWorkerCount), + testWorkflowServiceNotificationsFunc: workflowServiceNotificationsFunc, + + clusterID: clusterID, + clusterName: clusterName, + features: features, + proContext: proContext, + dockerImageVersion: dockerImageVersion, }, nil } @@ -151,6 +162,13 @@ func (ag *Agent) run(ctx context.Context) (err error) { return ag.runTestWorkflowNotificationsWorker(groupCtx, ag.testWorkflowNotificationsWorkerCount) }) + g.Go(func() error { + return ag.runTestWorkflowServiceNotificationsLoop(groupCtx) + }) + g.Go(func() error { + return ag.runTestWorkflowServiceNotificationsWorker(groupCtx, ag.testWorkflowServiceNotificationsWorkerCount) + }) + err = g.Wait() return err diff --git a/pkg/agent/agent_test.go b/pkg/agent/agent_test.go index fb6c6d1741..47973e09ac 100644 --- a/pkg/agent/agent_test.go +++ b/pkg/agent/agent_test.go @@ -59,10 +59,11 @@ func TestCommandExecution(t *testing.T) { var logStreamFunc func(ctx context.Context, executionID string) (chan output.Output, error) var workflowNotificationsStreamFunc func(ctx context.Context, executionID string) (<-chan testkube.TestWorkflowExecutionNotification, error) + var workflowServiceNotificationsStreamFunc func(ctx context.Context, executionID, serviceName string, serviceIndex int) (<-chan testkube.TestWorkflowExecutionNotification, error) logger, _ := zap.NewDevelopment() proContext := config.ProContext{APIKey: "api-key", WorkerCount: 5, LogStreamWorkerCount: 5, WorkflowNotificationsWorkerCount: 5} - agent, err := agent.NewAgent(logger.Sugar(), m, grpcClient, logStreamFunc, workflowNotificationsStreamFunc, "", "", featureflags.FeatureFlags{}, &proContext, "") + agent, err := agent.NewAgent(logger.Sugar(), m, grpcClient, logStreamFunc, workflowNotificationsStreamFunc, workflowServiceNotificationsStreamFunc, "", "", featureflags.FeatureFlags{}, &proContext, "") if err != nil { t.Fatal(err) } @@ -97,6 +98,12 @@ func (cs *CloudServer) GetTestWorkflowNotificationsStream(srv cloud.TestKubeClou return nil } +func (cs *CloudServer) GetTestWorkflowServiceNotificationsStream(srv cloud.TestKubeCloudAPI_GetTestWorkflowServiceNotificationsStreamServer) error { + <-cs.ctx.Done() + + return nil +} + func (cs *CloudServer) ExecuteAsync(srv cloud.TestKubeCloudAPI_ExecuteAsyncServer) error { md, ok := metadata.FromIncomingContext(srv.Context()) if !ok { diff --git a/pkg/agent/events_test.go b/pkg/agent/events_test.go index 663e06ac19..1f52ea26f4 100644 --- a/pkg/agent/events_test.go +++ b/pkg/agent/events_test.go @@ -56,9 +56,10 @@ func TestEventLoop(t *testing.T) { var logStreamFunc func(ctx context.Context, executionID string) (chan output.Output, error) var workflowNotificationsStreamFunc func(ctx context.Context, executionID string) (<-chan testkube.TestWorkflowExecutionNotification, error) + var workflowServiceNotificationsStreamFunc func(ctx context.Context, executionID, serviceName string, serviceIndex int) (<-chan testkube.TestWorkflowExecutionNotification, error) proContext := config.ProContext{APIKey: "api-key", WorkerCount: 5, LogStreamWorkerCount: 5, WorkflowNotificationsWorkerCount: 5} - agent, err := agent.NewAgent(logger.Sugar(), nil, grpcClient, logStreamFunc, workflowNotificationsStreamFunc, "", "", featureflags.FeatureFlags{}, &proContext, "") + agent, err := agent.NewAgent(logger.Sugar(), nil, grpcClient, logStreamFunc, workflowNotificationsStreamFunc, workflowServiceNotificationsStreamFunc, "", "", featureflags.FeatureFlags{}, &proContext, "") assert.NoError(t, err) go func() { l, err := agent.Load() @@ -110,6 +111,12 @@ func (cws *CloudEventServer) GetTestWorkflowNotificationsStream(srv cloud.TestKu return nil } +func (cws *CloudEventServer) GetTestWorkflowServiceNotificationsStream(srv cloud.TestKubeCloudAPI_GetTestWorkflowServiceNotificationsStreamServer) error { + <-cws.ctx.Done() + + return nil +} + func (cws *CloudEventServer) Send(srv cloud.TestKubeCloudAPI_SendServer) error { md, ok := metadata.FromIncomingContext(srv.Context()) if !ok { diff --git a/pkg/agent/logs_test.go b/pkg/agent/logs_test.go index 0491467415..59eaecf6f0 100644 --- a/pkg/agent/logs_test.go +++ b/pkg/agent/logs_test.go @@ -66,10 +66,11 @@ func TestLogStream(t *testing.T) { return ch, nil } var workflowNotificationsStreamFunc func(ctx context.Context, executionID string) (<-chan testkube.TestWorkflowExecutionNotification, error) + var workflowServiceNotificationsStreamFunc func(ctx context.Context, executionID, serviceName string, serviceIndex int) (<-chan testkube.TestWorkflowExecutionNotification, error) logger, _ := zap.NewDevelopment() proContext := config.ProContext{APIKey: "api-key", WorkerCount: 5, LogStreamWorkerCount: 5, WorkflowNotificationsWorkerCount: 5} - agent, err := agent.NewAgent(logger.Sugar(), m, grpcClient, logStreamFunc, workflowNotificationsStreamFunc, "", "", featureflags.FeatureFlags{}, &proContext, "") + agent, err := agent.NewAgent(logger.Sugar(), m, grpcClient, logStreamFunc, workflowNotificationsStreamFunc, workflowServiceNotificationsStreamFunc, "", "", featureflags.FeatureFlags{}, &proContext, "") if err != nil { t.Fatal(err) } @@ -102,6 +103,11 @@ func (cs *CloudLogsServer) GetTestWorkflowNotificationsStream(srv cloud.TestKube return nil } +func (cs *CloudLogsServer) GetTestWorkflowServiceNotificationsStream(srv cloud.TestKubeCloudAPI_GetTestWorkflowServiceNotificationsStreamServer) error { + <-cs.ctx.Done() + return nil +} + func (cs *CloudLogsServer) GetLogsStream(srv cloud.TestKubeCloudAPI_GetLogsStreamServer) error { md, ok := metadata.FromIncomingContext(srv.Context()) if !ok { diff --git a/pkg/agent/testworkflows.go b/pkg/agent/testworkflows.go index 2001fa9deb..969f443c6c 100644 --- a/pkg/agent/testworkflows.go +++ b/pkg/agent/testworkflows.go @@ -74,6 +74,51 @@ func (ag *Agent) runTestWorkflowNotificationsLoop(ctx context.Context) error { return err } +func (ag *Agent) runTestWorkflowServiceNotificationsLoop(ctx context.Context) error { + ctx = agentclient.AddAPIKeyMeta(ctx, ag.apiKey) + + ag.logger.Infow("initiating workflow service notifications streaming connection with Cloud API") + // creates a new Stream from the client side. ctx is used for the lifetime of the stream. + opts := []grpc.CallOption{grpc.UseCompressor(gzip.Name), grpc.MaxCallRecvMsgSize(math.MaxInt32)} + stream, err := ag.client.GetTestWorkflowServiceNotificationsStream(ctx, opts...) + if err != nil { + ag.logger.Errorf("failed to execute: %w", err) + return errors.Wrap(err, "failed to setup stream") + } + + // GRPC stream have special requirements for concurrency on SendMsg, and RecvMsg calls. + // Please check https://github.com/grpc/grpc-go/blob/master/Documentation/concurrency.md + g, groupCtx := errgroup.WithContext(ctx) + g.Go(func() error { + for { + cmd, err := ag.receiveTestWorkflowServiceNotificationsRequest(groupCtx, stream) + if err != nil { + return err + } + + ag.testWorkflowServiceNotificationsRequestBuffer <- cmd + } + }) + + g.Go(func() error { + for { + select { + case resp := <-ag.testWorkflowServiceNotificationsResponseBuffer: + err := ag.sendTestWorkflowServiceNotificationsResponse(groupCtx, stream, resp) + if err != nil { + return err + } + case <-groupCtx.Done(): + return groupCtx.Err() + } + } + }) + + err = g.Wait() + + return err +} + func (ag *Agent) runTestWorkflowNotificationsWorker(ctx context.Context, numWorkers int) error { g, groupCtx := errgroup.WithContext(ctx) for i := 0; i < numWorkers; i++ { @@ -102,6 +147,34 @@ func (ag *Agent) runTestWorkflowNotificationsWorker(ctx context.Context, numWork return g.Wait() } +func (ag *Agent) runTestWorkflowServiceNotificationsWorker(ctx context.Context, numWorkers int) error { + g, groupCtx := errgroup.WithContext(ctx) + for i := 0; i < numWorkers; i++ { + g.Go(func() error { + for { + select { + case req := <-ag.testWorkflowServiceNotificationsRequestBuffer: + if req.RequestType == cloud.TestWorkflowNotificationsRequestType_WORKFLOW_STREAM_HEALTH_CHECK { + ag.testWorkflowServiceNotificationsResponseBuffer <- &cloud.TestWorkflowServiceNotificationsResponse{ + StreamId: req.StreamId, + SeqNo: 0, + } + break + } + + err := ag.executeWorkflowServiceNotificationsRequest(groupCtx, req) + if err != nil { + ag.logger.Errorf("error executing workflow service notifications request: %s", err.Error()) + } + case <-groupCtx.Done(): + return groupCtx.Err() + } + } + }) + } + return g.Wait() +} + func (ag *Agent) executeWorkflowNotificationsRequest(ctx context.Context, req *cloud.TestWorkflowNotificationsRequest) error { notificationsCh, err := ag.testWorkflowNotificationsFunc(ctx, req.ExecutionId) for i := 0; i < testWorkflowNotificationsRetryCount; i++ { @@ -162,6 +235,66 @@ func (ag *Agent) executeWorkflowNotificationsRequest(ctx context.Context, req *c } } +func (ag *Agent) executeWorkflowServiceNotificationsRequest(ctx context.Context, req *cloud.TestWorkflowServiceNotificationsRequest) error { + notificationsCh, err := ag.testWorkflowServiceNotificationsFunc(ctx, req.ExecutionId, req.ServiceName, int(req.ServiceIndex)) + for i := 0; i < testWorkflowNotificationsRetryCount; i++ { + if err != nil { + // We have a race condition here + // Cloud sometimes slow to insert execution or test + // while WorkflowNotifications request from websockets comes in faster + // so we retry up to testWorkflowNotificationsRetryCount times. + time.Sleep(100 * time.Millisecond) + notificationsCh, err = ag.testWorkflowServiceNotificationsFunc(ctx, req.ExecutionId, req.ServiceName, int(req.ServiceIndex)) + } + } + if err != nil { + message := fmt.Sprintf("cannot get pod logs: %s", err.Error()) + ag.testWorkflowServiceNotificationsResponseBuffer <- &cloud.TestWorkflowServiceNotificationsResponse{ + StreamId: req.StreamId, + SeqNo: 0, + Type: cloud.TestWorkflowNotificationType_WORKFLOW_STREAM_ERROR, + Message: fmt.Sprintf("%s %s", time.Now().Format(controller.KubernetesLogTimeFormat), message), + } + return nil + } + + for { + var i uint32 + select { + case n, ok := <-notificationsCh: + if !ok { + return nil + } + t := getTestWorkflowNotificationType(n) + msg := &cloud.TestWorkflowServiceNotificationsResponse{ + StreamId: req.StreamId, + SeqNo: i, + Timestamp: n.Ts.Format(time.RFC3339Nano), + Ref: n.Ref, + Type: t, + } + if n.Result != nil { + m, _ := json.Marshal(n.Result) + msg.Message = string(m) + } else if n.Output != nil { + m, _ := json.Marshal(n.Output) + msg.Message = string(m) + } else { + msg.Message = n.Log + } + i++ + + select { + case ag.testWorkflowServiceNotificationsResponseBuffer <- msg: + case <-ctx.Done(): + return ctx.Err() + } + case <-ctx.Done(): + return ctx.Err() + } + } +} + func (ag *Agent) receiveTestWorkflowNotificationsRequest(ctx context.Context, stream cloud.TestKubeCloudAPI_GetTestWorkflowNotificationsStreamClient) (*cloud.TestWorkflowNotificationsRequest, error) { respChan := make(chan testWorkflowNotificationsRequest, 1) go func() { @@ -191,6 +324,35 @@ type testWorkflowNotificationsRequest struct { err error } +func (ag *Agent) receiveTestWorkflowServiceNotificationsRequest(ctx context.Context, stream cloud.TestKubeCloudAPI_GetTestWorkflowServiceNotificationsStreamClient) (*cloud.TestWorkflowServiceNotificationsRequest, error) { + respChan := make(chan testWorkflowServiceNotificationsRequest, 1) + go func() { + cmd, err := stream.Recv() + respChan <- testWorkflowServiceNotificationsRequest{resp: cmd, err: err} + }() + + var cmd *cloud.TestWorkflowServiceNotificationsRequest + select { + case resp := <-respChan: + cmd = resp.resp + err := resp.err + + if err != nil { + ag.logger.Errorf("agent stream receive: %v", err) + return nil, err + } + case <-ctx.Done(): + return nil, ctx.Err() + } + + return cmd, nil +} + +type testWorkflowServiceNotificationsRequest struct { + resp *cloud.TestWorkflowServiceNotificationsRequest + err error +} + func (ag *Agent) sendTestWorkflowNotificationsResponse(ctx context.Context, stream cloud.TestKubeCloudAPI_GetTestWorkflowNotificationsStreamClient, resp *cloud.TestWorkflowNotificationsResponse) error { errChan := make(chan error, 1) go func() { @@ -215,3 +377,28 @@ func (ag *Agent) sendTestWorkflowNotificationsResponse(ctx context.Context, stre return errors.New("send response too slow") } } + +func (ag *Agent) sendTestWorkflowServiceNotificationsResponse(ctx context.Context, stream cloud.TestKubeCloudAPI_GetTestWorkflowServiceNotificationsStreamClient, resp *cloud.TestWorkflowServiceNotificationsResponse) error { + errChan := make(chan error, 1) + go func() { + errChan <- stream.Send(resp) + close(errChan) + }() + + t := time.NewTimer(ag.sendTimeout) + select { + case err := <-errChan: + if !t.Stop() { + <-t.C + } + return err + case <-ctx.Done(): + if !t.Stop() { + <-t.C + } + + return ctx.Err() + case <-t.C: + return errors.New("send response too slow") + } +}