From 073e1c89b33b86d6e0a9481de3779aef6df9bb83 Mon Sep 17 00:00:00 2001 From: John Collier Date: Fri, 4 Aug 2023 17:19:25 -0400 Subject: [PATCH 1/9] [DEVHAS-434] Move GitOps operations to Go module Signed-off-by: John Collier --- codecov.yml | 4 +- ...onsnapshotenvironmentbinding_controller.go | 291 +- controllers/component_controller.go | 96 +- controllers/component_controller_unit_test.go | 10 +- controllers/start_test_env.go | 30 +- gitops-generator/.gitignore | 4 + gitops-generator/Dockerfile | 35 + gitops-generator/appdata.gitconfig | 3 + gitops-generator/entrypoint.sh | 9 + gitops-generator/go.mod | 36 + gitops-generator/pkg/devfile/constants.go | 48 + gitops-generator/pkg/devfile/devfile.go | 560 +++ gitops-generator/pkg/devfile/devfile_test.go | 3114 +++++++++++++++++ gitops-generator/pkg/generate/generate.go | 367 ++ .../pkg/generate}/generate_mock.go | 2 +- .../pkg/generate/generate_test.go | 898 +++++ gitops-generator/pkg/util/util.go | 159 + gitops-generator/pkg/util/util_test.go | 529 +++ gitops/generate.go | 52 - gitops/generate_build.go | 202 -- gitops/generate_build_test.go | 507 --- gitops/generate_test.go | 124 - gitops/prepare/prepare.go | 57 - gitops/prepare/prepare_test.go | 135 - go.work | 7 + go.work.sum | 4 + main.go | 33 +- pkg/util/util.go | 16 - pkg/util/util_test.go | 40 - 29 files changed, 5864 insertions(+), 1508 deletions(-) create mode 100644 gitops-generator/.gitignore create mode 100644 gitops-generator/Dockerfile create mode 100644 gitops-generator/appdata.gitconfig create mode 100644 gitops-generator/entrypoint.sh create mode 100644 gitops-generator/go.mod create mode 100644 gitops-generator/pkg/devfile/constants.go create mode 100644 gitops-generator/pkg/devfile/devfile.go create mode 100644 gitops-generator/pkg/devfile/devfile_test.go create mode 100644 gitops-generator/pkg/generate/generate.go rename {gitops => gitops-generator/pkg/generate}/generate_mock.go (99%) create mode 100644 gitops-generator/pkg/generate/generate_test.go create mode 100644 gitops-generator/pkg/util/util.go create mode 100644 gitops-generator/pkg/util/util_test.go delete mode 100644 gitops/generate.go delete mode 100644 gitops/generate_build.go delete mode 100644 gitops/generate_build_test.go delete mode 100644 gitops/generate_test.go delete mode 100644 gitops/prepare/prepare.go delete mode 100644 gitops/prepare/prepare_test.go create mode 100644 go.work create mode 100644 go.work.sum diff --git a/codecov.yml b/codecov.yml index 2cf44327..f78c669c 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,11 +1,11 @@ ignore: - "api/v1alpha1/zz_generated.deepcopy.go" # generated file, does not need to be included in the coverage - "pkg/spi/spi_mock.go" # mock file for testing - - "gitops/generate_mock.go" # mock file for testing + - "gitops-generator/pkg/generate/generate_mock.go" # mock file for testing - "controllers/start_test_env.go" # setup of a test environment for unit and Pact tests - "controllers/application_pact_test_state_handlers.go" # state handlers for the Pact tests - "controllers/application_pact_test_utils.go" # utils file for the Pact tests - "cdq-analysis/pkg/detect_mock.go" # mock file for testing - "cdq-analysis/main.go" # entry point for cdq-analysis docker image - "pkg/github/mock.go" # mock file for testing - - "pkg/github/token_moke.go" # mock file for testing \ No newline at end of file + - "pkg/github/token_moke.go" # mock file for testing diff --git a/controllers/applicationsnapshotenvironmentbinding_controller.go b/controllers/applicationsnapshotenvironmentbinding_controller.go index e128d0e4..025c268c 100644 --- a/controllers/applicationsnapshotenvironmentbinding_controller.go +++ b/controllers/applicationsnapshotenvironmentbinding_controller.go @@ -19,24 +19,17 @@ package controllers import ( "context" "fmt" - "path/filepath" - "reflect" - "github.com/prometheus/client_golang/prometheus" - cdqanalysis "github.com/redhat-appstudio/application-service/cdq-analysis/pkg" - "github.com/redhat-appstudio/application-service/pkg/metrics" - gitopsgenv1alpha1 "github.com/redhat-developer/gitops-generator/api/v1alpha1" + gitopsjoblib "github.com/redhat-appstudio/application-service/gitops-generator/pkg/generate" + gitopsgen "github.com/redhat-developer/gitops-generator/pkg" "golang.org/x/exp/maps" - corev1 "k8s.io/api/core/v1" - devfileParser "github.com/devfile/library/v2/pkg/devfile/parser" "github.com/go-logr/logr" appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" - devfile "github.com/redhat-appstudio/application-service/pkg/devfile" + "github.com/redhat-appstudio/application-service/gitops-generator/pkg/generate" github "github.com/redhat-appstudio/application-service/pkg/github" logutil "github.com/redhat-appstudio/application-service/pkg/log" - "github.com/redhat-appstudio/application-service/pkg/util" "github.com/redhat-appstudio/application-service/pkg/util/ioutils" "github.com/spf13/afero" "k8s.io/apimachinery/pkg/api/errors" @@ -60,6 +53,12 @@ type SnapshotEnvironmentBindingReconciler struct { AppFS afero.Afero Generator gitopsgen.Generator GitHubTokenClient github.GitHubToken + + // DoLocalGitOpsGen determines whether or not to only spawn off gitops generation jobs, or to run them locally inside HAS. Defaults to false + DoGitOpsJob bool + + // AllowLocalGitopsGen allows for certain resources to generate gitops resources locally, *if* an annotation is present on the resource. Defaults to false + AllowLocalGitopsGen bool } const asebName = "SnapshotEnvironmentBinding" @@ -110,7 +109,6 @@ func (r *SnapshotEnvironmentBindingReconciler) Reconcile(ctx context.Context, re applicationName := appSnapshotEnvBinding.Spec.Application environmentName := appSnapshotEnvBinding.Spec.Environment snapshotName := appSnapshotEnvBinding.Spec.Snapshot - components := appSnapshotEnvBinding.Spec.Components // Check if the labels have been applied to the binding requiredLabels := map[string]string{ @@ -155,274 +153,19 @@ func (r *SnapshotEnvironmentBindingReconciler) Reconcile(ctx context.Context, re return ctrl.Result{}, err } - componentGeneratedResources := make(map[string][]string) - var tempDir string - clone := true - - for _, component := range components { - componentName := component.Name - - // Get the Component CR - hasComponent := appstudiov1alpha1.Component{} - err = r.Get(ctx, types.NamespacedName{Name: componentName, Namespace: appSnapshotEnvBinding.Namespace}, &hasComponent) - if err != nil { - log.Error(err, fmt.Sprintf("unable to get the Component %s %v", componentName, req.NamespacedName)) - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) - return ctrl.Result{}, err - } - - if hasComponent.Spec.SkipGitOpsResourceGeneration { - continue - } - - // Sanity check to make sure the binding component has referenced the correct application - if hasComponent.Spec.Application != applicationName { - err := fmt.Errorf("component %s does not belong to the application %s", componentName, applicationName) - log.Error(err, "") - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) - return ctrl.Result{}, err - } - - var clusterIngressDomain string - isKubernetesCluster := isKubernetesCluster(environment) - unsupportedConfig := environment.Spec.UnstableConfigurationFields - if unsupportedConfig != nil { - clusterIngressDomain = unsupportedConfig.IngressDomain - } - - // Safeguard if Ingress Domain is empty on Kubernetes - if isKubernetesCluster && clusterIngressDomain == "" { - err = fmt.Errorf("ingress domain cannot be empty on a Kubernetes cluster") - log.Error(err, "unable to create an ingress resource on a Kubernetes cluster") - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) - return ctrl.Result{}, err - } - - devfileSrc := cdqanalysis.DevfileSrc{ - Data: hasComponent.Status.Devfile, - } - compDevfileData, err := cdqanalysis.ParseDevfile(devfileSrc) - if err != nil { - errMsg := fmt.Sprintf("Unable to parse the devfile from Component status, exiting reconcile loop %v", req.NamespacedName) - log.Error(err, errMsg) - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, fmt.Errorf("%v: %v", errMsg, err)) - return ctrl.Result{}, err - } - - deployAssociatedComponents, err := devfileParser.GetDeployComponents(compDevfileData) - if err != nil { - log.Error(err, "unable to get deploy components") - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) - return ctrl.Result{}, err - } - - var hostname string - if isKubernetesCluster { - hostname, err = devfile.GetIngressHostName(hasComponent.Name, appSnapshotEnvBinding.Namespace, clusterIngressDomain) - if err != nil { - log.Error(err, fmt.Sprintf("unable to get generate a host name from an ingress domain for %s %v", hasComponent.Name, req.NamespacedName)) - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) - return ctrl.Result{}, err - } - } - - // Generate a route name for the component - - kubernetesResources, err := devfile.GetResourceFromDevfile(log, compDevfileData, deployAssociatedComponents, hasComponent.Name, hasComponent.Spec.Application, hasComponent.Spec.ContainerImage, hostname) - if err != nil { - log.Error(err, "unable to get kubernetes resources from the devfile outerloop components") - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) - return ctrl.Result{}, err - } - - // Create a random, generated name for the route - // ToDo: Ideally we wouldn't need to loop here, but since the Component status is a list, we can't avoid it - var routeName string - for _, compStatus := range appSnapshotEnvBinding.Status.Components { - if compStatus.Name == componentName { - if compStatus.GeneratedRouteName != "" { - routeName = compStatus.GeneratedRouteName - log.Info(fmt.Sprintf("route name for component is %s", routeName)) - } - break - } - } - if routeName == "" { - routeName = util.GenerateRandomRouteName(hasComponent.Name) - log.Info(fmt.Sprintf("generated route name %s", routeName)) - } - - // If a route is present, update the first instance's name - if len(kubernetesResources.Routes) > 0 { - kubernetesResources.Routes[0].ObjectMeta.Name = routeName - } - - var imageName string - - for _, snapshotComponent := range appSnapshot.Spec.Components { - if snapshotComponent.Name == componentName { - imageName = snapshotComponent.ContainerImage - break - } - } - - if imageName == "" { - err := fmt.Errorf("application snapshot %s did not reference component %s", snapshotName, componentName) - log.Error(err, "") - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) - return ctrl.Result{}, err - } + localGitopsGen := (!r.DoGitOpsJob) || (r.AllowLocalGitopsGen && appSnapshotEnvBinding.Annotations["allowLocalGitopsGen"] == "true") - gitOpsRemoteURL, gitOpsBranch, gitOpsContext, err := util.ProcessGitOpsStatus(hasComponent.Status.GitOps, ghClient.Token) + if localGitopsGen { + err = gitopsjoblib.GenerateGitopsOverlays(context.Background(), log, r.Client, appSnapshotEnvBinding, ioutils.NewFilesystem(), generate.GitOpsGenParams{ + Generator: r.Generator, + Token: ghClient.Token, + }) if err != nil { r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) return ctrl.Result{}, err } - - if clone { - // Create a temp folder to create the gitops resources in - tempDir, err = ioutils.CreateTempPath(appSnapshotEnvBinding.Name, r.AppFS) - if err != nil { - log.Error(err, "unable to create temp directory for gitops resources due to error") - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) - return ctrl.Result{}, fmt.Errorf("unable to create temp directory for gitops resources due to error: %v", err) - } - } - - envVars := make([]corev1.EnvVar, 0) - for _, env := range component.Configuration.Env { - envVars = append(envVars, corev1.EnvVar{ - Name: env.Name, - Value: env.Value, - }) - } - - environmentConfigEnvVars := make([]corev1.EnvVar, 0) - for _, env := range environment.Spec.Configuration.Env { - environmentConfigEnvVars = append(environmentConfigEnvVars, corev1.EnvVar{ - Name: env.Name, - Value: env.Value, - }) - } - componentResources := corev1.ResourceRequirements{} - if component.Configuration.Resources != nil { - componentResources = *component.Configuration.Resources - } - - kubeLabels := map[string]string{ - "app.kubernetes.io/name": componentName, - "app.kubernetes.io/instance": component.Name, - "app.kubernetes.io/part-of": applicationName, - "app.kubernetes.io/managed-by": "kustomize", - "app.kubernetes.io/created-by": "application-service", - } - genOptions := gitopsgenv1alpha1.GeneratorOptions{ - Name: component.Name, - RouteName: routeName, - Resources: componentResources, - BaseEnvVar: envVars, - OverlayEnvVar: environmentConfigEnvVars, - K8sLabels: kubeLabels, - IsKubernetesCluster: isKubernetesCluster, - TargetPort: hasComponent.Spec.TargetPort, // pass the target port to the gitops gen library as they may generate a route/ingress based on the target port if the devfile does not have an ingress/route or an endpoint - } - - if component.Configuration.Replicas != nil { - genOptions.Replicas = *component.Configuration.Replicas - } - - if !reflect.DeepEqual(kubernetesResources, devfileParser.KubernetesResources{}) { - genOptions.KubernetesResources.Routes = append(genOptions.KubernetesResources.Routes, kubernetesResources.Routes...) - genOptions.KubernetesResources.Ingresses = append(genOptions.KubernetesResources.Ingresses, kubernetesResources.Ingresses...) - } - - if isKubernetesCluster && len(genOptions.KubernetesResources.Ingresses) == 0 { - // provide the hostname for the component if there are no ingresses - // Gitops Generator Library will create the Ingress with the hostname - genOptions.Route = hostname - } - - //Gitops functions return sanitized error messages - metrics.ControllerGitRequest.With(prometheus.Labels{"controller": asebName, "tokenName": ghClient.TokenName, "operation": "GenerateOverlaysAndPush"}).Inc() - err = r.Generator.GenerateOverlaysAndPush(tempDir, clone, gitOpsRemoteURL, genOptions, applicationName, environmentName, imageName, "", r.AppFS, gitOpsBranch, gitOpsContext, true, componentGeneratedResources) - if err != nil { - log.Error(err, fmt.Sprintf("unable to get generate gitops resources for %s %v", componentName, req.NamespacedName)) - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) // not worried with an err, its a best case attempt to delete the temp clone dir - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) - return ctrl.Result{}, err - } - - // Retrieve the commit ID - var commitID string - repoPath := filepath.Join(tempDir, applicationName) - metricsLabel := prometheus.Labels{"controller": asebName, "tokenName": ghClient.TokenName, "operation": "GetCommitIDFromRepo"} - metrics.ControllerGitRequest.With(metricsLabel).Inc() - if commitID, err = r.Generator.GetCommitIDFromRepo(r.AppFS, repoPath); err != nil { - //gitops generator errors are sanitized - log.Error(err, "") - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) - return ctrl.Result{}, err - } - - // Set the BindingComponent status - componentStatus := appstudiov1alpha1.BindingComponentStatus{ - Name: componentName, - GitOpsRepository: appstudiov1alpha1.BindingComponentGitOpsRepository{ - URL: hasComponent.Status.GitOps.RepositoryURL, - Branch: gitOpsBranch, - Path: filepath.Join(gitOpsContext, "components", componentName, "overlays", environmentName), - CommitID: commitID, - }, - } - - // On OpenShift, we generate a unique route name for each Component, so include that in the status - if !isKubernetesCluster { - componentStatus.GeneratedRouteName = routeName - log.Info(fmt.Sprintf("added RouteName %s for Component %s to status", routeName, componentName)) - } - - if _, ok := componentGeneratedResources[componentName]; ok { - componentStatus.GitOpsRepository.GeneratedResources = componentGeneratedResources[componentName] - } - - isNewComponent := true - for i := range appSnapshotEnvBinding.Status.Components { - if appSnapshotEnvBinding.Status.Components[i].Name == componentStatus.Name { - appSnapshotEnvBinding.Status.Components[i] = componentStatus - isNewComponent = false - break - } - } - if isNewComponent { - appSnapshotEnvBinding.Status.Components = append(appSnapshotEnvBinding.Status.Components, componentStatus) - } - - // Set the clone to false, since we dont want to clone the repo again for the other components - clone = false - } - - // Remove the cloned path - err = r.AppFS.RemoveAll(tempDir) - if err != nil { - log.Error(err, "Unable to remove the clone dir") - } - - // Update the binding status to reflect the GitOps data - err = r.Client.Status().Update(ctx, &appSnapshotEnvBinding) - if err != nil { - log.Error(err, "Unable to update App Snapshot Env Binding") - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) - return ctrl.Result{}, err + } else { + // ToDo: Add support for GitOps job } r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, nil) diff --git a/controllers/component_controller.go b/controllers/component_controller.go index df444116..20366bf7 100644 --- a/controllers/component_controller.go +++ b/controllers/component_controller.go @@ -20,12 +20,13 @@ import ( "context" "fmt" "os" - "path/filepath" "reflect" "time" "github.com/prometheus/client_golang/prometheus" cdqanalysis "github.com/redhat-appstudio/application-service/cdq-analysis/pkg" + "github.com/redhat-appstudio/application-service/gitops-generator/pkg/generate" + gitopsjoblib "github.com/redhat-appstudio/application-service/gitops-generator/pkg/generate" "github.com/redhat-appstudio/application-service/pkg/metrics" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -45,7 +46,6 @@ import ( "sigs.k8s.io/yaml" "github.com/devfile/api/v2/pkg/attributes" - devfileParser "github.com/devfile/library/v2/pkg/devfile/parser" data "github.com/devfile/library/v2/pkg/devfile/parser/data" "github.com/go-logr/logr" @@ -70,6 +70,12 @@ type ComponentReconciler struct { AppFS afero.Afero SPIClient spi.SPI GitHubTokenClient github.GitHubToken + + // DoLocalGitOpsGen determines whether or not to only spawn off gitops generation jobs, or to run them locally inside HAS. Defaults to false + DoGitOpsJob bool + + // AllowLocalGitopsGen allows for certain resources to generate gitops resources locally, *if* an annotation is present on the resource. Defaults to false + AllowLocalGitopsGen bool } const ( @@ -185,18 +191,13 @@ func (r *ComponentReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( for _, condition := range component.Status.Conditions { if condition.Type == "GitOpsResourcesGenerated" && condition.Reason == "GenerateError" && condition.Status == metav1.ConditionFalse { log.Info(fmt.Sprintf("Re-attempting GitOps generation for %s", component.Name)) - // Parse the Component Devfile - devfileSrc := cdqanalysis.DevfileSrc{ - Data: component.Status.Devfile, - } - compDevfileData, err := cdqanalysis.ParseDevfile(devfileSrc) if err != nil { errMsg := fmt.Sprintf("Unable to parse the devfile from Component status and re-attempt GitOps generation, exiting reconcile loop %v", req.NamespacedName) log.Error(err, errMsg) _ = r.SetGitOpsGeneratedConditionAndUpdateCR(ctx, req, &component, fmt.Errorf("%v: %v", errMsg, err)) return ctrl.Result{}, err } - if err := r.generateGitops(ctx, ghClient, &component, compDevfileData); err != nil { + if err := r.generateGitops(ctx, ghClient, &component); err != nil { errMsg := fmt.Sprintf("Unable to generate gitops resources for component %v", req.NamespacedName) log.Error(err, errMsg) _ = r.SetGitOpsGeneratedConditionAndUpdateCR(ctx, req, &component, fmt.Errorf("%v: %v", errMsg, err)) @@ -429,7 +430,7 @@ func (r *ComponentReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( // Generate and push the gitops resources if !component.Spec.SkipGitOpsResourceGeneration { - if err := r.generateGitops(ctx, ghClient, &component, compDevfileData); err != nil { + if err := r.generateGitops(ctx, ghClient, &component); err != nil { errMsg := fmt.Sprintf("Unable to generate gitops resources for component %v", req.NamespacedName) log.Error(err, errMsg) _ = r.SetGitOpsGeneratedConditionAndUpdateCR(ctx, req, &component, fmt.Errorf("%v: %v", errMsg, err)) @@ -520,7 +521,7 @@ func (r *ComponentReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( // Generate and push the gitops resources, if necessary. if !component.Spec.SkipGitOpsResourceGeneration { - if err := r.generateGitops(ctx, ghClient, &component, hasCompDevfileData); err != nil { + if err := r.generateGitops(ctx, ghClient, &component); err != nil { errMsg := fmt.Sprintf("Unable to generate gitops resources for component %v", req.NamespacedName) log.Error(err, errMsg) _ = r.SetGitOpsGeneratedConditionAndUpdateCR(ctx, req, &component, fmt.Errorf("%v: %v", errMsg, err)) @@ -549,7 +550,7 @@ func (r *ComponentReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( // generateGitops retrieves the necessary information about a Component's gitops repository (URL, branch, context) // and attempts to use the GitOps package to generate gitops resources based on that component -func (r *ComponentReconciler) generateGitops(ctx context.Context, ghClient *github.GitHubClient, component *appstudiov1alpha1.Component, compDevfileData data.DevfileData) error { +func (r *ComponentReconciler) generateGitops(ctx context.Context, ghClient *github.GitHubClient, component *appstudiov1alpha1.Component) error { log := ctrl.LoggerFrom(ctx) gitOpsURL, gitOpsBranch, gitOpsContext, err := util.ProcessGitOpsStatus(component.Status.GitOps, ghClient.Token) @@ -557,64 +558,25 @@ func (r *ComponentReconciler) generateGitops(ctx context.Context, ghClient *gith return err } - // Create a temp folder to create the gitops resources in - tempDir, err := ioutils.CreateTempPath(component.Name, r.AppFS) - if err != nil { - log.Error(err, "unable to create temp directory for GitOps resources due to error") - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) - return fmt.Errorf("unable to create temp directory for GitOps resources due to error: %v", err) - } - - deployAssociatedComponents, err := devfileParser.GetDeployComponents(compDevfileData) - if err != nil { - log.Error(err, "unable to get deploy components") - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) - return err - } - - kubernetesResources, err := devfile.GetResourceFromDevfile(log, compDevfileData, deployAssociatedComponents, component.Name, component.Spec.Application, component.Spec.ContainerImage, "") - if err != nil { - log.Error(err, "unable to get kubernetes resources from the devfile outerloop components") - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) - return err - } - - // Generate and push the gitops resources - mappedGitOpsComponent := util.GetMappedGitOpsComponent(*component, kubernetesResources) - - //add the token name to the metrics. When we add more tokens and rotate, we can determine how evenly distributed the requests are - metrics.ControllerGitRequest.With(prometheus.Labels{"controller": componentName, "tokenName": ghClient.TokenName, "operation": "CloneGenerateAndPush"}).Inc() - err = r.Generator.CloneGenerateAndPush(tempDir, gitOpsURL, mappedGitOpsComponent, r.AppFS, gitOpsBranch, gitOpsContext, false) - if err != nil { - log.Error(err, "unable to generate gitops resources due to error") - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) - return err - } - - //Gitops functions return sanitized error messages - metrics.ControllerGitRequest.With(prometheus.Labels{"controller": componentName, "tokenName": ghClient.TokenName, "operation": "CommitAndPush"}).Inc() - err = r.Generator.CommitAndPush(tempDir, "", gitOpsURL, mappedGitOpsComponent.Name, gitOpsBranch, "Generating GitOps resources") - if err != nil { - log.Error(err, "unable to commit and push gitops resources due to error") - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) - return err - } - - // Get the commit ID for the gitops repository - var commitID string - repoPath := filepath.Join(tempDir, component.Name) - metricsLabel := prometheus.Labels{"controller": componentName, "tokenName": ghClient.TokenName, "operation": "GetCommitIDFromRepo"} - metrics.ControllerGitRequest.With(metricsLabel).Inc() - if commitID, err = r.Generator.GetCommitIDFromRepo(r.AppFS, repoPath); err != nil { - log.Error(err, "") - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) - return err + // Determine if we're using a Kubernetes job for gitops generation, or generating locally + localGitopsGen := (!r.DoGitOpsJob) || (r.AllowLocalGitopsGen && component.Annotations["allowLocalGitopsGen"] == "true") + + if localGitopsGen { + err = gitopsjoblib.GenerateGitopsBase(context.Background(), log, r.Client, *component, ioutils.NewFilesystem(), generate.GitOpsGenParams{ + Generator: r.Generator, + Token: ghClient.Token, + RemoteURL: gitOpsURL, + Branch: gitOpsBranch, + Context: gitOpsContext, + }) + if err != nil { + return err + } + } else { + // ToDo: Implement GitOps generation in Kubernetes jobs } - component.Status.GitOps.CommitID = commitID - - // Remove the temp folder that was created - return r.AppFS.RemoveAll(tempDir) + return nil } // setGitopsStatus adds the necessary gitops info (url, branch, context) to the component CR status diff --git a/controllers/component_controller_unit_test.go b/controllers/component_controller_unit_test.go index d7264056..e4019a74 100644 --- a/controllers/component_controller_unit_test.go +++ b/controllers/component_controller_unit_test.go @@ -23,7 +23,7 @@ import ( "testing" "github.com/golang/mock/gomock" - "github.com/redhat-appstudio/application-service/gitops" + gitopsjoblib "github.com/redhat-appstudio/application-service/gitops-generator/pkg/generate" "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" "github.com/devfile/api/v2/pkg/attributes" @@ -163,13 +163,13 @@ func TestGenerateGitops(t *testing.T) { r := &ComponentReconciler{ Log: ctrl.Log.WithName("controllers").WithName("Component"), GitHubOrg: github.AppStudioAppDataOrg, - Generator: gitops.NewMockGenerator(), + Generator: gitopsjoblib.NewMockGenerator(), Client: fakeClient, GitHubTokenClient: github.MockGitHubTokenClient{}, } // Create a second reconciler for testing error scenarios - errGen := gitops.NewMockGenerator() + errGen := gitopsjoblib.NewMockGenerator() errGen.Errors.Push(errors.New("Fatal error")) errReconciler := &ComponentReconciler{ Log: ctrl.Log.WithName("controllers").WithName("Component"), @@ -557,7 +557,9 @@ func TestGenerateGitops(t *testing.T) { Client: github.GetMockedClient(), TokenName: "some-token", } - err := tt.reconciler.generateGitops(ctx, mockedClient, tt.component, mockDevfileData) + devfileYaml, _ := yaml.Marshal(mockDevfileData) + tt.component.Status.Devfile = string(devfileYaml) + err := tt.reconciler.generateGitops(ctx, mockedClient, tt.component) if (err != nil) != tt.wantErr { t.Errorf("TestGenerateGitops() unexpected error: %v", err) } diff --git a/controllers/start_test_env.go b/controllers/start_test_env.go index 7616ace0..344b61c8 100644 --- a/controllers/start_test_env.go +++ b/controllers/start_test_env.go @@ -20,7 +20,7 @@ import ( "go/build" "path/filepath" - "github.com/redhat-appstudio/application-service/gitops" + gitopsjoblib "github.com/redhat-appstudio/application-service/gitops-generator/pkg/generate" ginkgo "github.com/onsi/ginkgo" gomega "github.com/onsi/gomega" @@ -98,13 +98,14 @@ func setupTestEnv() { gomega.Expect(err).ToNot(gomega.HaveOccurred()) err = (&ComponentReconciler{ - Client: k8sManager.GetClient(), - Scheme: k8sManager.GetScheme(), - Log: ctrl.Log.WithName("controllers").WithName("Component"), - Generator: gitops.NewMockGenerator(), - AppFS: ioutils.NewMemoryFilesystem(), - SPIClient: spi.MockSPIClient{}, - GitHubTokenClient: mockGhTokenClient, + Client: k8sManager.GetClient(), + Scheme: k8sManager.GetScheme(), + Log: ctrl.Log.WithName("controllers").WithName("Component"), + Generator: gitopsjoblib.NewMockGenerator(), + AppFS: ioutils.NewMemoryFilesystem(), + SPIClient: spi.MockSPIClient{}, + GitHubTokenClient: mockGhTokenClient, + AllowLocalGitopsGen: true, }).SetupWithManager(ctx, k8sManager) gomega.Expect(err).ToNot(gomega.HaveOccurred()) @@ -120,12 +121,13 @@ func setupTestEnv() { gomega.Expect(err).ToNot(gomega.HaveOccurred()) err = (&SnapshotEnvironmentBindingReconciler{ - Client: k8sManager.GetClient(), - Scheme: k8sManager.GetScheme(), - Log: ctrl.Log.WithName("controllers").WithName("SnapshotEnvironmentBinding"), - Generator: gitops.NewMockGenerator(), - AppFS: ioutils.NewMemoryFilesystem(), - GitHubTokenClient: mockGhTokenClient, + Client: k8sManager.GetClient(), + Scheme: k8sManager.GetScheme(), + Log: ctrl.Log.WithName("controllers").WithName("SnapshotEnvironmentBinding"), + Generator: gitopsjoblib.NewMockGenerator(), + AppFS: ioutils.NewMemoryFilesystem(), + GitHubTokenClient: mockGhTokenClient, + AllowLocalGitopsGen: true, }).SetupWithManager(ctx, k8sManager) gomega.Expect(err).ToNot(gomega.HaveOccurred()) diff --git a/gitops-generator/.gitignore b/gitops-generator/.gitignore new file mode 100644 index 00000000..d11b364d --- /dev/null +++ b/gitops-generator/.gitignore @@ -0,0 +1,4 @@ +main +component.yaml +seb.yaml +gitops-generator \ No newline at end of file diff --git a/gitops-generator/Dockerfile b/gitops-generator/Dockerfile new file mode 100644 index 00000000..0e81c91e --- /dev/null +++ b/gitops-generator/Dockerfile @@ -0,0 +1,35 @@ +# Build the gitops generator binary +FROM golang:1.18 as builder + +WORKDIR /workspace +# Copy the Go Modules manifests +COPY go.mod go.mod +COPY go.sum go.sum +# cache deps before building and copying source so that we don't need to re-download as much +# and so that source changes don't invalidate our downloaded layer +RUN go mod download + +# Copy the go source +COPY main.go main.go +COPY pkg pkg/ + +# Build +RUN CGO_ENABLED=0 GOOS=linux go build -a -o gitops-generator main.go + + +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.6-751 +RUN microdnf update --setopt=install_weak_deps=0 -y && microdnf install git + +# Set up the non-root workspace and copy over the gitops generator binary and entrypoint script +WORKDIR /workspace +COPY --from=builder /workspace/gitops-generator . + +COPY entrypoint.sh . +RUN chgrp -R 0 /workspace && chmod -R g=u /workspace + +COPY appdata.gitconfig /.gitconfig +RUN chgrp -R 0 /.gitconfig && chmod -R g=u /.gitconfig + +USER 1001 + +ENTRYPOINT ["/workspace/entrypoint.sh"] \ No newline at end of file diff --git a/gitops-generator/appdata.gitconfig b/gitops-generator/appdata.gitconfig new file mode 100644 index 00000000..3e5ab19c --- /dev/null +++ b/gitops-generator/appdata.gitconfig @@ -0,0 +1,3 @@ +[user] + name = AppData Robot + email = 95716864+appstudio-appdata-bot@users.noreply.github.com \ No newline at end of file diff --git a/gitops-generator/entrypoint.sh b/gitops-generator/entrypoint.sh new file mode 100644 index 00000000..32fd0f4d --- /dev/null +++ b/gitops-generator/entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/sh +set -eux + +# Invoke the GitOps generator for the specified option +if [ "$OPERATION" = "generate-base" ]; then + ./gitops-generator --operation "$OPERATION" --namespace "$NAMESPACE" --repoURL "$REPOURL" --component "$RESOURCE" --branch "$BRANCH" --path "$CONTEXT" +else + ./gitops-generator --operation "$OPERATION" --namespace "$NAMESPACE" --seb "$RESOURCE" +fi \ No newline at end of file diff --git a/gitops-generator/go.mod b/gitops-generator/go.mod new file mode 100644 index 00000000..ae1788dd --- /dev/null +++ b/gitops-generator/go.mod @@ -0,0 +1,36 @@ +module github.com/redhat-appstudio/application-service/gitops-generator + +go 1.19 + +require ( + github.com/brianvoe/gofakeit/v6 v6.9.0 + github.com/devfile/api/v2 v2.2.1-alpha.0.20230413012049-a6c32fca0dbd + github.com/devfile/library/v2 v2.2.1-0.20230418160146-e75481b7eebd + github.com/go-logr/logr v1.2.3 + github.com/gofri/go-github-ratelimit v1.0.3-0.20230428184158-a500e14de53f + github.com/golang/mock v1.6.0 + github.com/google/go-github/v52 v52.0.1-0.20230514113659-60429b4ba0ba + github.com/migueleliasweb/go-github-mock v0.0.17 + github.com/mitchellh/go-homedir v1.1.0 + github.com/onsi/ginkgo v1.16.5 + github.com/onsi/gomega v1.24.1 + github.com/openshift-pipelines/pipelines-as-code v0.0.0-20220622161720-2a6007e17200 + github.com/openshift/api v0.0.0-20210503193030-25175d9d392d + github.com/pact-foundation/pact-go v1.7.0 + github.com/prometheus/client_golang v1.14.0 + github.com/redhat-appstudio/application-api v0.0.0-20230704143842-035c661f115f + github.com/redhat-appstudio/application-service/cdq-analysis v0.0.0 + github.com/redhat-appstudio/service-provider-integration-scm-file-retriever v0.8.3 + github.com/redhat-developer/gitops-generator v0.0.0-20230614175323-aff86c6bc55e + github.com/spf13/afero v1.8.0 + github.com/stretchr/testify v1.8.1 + go.uber.org/zap v1.24.0 + golang.org/x/exp v0.0.0-20230206171751-46f607a40771 + golang.org/x/oauth2 v0.7.0 + k8s.io/api v0.26.1 + k8s.io/apimachinery v0.26.1 + k8s.io/client-go v0.26.1 + sigs.k8s.io/controller-runtime v0.14.4 + sigs.k8s.io/yaml v1.3.0 + +) \ No newline at end of file diff --git a/gitops-generator/pkg/devfile/constants.go b/gitops-generator/pkg/devfile/constants.go new file mode 100644 index 00000000..6d14327f --- /dev/null +++ b/gitops-generator/pkg/devfile/constants.go @@ -0,0 +1,48 @@ +// +// Copyright 2023 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package devfile + +const ( + // RouteKey is the key to reference route + RouteKey = "deployment/route" + + // ReplicaKey is the key to reference replica + ReplicaKey = "deployment/replicas" + + // StorageLimitKey is the key to reference storage limit + StorageLimitKey = "deployment/storageLimit" + + // StorageRequestKey is the key to reference storage request + StorageRequestKey = "deployment/storageRequest" + + // CpuLimitKey is the key to reference cpu limit + CpuLimitKey = "deployment/cpuLimit" + + // CpuRequestKey is the key to reference cpu request + CpuRequestKey = "deployment/cpuRequest" + + // MemoryLimitKey is the key to reference memory limit + MemoryLimitKey = "deployment/memoryLimit" + + // MemoryRequestKey is the key to reference memory request + MemoryRequestKey = "deployment/memoryRequest" + + // ContainerImagePortKey is the key to reference container image port + ContainerImagePortKey = "deployment/container-port" + + // ContainerENVKey is the key to reference container environment variables + ContainerENVKey = "deployment/containerENV" +) diff --git a/gitops-generator/pkg/devfile/devfile.go b/gitops-generator/pkg/devfile/devfile.go new file mode 100644 index 00000000..e8bb3085 --- /dev/null +++ b/gitops-generator/pkg/devfile/devfile.go @@ -0,0 +1,560 @@ +// +// Copyright 2021-2023 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package devfile + +import ( + "fmt" + "regexp" + "strconv" + + "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + "github.com/devfile/api/v2/pkg/attributes" + devfilePkg "github.com/devfile/library/v2/pkg/devfile" + "github.com/devfile/library/v2/pkg/devfile/generator" + parser "github.com/devfile/library/v2/pkg/devfile/parser" + data "github.com/devfile/library/v2/pkg/devfile/parser/data" + "github.com/devfile/library/v2/pkg/devfile/parser/data/v2/common" + "golang.org/x/exp/maps" + + "github.com/redhat-appstudio/application-service/pkg/util" + + "github.com/go-logr/logr" + + routev1 "github.com/openshift/api/route/v1" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/api/resource" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "k8s.io/apimachinery/pkg/util/intstr" +) + +// DevfileSrc specifies the src of the Devfile +type DevfileSrc struct { + Data string + URL string + Path string +} + +func GetResourceFromDevfile(log logr.Logger, devfileData data.DevfileData, deployAssociatedComponents map[string]string, compName, appName, image, hostname string) (parser.KubernetesResources, error) { + kubernetesComponentFilter := common.DevfileOptions{ + ComponentOptions: common.ComponentOptions{ + ComponentType: v1alpha2.KubernetesComponentType, + }, + } + kubernetesComponents, err := devfileData.GetComponents(kubernetesComponentFilter) + if err != nil { + return parser.KubernetesResources{}, err + } + + var appendedResources parser.KubernetesResources + k8sLabels := generateK8sLabels(compName, appName) + matchLabels := getMatchLabel(compName) + + if len(kubernetesComponents) == 0 { + return parser.KubernetesResources{}, fmt.Errorf("the devfile has no kubernetes components defined, missing outerloop definition") + } else if len(kubernetesComponents) == 1 && len(deployAssociatedComponents) == 0 { + // only one kubernetes components defined, but no deploy cmd associated + deployAssociatedComponents[kubernetesComponents[0].Name] = "place-holder" + } + for _, component := range kubernetesComponents { + // get kubecomponent referenced by default deploy command + if _, ok := deployAssociatedComponents[component.Name]; ok && component.Kubernetes != nil { + if component.Kubernetes.Inlined != "" { + log.Info(fmt.Sprintf("reading the kubernetes inline from component %s", component.Name)) + src := parser.YamlSrc{ + Data: []byte(component.Kubernetes.Inlined), + } + values, err := parser.ReadKubernetesYaml(src, nil) + if err != nil { + return parser.KubernetesResources{}, err + } + + resources, err := parser.ParseKubernetesYaml(values) + if err != nil { + return parser.KubernetesResources{}, err + } + + var endpointRoutes []routev1.Route + var endpointIngresses []networkingv1.Ingress + for _, endpoint := range component.Kubernetes.Endpoints { + if endpoint.Exposure != v1alpha2.NoneEndpointExposure && endpoint.Exposure != v1alpha2.InternalEndpointExposure { + var isSecure bool + if endpoint.Secure != nil { + isSecure = *endpoint.Secure + } + + ingressEndpoint, err := GetIngressFromEndpoint(endpoint.Name, compName, fmt.Sprintf("%d", endpoint.TargetPort), endpoint.Path, isSecure, endpoint.Annotations, hostname) + if err != nil { + return parser.KubernetesResources{}, err + } + endpointIngresses = append(endpointIngresses, ingressEndpoint) + + endpointRoutes = append(endpointRoutes, GetRouteFromEndpoint(endpoint.Name, compName, fmt.Sprintf("%d", endpoint.TargetPort), endpoint.Path, isSecure, endpoint.Annotations)) + } + } + // attempt to always merge the devfile endpoints to the list first as it has priority + resources.Routes = append(endpointRoutes, resources.Routes...) + resources.Ingresses = append(endpointIngresses, resources.Ingresses...) + + // update for port + currentPort := int(component.Attributes.GetNumber(ContainerImagePortKey, &err)) + if err != nil { + if _, ok := err.(*attributes.KeyNotFoundError); !ok { + return parser.KubernetesResources{}, err + } + } + + // update for ENV + currentENV := []corev1.EnvVar{} + err = component.Attributes.GetInto(ContainerENVKey, ¤tENV) + if err != nil { + if _, ok := err.(*attributes.KeyNotFoundError); !ok { + return parser.KubernetesResources{}, err + } + } + + if len(resources.Deployments) > 0 { + // update for replica + currentReplica := int32(component.Attributes.GetNumber(ReplicaKey, &err)) + if err != nil { + if _, ok := err.(*attributes.KeyNotFoundError); !ok { + return parser.KubernetesResources{}, err + } + } + + // Set the RevisionHistoryLimit for all Deployments to 0, if it's unset + // If set, leave it alone + for i := range resources.Deployments { + if resources.Deployments[i].Spec.RevisionHistoryLimit == nil { + resources.Deployments[i].Spec.RevisionHistoryLimit = &util.RevisionHistoryLimit + } + } + + // replace the deployment metadata.name to use the component name + resources.Deployments[0].ObjectMeta.Name = compName + + // generate and append the deployment labels with the hc & ha information + if resources.Deployments[0].ObjectMeta.Labels != nil { + maps.Copy(resources.Deployments[0].ObjectMeta.Labels, k8sLabels) + } else { + resources.Deployments[0].ObjectMeta.Labels = k8sLabels + } + if resources.Deployments[0].Spec.Selector != nil { + if resources.Deployments[0].Spec.Selector.MatchLabels != nil { + maps.Copy(resources.Deployments[0].Spec.Selector.MatchLabels, matchLabels) + } else { + resources.Deployments[0].Spec.Selector.MatchLabels = matchLabels + } + } else { + resources.Deployments[0].Spec.Selector = &v1.LabelSelector{ + MatchLabels: matchLabels, + } + } + if resources.Deployments[0].Spec.Template.ObjectMeta.Labels != nil { + maps.Copy(resources.Deployments[0].Spec.Template.ObjectMeta.Labels, matchLabels) + } else { + resources.Deployments[0].Spec.Template.ObjectMeta.Labels = matchLabels + } + + if currentReplica > 0 { + resources.Deployments[0].Spec.Replicas = ¤tReplica + } + + if len(resources.Deployments[0].Spec.Template.Spec.Containers) > 0 { + if image != "" { + resources.Deployments[0].Spec.Template.Spec.Containers[0].Image = image + } + + if currentPort > 0 { + containerPort := corev1.ContainerPort{ + ContainerPort: int32(currentPort), + } + + isPresent := false + for _, port := range resources.Deployments[0].Spec.Template.Spec.Containers[0].Ports { + if port.ContainerPort == containerPort.ContainerPort { + isPresent = true + break + } + } + + if !isPresent { + resources.Deployments[0].Spec.Template.Spec.Containers[0].Ports = append(resources.Deployments[0].Spec.Template.Spec.Containers[0].Ports, containerPort) + } + + if resources.Deployments[0].Spec.Template.Spec.Containers[0].ReadinessProbe != nil && resources.Deployments[0].Spec.Template.Spec.Containers[0].ReadinessProbe.ProbeHandler.TCPSocket != nil { + resources.Deployments[0].Spec.Template.Spec.Containers[0].ReadinessProbe.ProbeHandler.TCPSocket.Port.IntVal = int32(currentPort) + } + + if resources.Deployments[0].Spec.Template.Spec.Containers[0].LivenessProbe != nil && resources.Deployments[0].Spec.Template.Spec.Containers[0].LivenessProbe.ProbeHandler.HTTPGet != nil { + resources.Deployments[0].Spec.Template.Spec.Containers[0].LivenessProbe.ProbeHandler.HTTPGet.Port.IntVal = int32(currentPort) + } + } + + for _, devfileEnv := range currentENV { + isPresent := false + for i, containerEnv := range resources.Deployments[0].Spec.Template.Spec.Containers[0].Env { + if containerEnv.Name == devfileEnv.Name { + isPresent = true + resources.Deployments[0].Spec.Template.Spec.Containers[0].Env[i].Value = devfileEnv.Value + } + } + + if !isPresent { + resources.Deployments[0].Spec.Template.Spec.Containers[0].Env = append(resources.Deployments[0].Spec.Template.Spec.Containers[0].Env, devfileEnv) + } + } + + // Update for limits + cpuLimit := component.Attributes.GetString(CpuLimitKey, &err) + if err != nil { + if _, ok := err.(*attributes.KeyNotFoundError); !ok { + return parser.KubernetesResources{}, err + } + } + + memoryLimit := component.Attributes.GetString(MemoryLimitKey, &err) + if err != nil { + if _, ok := err.(*attributes.KeyNotFoundError); !ok { + return parser.KubernetesResources{}, err + } + } + + storageLimit := component.Attributes.GetString(StorageLimitKey, &err) + if err != nil { + if _, ok := err.(*attributes.KeyNotFoundError); !ok { + return parser.KubernetesResources{}, err + } + } + + containerLimits := resources.Deployments[0].Spec.Template.Spec.Containers[0].Resources.Limits + if len(containerLimits) == 0 { + containerLimits = make(corev1.ResourceList) + } + + if cpuLimit != "" && cpuLimit != "0" { + cpuLimitQuantity, err := resource.ParseQuantity(cpuLimit) + if err != nil { + return parser.KubernetesResources{}, err + } + containerLimits[corev1.ResourceCPU] = cpuLimitQuantity + } + + if memoryLimit != "" && memoryLimit != "0" { + memoryLimitQuantity, err := resource.ParseQuantity(memoryLimit) + if err != nil { + return parser.KubernetesResources{}, err + } + containerLimits[corev1.ResourceMemory] = memoryLimitQuantity + } + + if storageLimit != "" && storageLimit != "0" { + storageLimitQuantity, err := resource.ParseQuantity(storageLimit) + if err != nil { + return parser.KubernetesResources{}, err + } + containerLimits[corev1.ResourceStorage] = storageLimitQuantity + } + + resources.Deployments[0].Spec.Template.Spec.Containers[0].Resources.Limits = containerLimits + + // Update for requests + cpuRequest := component.Attributes.GetString(CpuRequestKey, &err) + if err != nil { + if _, ok := err.(*attributes.KeyNotFoundError); !ok { + return parser.KubernetesResources{}, err + } + } + + memoryRequest := component.Attributes.GetString(MemoryRequestKey, &err) + if err != nil { + if _, ok := err.(*attributes.KeyNotFoundError); !ok { + return parser.KubernetesResources{}, err + } + } + + storageRequest := component.Attributes.GetString(StorageRequestKey, &err) + if err != nil { + if _, ok := err.(*attributes.KeyNotFoundError); !ok { + return parser.KubernetesResources{}, err + } + } + + containerRequests := resources.Deployments[0].Spec.Template.Spec.Containers[0].Resources.Requests + if len(containerRequests) == 0 { + containerRequests = make(corev1.ResourceList) + } + + if cpuRequest != "" && cpuRequest != "0" { + cpuRequestQuantity, err := resource.ParseQuantity(cpuRequest) + if err != nil { + return parser.KubernetesResources{}, err + } + containerRequests[corev1.ResourceCPU] = cpuRequestQuantity + } + + if memoryRequest != "" && memoryRequest != "0" { + memoryRequestQuantity, err := resource.ParseQuantity(memoryRequest) + if err != nil { + return parser.KubernetesResources{}, err + } + containerRequests[corev1.ResourceMemory] = memoryRequestQuantity + } + + if storageRequest != "" && storageRequest != "0" { + storageRequestQuantity, err := resource.ParseQuantity(storageRequest) + if err != nil { + return parser.KubernetesResources{}, err + } + containerRequests[corev1.ResourceStorage] = storageRequestQuantity + } + + resources.Deployments[0].Spec.Template.Spec.Containers[0].Resources.Requests = containerRequests + } + } + + if len(resources.Services) > 0 { + // replace the service metadata.name to use the component name + resources.Services[0].ObjectMeta.Name = compName + + // generate and append the service labels with the hc & ha information + if resources.Services[0].ObjectMeta.Labels != nil { + maps.Copy(resources.Services[0].ObjectMeta.Labels, k8sLabels) + } else { + resources.Services[0].ObjectMeta.Labels = k8sLabels + } + if resources.Services[0].Spec.Selector != nil { + maps.Copy(resources.Services[0].Spec.Selector, matchLabels) + } else { + resources.Services[0].Spec.Selector = matchLabels + } + + if currentPort > 0 { + servicePort := corev1.ServicePort{ + Port: int32(currentPort), + TargetPort: intstr.FromInt(currentPort), + } + + isPresent := false + for _, port := range resources.Services[0].Spec.Ports { + if port.Port == servicePort.Port { + isPresent = true + break + } + } + + if !isPresent { + resources.Services[0].Spec.Ports = append(resources.Services[0].Spec.Ports, servicePort) + } + } + } + if len(resources.Routes) > 0 { + // replace the route metadata.name to use the component name + // Trim the route name if needed + routeName := compName + if len(routeName) >= 30 { + routeName = routeName[0:25] + util.GetRandomString(4, true) + } + + resources.Routes[0].ObjectMeta.Name = routeName + + // generate and append the route labels with the hc & ha information + if resources.Routes[0].ObjectMeta.Labels != nil { + maps.Copy(resources.Routes[0].ObjectMeta.Labels, k8sLabels) + } else { + resources.Routes[0].ObjectMeta.Labels = k8sLabels + } + + if currentPort > 0 { + if resources.Routes[0].Spec.Port == nil { + resources.Routes[0].Spec.Port = &routev1.RoutePort{} + } + resources.Routes[0].Spec.Port.TargetPort = intstr.FromInt(currentPort) + // Update for route + route := component.Attributes.GetString(RouteKey, &err) + if err != nil { + if _, ok := err.(*attributes.KeyNotFoundError); !ok { + return parser.KubernetesResources{}, err + } + } + + if route != "" { + resources.Routes[0].Spec.Host = route + } + } + } + if len(resources.Ingresses) > 0 { + // replace the ingress metadata.name to use the component name + ingressName := compName + + resources.Ingresses[0].ObjectMeta.Name = ingressName + + // generate and append the ingress labels with the hc & ha information + if resources.Ingresses[0].ObjectMeta.Labels != nil { + maps.Copy(resources.Ingresses[0].ObjectMeta.Labels, k8sLabels) + } else { + resources.Ingresses[0].ObjectMeta.Labels = k8sLabels + } + if currentPort > 0 { + if len(resources.Ingresses[0].Spec.Rules) > 0 { + if resources.Ingresses[0].Spec.Rules[0].HTTP != nil && len(resources.Ingresses[0].Spec.Rules[0].HTTP.Paths) > 0 { + if resources.Ingresses[0].Spec.Rules[0].HTTP.Paths[0].Backend.Service != nil { + resources.Ingresses[0].Spec.Rules[0].HTTP.Paths[0].Backend.Service.Port.Number = int32(currentPort) + } + } + } + } + } + + appendedResources.Deployments = append(appendedResources.Deployments, resources.Deployments...) + appendedResources.Services = append(appendedResources.Services, resources.Services...) + appendedResources.Routes = append(appendedResources.Routes, resources.Routes...) + appendedResources.Ingresses = append(appendedResources.Ingresses, resources.Ingresses...) + appendedResources.Others = append(appendedResources.Others, resources.Others...) + } else { + log.Info(fmt.Sprintf("Kubernetes Component %s did not have an inline content, gitOps resources may be auto generated", component.Name)) + } + } + } + + return appendedResources, err +} + +// GetIngressFromEndpoint gets an ingress resource from the devfile endpoint information +func GetIngressFromEndpoint(name, serviceName, port, path string, secure bool, annotations map[string]string, hostname string) (networkingv1.Ingress, error) { + + if path == "" { + path = "/" + } + + implementationSpecific := networkingv1.PathTypeImplementationSpecific + + portNumber, err := strconv.ParseInt(port, 10, 32) + if err != nil { + return networkingv1.Ingress{}, err + } + + ingress := networkingv1.Ingress{ + TypeMeta: generator.GetTypeMeta("Ingress", "networking.k8s.io/v1"), + ObjectMeta: generator.GetObjectMeta(name, "", nil, annotations), + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + Host: hostname, + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: path, + PathType: &implementationSpecific, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: serviceName, + Port: networkingv1.ServiceBackendPort{ + Number: int32(portNumber), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + return ingress, nil +} + +// GetRouteFromEndpoint gets the route resource +func GetRouteFromEndpoint(name, serviceName, port, path string, secure bool, annotations map[string]string) routev1.Route { + + if path == "" { + path = "/" + } + + routeParams := generator.RouteParams{ + ObjectMeta: generator.GetObjectMeta(name, "", nil, nil), + TypeMeta: generator.GetTypeMeta("Route", "route.openshift.io/v1"), + RouteSpecParams: generator.RouteSpecParams{ + ServiceName: serviceName, + PortNumber: intstr.FromString(port), + Path: path, + Secure: secure, + }, + } + + return *generator.GetRoute(v1alpha2.Endpoint{Annotations: annotations}, routeParams) +} + +// ParseDevfile calls the devfile library's parse and returns the devfile data. +// Provide either a Data src or the URL src +func ParseDevfile(src DevfileSrc) (data.DevfileData, error) { + + httpTimeout := 10 + convert := true + parserArgs := parser.ParserArgs{ + HTTPTimeout: &httpTimeout, + ConvertKubernetesContentInUri: &convert, + } + + if src.Data != "" { + parserArgs.Data = []byte(src.Data) + } else if src.URL != "" { + parserArgs.URL = src.URL + } else if src.Path != "" { + parserArgs.Path = src.Path + } else { + return nil, fmt.Errorf("cannot parse devfile without a src") + } + devfileObj, _, err := devfilePkg.ParseDevfileAndValidate(parserArgs) + return devfileObj.Data, err +} + +func generateK8sLabels(name, application string) map[string]string { + return map[string]string{ + "app.kubernetes.io/name": name, + "app.kubernetes.io/instance": name, + "app.kubernetes.io/part-of": application, + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/created-by": "application-service", + } +} + +// GetMatchLabel returns the label selector that will be used to tie deployments, services, and pods together +// For cleanliness, using just one unique label from the generateK8sLabels function +func getMatchLabel(name string) map[string]string { + return map[string]string{ + "app.kubernetes.io/instance": name, + } +} + +// GetIngressHostName gets the ingress host name from the component name, namepsace and ingress domain +func GetIngressHostName(componentName, namespace, ingressDomain string) (string, error) { + + regexString := `[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*` + ingressHostRegex := regexp.MustCompile(regexString) + + host := fmt.Sprintf("%s-%s.%s", componentName, namespace, ingressDomain) + + if !ingressHostRegex.MatchString(host) { + return "", fmt.Errorf("hostname %s should match regex %s", host, regexString) + } + + return host, nil +} diff --git a/gitops-generator/pkg/devfile/devfile_test.go b/gitops-generator/pkg/devfile/devfile_test.go new file mode 100644 index 00000000..e61b8d4c --- /dev/null +++ b/gitops-generator/pkg/devfile/devfile_test.go @@ -0,0 +1,3114 @@ +package devfile + +import ( + "io/ioutil" + "net" + "net/http" + "net/http/httptest" + "os" + "path" + "reflect" + "strings" + "testing" + + "github.com/devfile/api/v2/pkg/attributes" + "github.com/devfile/api/v2/pkg/devfile" + "github.com/devfile/library/pkg/devfile/parser/data" + v2 "github.com/devfile/library/pkg/devfile/parser/data/v2" + parser "github.com/devfile/library/v2/pkg/devfile/parser" + networkingv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/stretchr/testify/assert" + + routev1 "github.com/openshift/api/route/v1" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + ctrl "sigs.k8s.io/controller-runtime" +) + +func TestGetIngressHostName(t *testing.T) { + + tests := []struct { + name string + componentName string + namespace string + ingressDomain string + wantHostName string + wantErr bool + }{ + { + name: "all string present", + componentName: "my-component", + namespace: "test", + ingressDomain: "domain.example.com", + wantHostName: "my-component-test.domain.example.com", + }, + { + name: "Capitalized component name should be ok", + componentName: "my-Component", + namespace: "test", + ingressDomain: "domain.example.com", + wantHostName: "my-Component-test.domain.example.com", + }, + { + name: "invalid char in string", + componentName: "&", + namespace: "$", + ingressDomain: "$", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + gotHostName, err := GetIngressHostName(tt.componentName, tt.namespace, tt.ingressDomain) + if !tt.wantErr && err != nil { + t.Errorf("Unexpected err: %+v", err) + } else if tt.wantErr && err == nil { + t.Errorf("Expected error but got nil") + } else if !reflect.DeepEqual(tt.wantHostName, gotHostName) { + t.Errorf("Expected: %+v, \nGot: %+v", tt.wantHostName, gotHostName) + } + }) + } +} + +func TestGetResourceFromDevfile(t *testing.T) { + + weight := int32(100) + + kubernetesInlinedDevfile := ` +commands: +- apply: + component: image-build + id: build-image +- apply: + component: kubernetes-deploy + id: deployk8s +- composite: + commands: + - build-image + - deployk8s + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- attributes: + api.devfile.io/k8sLikeComponent-originalURI: deploy.yaml + deployment/container-port: 5566 + deployment/containerENV: + - name: FOO + value: foo11 + - name: BAR + value: bar11 + deployment/cpuLimit: "2" + deployment/cpuRequest: 701m + deployment/memoryLimit: 500Mi + deployment/memoryRequest: 401Mi + deployment/replicas: 5 + deployment/route: route111222 + kubernetes: + deployByDefault: false + endpoints: + - name: http-8081 + path: / + secure: false + targetPort: 8081 + inlined: |- + apiVersion: apps/v1 + kind: Deployment + metadata: + creationTimestamp: null + labels: + maysun: test + name: deploy-sample + spec: + replicas: 1 + selector: {} + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/instance: component-sample + spec: + containers: + - env: + - name: FOO + value: foo1 + - name: BARBAR + value: bar1 + image: quay.io/redhat-appstudio/user-workload:application-service-system-component-sample + imagePullPolicy: Always + livenessProbe: + httpGet: + path: / + port: 1111 + initialDelaySeconds: 10 + periodSeconds: 10 + name: container-image + ports: + - containerPort: 1111 + readinessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + tcpSocket: + port: 1111 + resources: + limits: + cpu: "2" + memory: 500Mi + requests: + cpu: 700m + memory: 400Mi + status: {} + --- + apiVersion: apps/v1 + kind: Deployment + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + maysun: test + name: deploy-sample-2 + spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: component-sample + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/instance: component-sample + spec: + containers: + - env: + - name: FOO + value: foo1 + - name: BAR + value: bar1 + image: quay.io/redhat-appstudio/user-workload:application-service-system-component-sample + imagePullPolicy: Always + livenessProbe: + httpGet: + path: / + port: 1111 + initialDelaySeconds: 10 + periodSeconds: 10 + name: container-image + ports: + - containerPort: 1111 + readinessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + tcpSocket: + port: 1111 + resources: + limits: + cpu: "2" + memory: 500Mi + storage: 400Mi + requests: + cpu: 700m + memory: 400Mi + storage: 200Mi + status: {} + --- + apiVersion: v1 + kind: Service + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + maysun: test + name: service-sample + spec: + ports: + - port: 1111 + targetPort: 1111 + selector: + app.kubernetes.io/instance: component-sample + status: + loadBalancer: {} + --- + apiVersion: v1 + kind: Service + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + maysun: test + name: service-sample-2 + spec: + ports: + - port: 1111 + targetPort: 1111 + selector: + app.kubernetes.io/instance: component-sample + status: + loadBalancer: {} + --- + apiVersion: route.openshift.io/v1 + kind: Route + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + maysun: test + name: route-sample + spec: + host: route111 + port: + targetPort: 1111 + tls: + insecureEdgeTerminationPolicy: Redirect + termination: edge + to: + kind: Service + name: component-sample + weight: 100 + status: {} + --- + apiVersion: route.openshift.io/v1 + kind: Route + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + maysun: test + name: route-sample-2 + spec: + host: route111 + port: + targetPort: 1111 + tls: + insecureEdgeTerminationPolicy: Redirect + termination: edge + to: + kind: Service + name: component-sample + weight: 100 + status: {} + --- + apiVersion: networking.k8s.io/v1 + kind: Ingress + metadata: + name: ingress-sample + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + maysun: test + spec: + ingressClassName: nginx-example + rules: + - http: + paths: + - path: /testpath + pathType: Prefix + backend: + service: + name: test + port: + number: 80 + --- + apiVersion: networking.k8s.io/v1 + kind: Ingress + metadata: + name: ingress-sample-2 + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + maysun: test + spec: + ingressClassName: nginx-example + rules: + - http: + paths: + - path: /testpath + pathType: Prefix + backend: + service: + name: test + port: + number: 80 + --- + apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: pvc-sample + labels: + maysun: test + spec: + accessModes: + - ReadWriteOnce + volumeMode: Filesystem + resources: + requests: + storage: 8Gi + storageClassName: slow + selector: + matchLabels: + release: "stable" + matchExpressions: + - {key: environment, operator: In, values: [dev]} + --- + apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: pvc-sample-2 + labels: + maysun: test + spec: + accessModes: + - ReadWriteOnce + volumeMode: Filesystem + resources: + requests: + storage: 8Gi + storageClassName: slow + selector: + matchLabels: + release: "stable" + matchExpressions: + - {key: environment, operator: In, values: [dev]} + name: kubernetes-deploy +metadata: + name: java-springboot +schemaVersion: 2.2.0` + + kubernetesInlinedDevfileIngress := ` +commands: +- apply: + component: image-build + id: build-image +- apply: + component: kubernetes-deploy + id: deployk8s +- composite: + commands: + - build-image + - deployk8s + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- attributes: + api.devfile.io/k8sLikeComponent-originalURI: deploy.yaml + deployment/container-port: 5566 + deployment/containerENV: + - name: FOO + value: foo11 + - name: BAR + value: bar11 + deployment/cpuLimit: "2" + deployment/cpuRequest: 701m + deployment/memoryLimit: 500Mi + deployment/memoryRequest: 401Mi + deployment/replicas: 5 + deployment/route: route111222 + deployment/storageLimit: 400Mi + deployment/storageRequest: 201Mi + kubernetes: + deployByDefault: false + inlined: |- + apiVersion: networking.k8s.io/v1 + kind: Ingress + metadata: + name: ingress-sample + labels: + test: test + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + test: yes + spec: + rules: + - host: "foo.bar.com" + http: + paths: + - path: /testpath + pathType: ImplementationSpecific + backend: + service: + name: test + port: + number: 80 + status: {} + name: kubernetes-deploy +metadata: + name: java-springboot +schemaVersion: 2.2.0` + + kubernetesInlinedDevfileRoute := ` +commands: +- apply: + component: image-build + id: build-image +- apply: + component: kubernetes-deploy + id: deployk8s +- composite: + commands: + - build-image + - deployk8s + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- attributes: + api.devfile.io/k8sLikeComponent-originalURI: deploy.yaml + deployment/container-port: 5566 + deployment/containerENV: + - name: FOO + value: foo11 + - name: BAR + value: bar11 + deployment/cpuLimit: "2" + deployment/cpuRequest: 701m + deployment/memoryLimit: 500Mi + deployment/memoryRequest: 401Mi + deployment/replicas: 5 + deployment/route: route111222 + deployment/storageLimit: 400Mi + deployment/storageRequest: 201Mi + kubernetes: + deployByDefault: false + inlined: |- + apiVersion: route.openshift.io/v1 + kind: Route + metadata: + creationTimestamp: null + name: route-sample-2 + labels: + test: test + spec: + host: route111 + port: + targetPort: 1111 + tls: + insecureEdgeTerminationPolicy: Redirect + termination: edge + to: + kind: Service + name: component-sample + weight: 100 + status: {} + name: kubernetes-deploy +metadata: + name: java-springboot +schemaVersion: 2.2.0` + + kubernetesInlinedDevfileRouteNoTargetPort := ` +commands: +- apply: + component: image-build + id: build-image +- apply: + component: kubernetes-deploy + id: deployk8s +- composite: + commands: + - build-image + - deployk8s + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- attributes: + api.devfile.io/k8sLikeComponent-originalURI: deploy.yaml + deployment/container-port: 5566 + deployment/containerENV: + - name: FOO + value: foo11 + - name: BAR + value: bar11 + deployment/cpuLimit: "2" + deployment/cpuRequest: 701m + deployment/memoryLimit: 500Mi + deployment/memoryRequest: 401Mi + deployment/replicas: 5 + deployment/route: route111222 + deployment/storageLimit: 400Mi + deployment/storageRequest: 201Mi + kubernetes: + deployByDefault: false + inlined: |- + apiVersion: route.openshift.io/v1 + kind: Route + metadata: + creationTimestamp: null + name: route-sample-2 + labels: + test: test + spec: + host: route111 + tls: + insecureEdgeTerminationPolicy: Redirect + termination: edge + to: + kind: Service + name: component-sample + weight: 100 + status: {} + name: kubernetes-deploy +metadata: + name: java-springboot +schemaVersion: 2.2.0` + + kubernetesInlinedDevfileSvc := ` +commands: +- apply: + component: image-build + id: build-image +- apply: + component: kubernetes-deploy + id: deployk8s +- composite: + commands: + - build-image + - deployk8s + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- attributes: + api.devfile.io/k8sLikeComponent-originalURI: deploy.yaml + deployment/container-port: 1111 + kubernetes: + deployByDefault: false + inlined: |- + apiVersion: v1 + kind: Service + metadata: + creationTimestamp: null + name: service-sample + spec: + ports: + - port: 1111 + targetPort: 1111 + status: + loadBalancer: {} + name: kubernetes-deploy +metadata: + name: java-springboot +schemaVersion: 2.2.0` + + kubernetesInlinedDevfileDeploy := ` +commands: +- apply: + component: image-build + id: build-image +- apply: + component: kubernetes-deploy + id: deployk8s +- composite: + commands: + - build-image + - deployk8s + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- attributes: + api.devfile.io/k8sLikeComponent-originalURI: deploy.yaml + deployment/container-port: 1111 + deployment/storageLimit: 401Mi + deployment/storageRequest: 201Mi + kubernetes: + deployByDefault: false + endpoints: + - name: http-8081 + path: / + secure: false + targetPort: 8081 + inlined: |- + apiVersion: apps/v1 + kind: Deployment + metadata: + creationTimestamp: null + name: deploy-sample + spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: component-sample + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/instance: component-sample + spec: + containers: + - env: + - name: FOOFOO + value: foo1 + - name: BARBAR + value: bar1 + image: quay.io/redhat-appstudio/user-workload:application-service-system-component-sample + imagePullPolicy: Always + livenessProbe: + httpGet: + path: / + port: 1111 + initialDelaySeconds: 10 + periodSeconds: 10 + name: container-image + ports: + - containerPort: 1111 + readinessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + tcpSocket: + port: 1111 + resources: + limits: + cpu: "2" + memory: 500Mi + storage: 400Mi + requests: + cpu: 700m + memory: 400Mi + storage: 200Mi + status: {} + name: kubernetes-deploy +metadata: + name: java-springboot +schemaVersion: 2.2.0` + + kubernetesInlinedDevfileSeparatedKubeComps := ` +commands: +- apply: + component: image-build + id: build-image +- apply: + component: kubernetes-deploy + id: deployk8s +- apply: + component: kubernetes-svc + id: svck8s +- composite: + commands: + - build-image + - deployk8s + - svck8s + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- attributes: + api.devfile.io/k8sLikeComponent-originalURI: deploy.yaml + deployment/container-port: 1111 + deployment/storageLimit: 401Mi + deployment/storageRequest: 201Mi + kubernetes: + deployByDefault: false + endpoints: + - name: http-8081 + path: / + secure: false + targetPort: 8081 + inlined: |- + apiVersion: apps/v1 + kind: Deployment + metadata: + creationTimestamp: null + name: deploy-sample + spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: component-sample + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/instance: component-sample + spec: + containers: + - env: + - name: FOOFOO + value: foo1 + - name: BARBAR + value: bar1 + image: quay.io/redhat-appstudio/user-workload:application-service-system-component-sample + imagePullPolicy: Always + livenessProbe: + httpGet: + path: / + port: 1111 + initialDelaySeconds: 10 + periodSeconds: 10 + name: container-image + ports: + - containerPort: 1111 + readinessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + tcpSocket: + port: 1111 + resources: + limits: + cpu: "2" + memory: 500Mi + storage: 400Mi + requests: + cpu: 700m + memory: 400Mi + storage: 200Mi + status: {} + name: kubernetes-deploy +- attributes: + deployment/container-port: 1111 + kubernetes: + deployByDefault: false + inlined: |- + apiVersion: v1 + kind: Service + metadata: + creationTimestamp: null + name: service-sample + spec: + ports: + - port: 1111 + targetPort: 1111 + status: + loadBalancer: {} + name: kubernetes-svc +metadata: + name: java-springboot +schemaVersion: 2.2.0` + + kubernetesInlinedDevfileSeparatedKubeCompsRevHistory := ` +commands: +- apply: + component: image-build + id: build-image +- apply: + component: kubernetes-deploy + id: deployk8s +- apply: + component: kubernetes-svc + id: svck8s +- composite: + commands: + - build-image + - deployk8s + - svck8s + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- attributes: + api.devfile.io/k8sLikeComponent-originalURI: deploy.yaml + deployment/container-port: 1111 + deployment/storageLimit: 401Mi + deployment/storageRequest: 201Mi + kubernetes: + deployByDefault: false + endpoints: + - name: http-8081 + path: / + secure: false + targetPort: 8081 + inlined: |- + apiVersion: apps/v1 + kind: Deployment + metadata: + creationTimestamp: null + name: deploy-sample + spec: + revisionHistoryLimit: 5 + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: component-sample + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/instance: component-sample + spec: + containers: + - env: + - name: FOOFOO + value: foo1 + - name: BARBAR + value: bar1 + image: quay.io/redhat-appstudio/user-workload:application-service-system-component-sample + imagePullPolicy: Always + livenessProbe: + httpGet: + path: / + port: 1111 + initialDelaySeconds: 10 + periodSeconds: 10 + name: container-image + ports: + - containerPort: 1111 + readinessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + tcpSocket: + port: 1111 + resources: + limits: + cpu: "2" + memory: 500Mi + storage: 400Mi + requests: + cpu: 700m + memory: 400Mi + storage: 200Mi + status: {} + name: kubernetes-deploy +- attributes: + deployment/container-port: 1111 + kubernetes: + deployByDefault: false + inlined: |- + apiVersion: v1 + kind: Service + metadata: + creationTimestamp: null + name: service-sample + spec: + ports: + - port: 1111 + targetPort: 1111 + status: + loadBalancer: {} + name: kubernetes-svc +metadata: + name: java-springboot +schemaVersion: 2.2.0` + + kubernetesInlinedDevfileRouteHostMissing := ` +commands: +- apply: + component: image-build + id: build-image +- apply: + component: kubernetes-deploy + id: deployk8s +- composite: + commands: + - build-image + - deployk8s + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- attributes: + api.devfile.io/k8sLikeComponent-originalURI: deploy.yaml + deployment/container-port: 5566 + kubernetes: + deployByDefault: false + endpoints: + - name: http-8081 + path: / + secure: false + targetPort: 8081 + inlined: |- + apiVersion: apps/v1 + kind: Deployment + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + maysun: test + name: deploy-sample + spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: component-sample + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/instance: component-sample + spec: + containers: + - env: + - name: FOOFOO + value: foo1 + - name: BARBAR + value: bar1 + image: quay.io/redhat-appstudio/user-workload:application-service-system-component-sample + imagePullPolicy: Always + livenessProbe: + httpGet: + path: / + port: 1111 + initialDelaySeconds: 10 + periodSeconds: 10 + name: container-image + ports: + - containerPort: 1111 + readinessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + tcpSocket: + port: 1111 + resources: + limits: + cpu: "2" + memory: 500Mi + storage: 400Mi + requests: + cpu: 700m + memory: 400Mi + storage: 200Mi + status: {} + name: kubernetes-deploy +metadata: + name: java-springboot +schemaVersion: 2.2.0` + + kubernetesWithoutInline := ` +commands: +- apply: + component: image-build + id: build-image +- apply: + component: kubernetes-deploy + id: deployk8s +- composite: + commands: + - build-image + - deployk8s + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- kubernetes: + deployByDefault: false + uri: "" + name: kubernetes-deploy +metadata: + name: java-springboot +schemaVersion: 2.2.0` + + kubernetesInlinedDevfileErrCase_BadMemoryLimit := ` +commands: +- apply: + component: image-build + id: build-image +- apply: + component: kubernetes-deploy + id: deployk8s +- composite: + commands: + - build-image + - deployk8s + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- attributes: + deployment/memoryLimit: abc + kubernetes: + deployByDefault: false + inlined: |- + apiVersion: apps/v1 + kind: Deployment + metadata: + labels: + maysun: test + name: deploy-sample + spec: + template: + spec: + containers: + - image: quay.io/redhat-appstudio/user-workload:application-service-system-component-sample + name: container-image + name: kubernetes-deploy +metadata: + language: Java + name: java-springboot + projectType: springboot + version: 1.2.1 +schemaVersion: 2.2.0` + + kubernetesInlinedDevfileErrCase_BadStorageLimit := ` +commands: +- apply: + component: image-build + id: build-image +- apply: + component: kubernetes-deploy + id: deployk8s +- composite: + commands: + - build-image + - deployk8s + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- attributes: + deployment/storageLimit: abc + kubernetes: + deployByDefault: false + inlined: |- + apiVersion: apps/v1 + kind: Deployment + metadata: + labels: + maysun: test + name: deploy-sample + spec: + template: + spec: + containers: + - image: quay.io/redhat-appstudio/user-workload:application-service-system-component-sample + name: container-image + name: kubernetes-deploy +metadata: + name: java-springboot +schemaVersion: 2.2.0` + + kubernetesInlinedDevfileErrCase_BadCPULimit := ` +commands: +- apply: + component: image-build + id: build-image +- apply: + component: kubernetes-deploy + id: deployk8s +- composite: + commands: + - build-image + - deployk8s + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- attributes: + deployment/cpuLimit: "abc" + kubernetes: + deployByDefault: false + inlined: |- + apiVersion: apps/v1 + kind: Deployment + metadata: + labels: + maysun: test + name: deploy-sample + spec: + template: + spec: + containers: + - image: quay.io/redhat-appstudio/user-workload:application-service-system-component-sample + name: container-image + name: kubernetes-deploy +metadata: + language: Java + name: java-springboot + projectType: springboot + version: 1.2.1 +schemaVersion: 2.2.0` + + kubernetesInlinedDevfileErrCase_BadMemoryRequest := ` +commands: +- apply: + component: image-build + id: build-image +- apply: + component: kubernetes-deploy + id: deployk8s +- composite: + commands: + - build-image + - deployk8s + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- attributes: + deployment/memoryRequest: abc + kubernetes: + deployByDefault: false + inlined: |- + apiVersion: apps/v1 + kind: Deployment + metadata: + labels: + maysun: test + name: deploy-sample + spec: + template: + spec: + containers: + - image: quay.io/redhat-appstudio/user-workload:application-service-system-component-sample + name: container-image + name: kubernetes-deploy +metadata: + language: Java + name: java-springboot + projectType: springboot + version: 1.2.1 +schemaVersion: 2.2.0` + + kubernetesInlinedDevfileErrCase_BadStorageRequest := ` +commands: +- apply: + component: image-build + id: build-image +- apply: + component: kubernetes-deploy + id: deployk8s +- composite: + commands: + - build-image + - deployk8s + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- attributes: + deployment/storageRequest: abc + kubernetes: + deployByDefault: false + inlined: |- + apiVersion: apps/v1 + kind: Deployment + metadata: + labels: + maysun: test + name: deploy-sample + spec: + template: + spec: + containers: + - image: quay.io/redhat-appstudio/user-workload:application-service-system-component-sample + name: container-image + name: kubernetes-deploy +metadata: + language: Java + name: java-springboot + projectType: springboot + version: 1.2.1 +schemaVersion: 2.2.0` + + kubernetesInlinedDevfileErrCase_BadCPUrequest := ` +commands: +- apply: + component: image-build + id: build-image +- apply: + component: kubernetes-deploy + id: deployk8s +- composite: + commands: + - build-image + - deployk8s + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- attributes: + deployment/cpuRequest: "abc" + kubernetes: + deployByDefault: false + inlined: |- + apiVersion: apps/v1 + kind: Deployment + metadata: + labels: + maysun: test + name: deploy-sample + spec: + template: + spec: + containers: + - image: quay.io/redhat-appstudio/user-workload:application-service-system-component-sample + name: container-image + name: kubernetes-deploy +metadata: + language: Java + name: java-springboot + projectType: springboot + version: 1.2.1 +schemaVersion: 2.2.0` + + noKubernetesCompDevfile := ` +commands: +- apply: + component: image-build + id: build-image +- composite: + commands: + - build-image + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +metadata: + name: java-springboot +schemaVersion: 2.2.0` + + multipleKubernetesCompsDevfile := ` +commands: +- apply: + component: image-build + id: build-image +- apply: + component: kubernetes-deploy + id: deployk8s +- composite: + commands: + - build-image + - deployk8s + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- attributes: + deployment/container-port: 1111 + kubernetes: + deployByDefault: false + inlined: |- + apiVersion: v1 + kind: Service + metadata: + creationTimestamp: null + name: service-sample + spec: + ports: + - port: 1111 + targetPort: 1111 + status: + loadBalancer: {} + name: kubernetes-deploy +- attributes: + deployment/container-port: 1111 + kubernetes: + deployByDefault: false + inlined: |- + apiVersion: v1 + kind: Service + metadata: + creationTimestamp: null + name: service-sample2 + spec: + ports: + - port: 1111 + targetPort: 1111 + status: + loadBalancer: {} + name: kubernetes-deploy2 +metadata: + name: java-springboot +schemaVersion: 2.2.0` + + kubernetesCompsWithNoDeployCmdDevfile := ` +commands: +- apply: + component: image-build + id: build-image +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- attributes: + deployment/container-port: 1111 + kubernetes: + deployByDefault: false + inlined: |- + apiVersion: v1 + kind: Service + metadata: + creationTimestamp: null + name: service-sample + spec: + ports: + - port: 1111 + targetPort: 1111 + status: + loadBalancer: {} + name: kubernetes-deploy +metadata: + name: java-springboot +schemaVersion: 2.2.0` + + replica := int32(5) + replicaUpdated := int32(1) + revHistoryLimit := int32(0) + setRevHistoryLimit := int32(5) + + host := "host.example.com" + implementationSpecific := networkingv1.PathTypeImplementationSpecific + + tests := []struct { + name string + devfileString string + componentName string + appName string + image string + hostname string + wantDeploy appsv1.Deployment + wantService corev1.Service + wantRoute routev1.Route + wantIngress networkingv1.Ingress + wantErr bool + }{ + { + name: "Simple devfile from Inline", + devfileString: kubernetesInlinedDevfile, + componentName: "component-sample", + appName: "application-sample", + image: "image1", + hostname: host, + wantDeploy: appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + Kind: "Deployment", + APIVersion: "apps/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + "maysun": "test", + }, + }, + Spec: appsv1.DeploymentSpec{ + RevisionHistoryLimit: &revHistoryLimit, + Replicas: &replica, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/instance": "component-sample", + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app.kubernetes.io/instance": "component-sample", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "container-image", + Env: []corev1.EnvVar{ + { + Name: "FOO", + Value: "foo11", + }, + { + Name: "BARBAR", + Value: "bar1", + }, + { + Name: "BAR", + Value: "bar11", + }, + }, + Image: "image1", + ImagePullPolicy: corev1.PullAlways, + LivenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/", + Port: intstr.FromInt(5566), + }, + }, + InitialDelaySeconds: int32(10), + PeriodSeconds: int32(10), + }, + Ports: []corev1.ContainerPort{ + { + ContainerPort: int32(1111), + }, + { + ContainerPort: int32(5566), + }, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.FromInt(5566), + }, + }, + InitialDelaySeconds: int32(10), + PeriodSeconds: int32(10), + }, + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("2"), + corev1.ResourceMemory: resource.MustParse("500Mi"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("701m"), + corev1.ResourceMemory: resource.MustParse("401Mi"), + }, + }, + }, + }, + }, + }, + }, + }, + wantService: corev1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + "maysun": "test", + }, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Port: int32(1111), + TargetPort: intstr.FromInt(1111), + }, + { + Port: int32(5566), + TargetPort: intstr.FromInt(5566), + }, + }, + Selector: map[string]string{ + "app.kubernetes.io/instance": "component-sample", + }, + }, + }, + wantIngress: networkingv1.Ingress{ + TypeMeta: metav1.TypeMeta{ + Kind: "Ingress", + APIVersion: "networking.k8s.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + PathType: &implementationSpecific, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "component-sample", + Port: networkingv1.ServiceBackendPort{ + Number: 5566, + }, + }, + }, + }, + }, + }, + }, + Host: host, + }, + }, + }, + }, + wantRoute: routev1.Route{ + TypeMeta: metav1.TypeMeta{ + Kind: "Route", + APIVersion: "route.openshift.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + Annotations: map[string]string{}, + }, + Spec: routev1.RouteSpec{ + Host: "route111222", + Path: "/", + Port: &routev1.RoutePort{ + TargetPort: intstr.FromInt(5566), + }, + To: routev1.RouteTargetReference{ + Kind: "Service", + Name: "component-sample", + }, + }, + }, + }, + { + name: "Simple devfile from Inline with deployment and svc from separated kube components", + devfileString: kubernetesInlinedDevfileSeparatedKubeComps, + componentName: "component-sample", + appName: "application-sample", + image: "image1", + hostname: host, + wantDeploy: appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + Kind: "Deployment", + APIVersion: "apps/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + }, + Spec: appsv1.DeploymentSpec{ + RevisionHistoryLimit: &revHistoryLimit, + Replicas: &replicaUpdated, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/instance": "component-sample", + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app.kubernetes.io/instance": "component-sample", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "container-image", + Env: []corev1.EnvVar{ + { + Name: "FOOFOO", + Value: "foo1", + }, + { + Name: "BARBAR", + Value: "bar1", + }, + }, + Image: "image1", + ImagePullPolicy: corev1.PullAlways, + LivenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/", + Port: intstr.FromInt(1111), + }, + }, + InitialDelaySeconds: int32(10), + PeriodSeconds: int32(10), + }, + Ports: []corev1.ContainerPort{ + { + ContainerPort: int32(1111), + }, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.FromInt(1111), + }, + }, + InitialDelaySeconds: int32(10), + PeriodSeconds: int32(10), + }, + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("2"), + corev1.ResourceMemory: resource.MustParse("500Mi"), + corev1.ResourceStorage: resource.MustParse("401Mi"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("700m"), + corev1.ResourceMemory: resource.MustParse("400Mi"), + corev1.ResourceStorage: resource.MustParse("201Mi"), + }, + }, + }, + }, + }, + }, + }, + }, + wantIngress: networkingv1.Ingress{ + TypeMeta: metav1.TypeMeta{ + Kind: "Ingress", + APIVersion: "networking.k8s.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + PathType: &implementationSpecific, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "component-sample", + Port: networkingv1.ServiceBackendPort{ + Number: 1111, + }, + }, + }, + }, + }, + }, + }, + Host: host, + }, + }, + }, + }, + wantRoute: routev1.Route{ + TypeMeta: metav1.TypeMeta{ + Kind: "Route", + APIVersion: "route.openshift.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + Annotations: map[string]string{}, + }, + Spec: routev1.RouteSpec{ + Path: "/", + Port: &routev1.RoutePort{ + TargetPort: intstr.FromInt(1111), + }, + To: routev1.RouteTargetReference{ + Kind: "Service", + Name: "component-sample", + }, + }, + }, + wantService: corev1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Port: int32(1111), + TargetPort: intstr.FromInt(1111), + }, + }, + Selector: map[string]string{ + "app.kubernetes.io/instance": "component-sample", + }, + }, + }, + }, + { + name: "Simple devfile from Inline with deployment and svc from separated kube components - with RevisionHistoryLimit set", + devfileString: kubernetesInlinedDevfileSeparatedKubeCompsRevHistory, + componentName: "component-sample", + appName: "application-sample", + image: "image1", + hostname: host, + wantDeploy: appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + Kind: "Deployment", + APIVersion: "apps/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + }, + Spec: appsv1.DeploymentSpec{ + RevisionHistoryLimit: &setRevHistoryLimit, + Replicas: &replicaUpdated, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/instance": "component-sample", + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app.kubernetes.io/instance": "component-sample", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "container-image", + Env: []corev1.EnvVar{ + { + Name: "FOOFOO", + Value: "foo1", + }, + { + Name: "BARBAR", + Value: "bar1", + }, + }, + Image: "image1", + ImagePullPolicy: corev1.PullAlways, + LivenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/", + Port: intstr.FromInt(1111), + }, + }, + InitialDelaySeconds: int32(10), + PeriodSeconds: int32(10), + }, + Ports: []corev1.ContainerPort{ + { + ContainerPort: int32(1111), + }, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.FromInt(1111), + }, + }, + InitialDelaySeconds: int32(10), + PeriodSeconds: int32(10), + }, + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("2"), + corev1.ResourceMemory: resource.MustParse("500Mi"), + corev1.ResourceStorage: resource.MustParse("401Mi"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("700m"), + corev1.ResourceMemory: resource.MustParse("400Mi"), + corev1.ResourceStorage: resource.MustParse("201Mi"), + }, + }, + }, + }, + }, + }, + }, + }, + wantIngress: networkingv1.Ingress{ + TypeMeta: metav1.TypeMeta{ + Kind: "Ingress", + APIVersion: "networking.k8s.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + PathType: &implementationSpecific, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "component-sample", + Port: networkingv1.ServiceBackendPort{ + Number: 1111, + }, + }, + }, + }, + }, + }, + }, + Host: host, + }, + }, + }, + }, + wantRoute: routev1.Route{ + TypeMeta: metav1.TypeMeta{ + Kind: "Route", + APIVersion: "route.openshift.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + Annotations: map[string]string{}, + }, + Spec: routev1.RouteSpec{ + Path: "/", + Port: &routev1.RoutePort{ + TargetPort: intstr.FromInt(1111), + }, + To: routev1.RouteTargetReference{ + Kind: "Service", + Name: "component-sample", + }, + }, + }, + wantService: corev1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Port: int32(1111), + TargetPort: intstr.FromInt(1111), + }, + }, + Selector: map[string]string{ + "app.kubernetes.io/instance": "component-sample", + }, + }, + }, + }, + { + name: "Simple devfile from Inline with only route", + devfileString: kubernetesInlinedDevfileRoute, + componentName: "component-sample", + appName: "application-sample", + image: "image1", + wantRoute: routev1.Route{ + TypeMeta: metav1.TypeMeta{ + Kind: "Route", + APIVersion: "route.openshift.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + "test": "test", + }, + }, + Spec: routev1.RouteSpec{ + Host: "route111222", + Port: &routev1.RoutePort{ + TargetPort: intstr.FromInt(5566), + }, + To: routev1.RouteTargetReference{ + Kind: "Service", + Name: "component-sample", + Weight: &weight, + }, + TLS: &routev1.TLSConfig{ + Termination: routev1.TLSTerminationEdge, + InsecureEdgeTerminationPolicy: routev1.InsecureEdgeTerminationPolicyRedirect, + }, + }, + }, + }, + { + name: "Simple devfile from Inline with only route, no targetport - should not panic", + devfileString: kubernetesInlinedDevfileRouteNoTargetPort, + componentName: "component-sample", + appName: "application-sample", + image: "image1", + wantRoute: routev1.Route{ + TypeMeta: metav1.TypeMeta{ + Kind: "Route", + APIVersion: "route.openshift.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + "test": "test", + }, + }, + Spec: routev1.RouteSpec{ + Host: "route111222", + Port: &routev1.RoutePort{ + TargetPort: intstr.FromInt(5566), + }, + To: routev1.RouteTargetReference{ + Kind: "Service", + Name: "component-sample", + Weight: &weight, + }, + TLS: &routev1.TLSConfig{ + Termination: routev1.TLSTerminationEdge, + InsecureEdgeTerminationPolicy: routev1.InsecureEdgeTerminationPolicyRedirect, + }, + }, + }, + }, + { + name: "Simple devfile from Inline with only Ingress", + devfileString: kubernetesInlinedDevfileIngress, + componentName: "component-sample", + appName: "application-sample", + hostname: host, + image: "image1", + wantIngress: networkingv1.Ingress{ + TypeMeta: metav1.TypeMeta{ + Kind: "Ingress", + APIVersion: "networking.k8s.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + "test": "test", + }, + Annotations: map[string]string{ + "nginx.ingress.kubernetes.io/rewrite-target": "/", + "test": "yes", + }, + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/testpath", + PathType: &implementationSpecific, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test", + Port: networkingv1.ServiceBackendPort{ + Number: 5566, + }, + }, + }, + }, + }, + }, + }, + Host: "foo.bar.com", + }, + }, + }, + }, + }, + { + name: "Simple devfile from Inline with only Svc", + devfileString: kubernetesInlinedDevfileSvc, + componentName: "component-sample", + appName: "application-sample", + image: "image1", + wantService: corev1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Port: int32(1111), + TargetPort: intstr.FromInt(1111), + }, + }, + Selector: map[string]string{ + "app.kubernetes.io/instance": "component-sample", + }, + }, + }, + }, + { + name: "Simple devfile from Inline with Deploy", + devfileString: kubernetesInlinedDevfileDeploy, + componentName: "component-sample", + appName: "application-sample", + image: "image1", + hostname: host, + wantDeploy: appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + Kind: "Deployment", + APIVersion: "apps/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + }, + Spec: appsv1.DeploymentSpec{ + RevisionHistoryLimit: &revHistoryLimit, + Replicas: &replicaUpdated, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/instance": "component-sample", + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app.kubernetes.io/instance": "component-sample", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "container-image", + Env: []corev1.EnvVar{ + { + Name: "FOOFOO", + Value: "foo1", + }, + { + Name: "BARBAR", + Value: "bar1", + }, + }, + Image: "image1", + ImagePullPolicy: corev1.PullAlways, + LivenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/", + Port: intstr.FromInt(1111), + }, + }, + InitialDelaySeconds: int32(10), + PeriodSeconds: int32(10), + }, + Ports: []corev1.ContainerPort{ + { + ContainerPort: int32(1111), + }, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.FromInt(1111), + }, + }, + InitialDelaySeconds: int32(10), + PeriodSeconds: int32(10), + }, + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("2"), + corev1.ResourceMemory: resource.MustParse("500Mi"), + corev1.ResourceStorage: resource.MustParse("401Mi"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("700m"), + corev1.ResourceMemory: resource.MustParse("400Mi"), + corev1.ResourceStorage: resource.MustParse("201Mi"), + }, + }, + }, + }, + }, + }, + }, + }, + wantIngress: networkingv1.Ingress{ + TypeMeta: metav1.TypeMeta{ + Kind: "Ingress", + APIVersion: "networking.k8s.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + PathType: &implementationSpecific, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "component-sample", + Port: networkingv1.ServiceBackendPort{ + Number: 1111, + }, + }, + }, + }, + }, + }, + }, + Host: host, + }, + }, + }, + }, + wantRoute: routev1.Route{ + TypeMeta: metav1.TypeMeta{ + Kind: "Route", + APIVersion: "route.openshift.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + Annotations: map[string]string{}, + }, + Spec: routev1.RouteSpec{ + Path: "/", + Port: &routev1.RoutePort{ + TargetPort: intstr.FromInt(1111), + }, + To: routev1.RouteTargetReference{ + Kind: "Service", + Name: "component-sample", + }, + }, + }, + }, + { + name: "Devfile with long component name - route name should be trimmed", + devfileString: kubernetesInlinedDevfileDeploy, + componentName: "component-sample-component-sample-component-sample", + appName: "application-sample", + image: "image1", + hostname: host, + wantDeploy: appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + Kind: "Deployment", + APIVersion: "apps/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample-component-sample-component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample-component-sample-component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample-component-sample-component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + }, + Spec: appsv1.DeploymentSpec{ + RevisionHistoryLimit: &revHistoryLimit, + Replicas: &replicaUpdated, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/instance": "component-sample-component-sample-component-sample", + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app.kubernetes.io/instance": "component-sample-component-sample-component-sample", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "container-image", + Env: []corev1.EnvVar{ + { + Name: "FOOFOO", + Value: "foo1", + }, + { + Name: "BARBAR", + Value: "bar1", + }, + }, + Image: "image1", + ImagePullPolicy: corev1.PullAlways, + LivenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/", + Port: intstr.FromInt(1111), + }, + }, + InitialDelaySeconds: int32(10), + PeriodSeconds: int32(10), + }, + Ports: []corev1.ContainerPort{ + { + ContainerPort: int32(1111), + }, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.FromInt(1111), + }, + }, + InitialDelaySeconds: int32(10), + PeriodSeconds: int32(10), + }, + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("2"), + corev1.ResourceMemory: resource.MustParse("500Mi"), + corev1.ResourceStorage: resource.MustParse("401Mi"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("700m"), + corev1.ResourceMemory: resource.MustParse("400Mi"), + corev1.ResourceStorage: resource.MustParse("201Mi"), + }, + }, + }, + }, + }, + }, + }, + }, + wantIngress: networkingv1.Ingress{ + TypeMeta: metav1.TypeMeta{ + Kind: "Ingress", + APIVersion: "networking.k8s.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample-component-sample-component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample-component-sample-component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample-component-sample-component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + PathType: &implementationSpecific, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "component-sample-component-sample-component-sample", + Port: networkingv1.ServiceBackendPort{ + Number: 1111, + }, + }, + }, + }, + }, + }, + }, + Host: host, + }, + }, + }, + }, + wantRoute: routev1.Route{ + TypeMeta: metav1.TypeMeta{ + Kind: "Route", + APIVersion: "route.openshift.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample-component-sample-component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample-component-sample-component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample-component-sample-component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + Annotations: map[string]string{}, + }, + Spec: routev1.RouteSpec{ + Path: "/", + Port: &routev1.RoutePort{ + TargetPort: intstr.FromInt(1111), + }, + To: routev1.RouteTargetReference{ + Kind: "Service", + Name: "component-sample-component-sample-component-sample", + }, + }, + }, + }, + { + name: "Simple devfile from Inline with Route Host missing", + devfileString: kubernetesInlinedDevfileRouteHostMissing, + componentName: "component-sample", + appName: "application-sample", + image: "image1", + hostname: host, + wantDeploy: appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + Kind: "Deployment", + APIVersion: "apps/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + "maysun": "test", + }, + }, + Spec: appsv1.DeploymentSpec{ + RevisionHistoryLimit: &revHistoryLimit, + Replicas: &replicaUpdated, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/instance": "component-sample", + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app.kubernetes.io/instance": "component-sample", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "container-image", + Env: []corev1.EnvVar{ + { + Name: "FOOFOO", + Value: "foo1", + }, + { + Name: "BARBAR", + Value: "bar1", + }, + }, + Image: "image1", + ImagePullPolicy: corev1.PullAlways, + LivenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/", + Port: intstr.FromInt(5566), + }, + }, + InitialDelaySeconds: int32(10), + PeriodSeconds: int32(10), + }, + Ports: []corev1.ContainerPort{ + { + ContainerPort: int32(1111), + }, + { + ContainerPort: int32(5566), + }, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.FromInt(5566), + }, + }, + InitialDelaySeconds: int32(10), + PeriodSeconds: int32(10), + }, + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("2"), + corev1.ResourceMemory: resource.MustParse("500Mi"), + corev1.ResourceStorage: resource.MustParse("400Mi"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("700m"), + corev1.ResourceMemory: resource.MustParse("400Mi"), + corev1.ResourceStorage: resource.MustParse("200Mi"), + }, + }, + }, + }, + }, + }, + }, + }, + wantIngress: networkingv1.Ingress{ + TypeMeta: metav1.TypeMeta{ + Kind: "Ingress", + APIVersion: "networking.k8s.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + PathType: &implementationSpecific, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "component-sample", + Port: networkingv1.ServiceBackendPort{ + Number: 5566, + }, + }, + }, + }, + }, + }, + }, + Host: host, + }, + }, + }, + }, + wantRoute: routev1.Route{ + TypeMeta: metav1.TypeMeta{ + Kind: "Route", + APIVersion: "route.openshift.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + Annotations: map[string]string{}, + }, + Spec: routev1.RouteSpec{ + Path: "/", + Port: &routev1.RoutePort{ + TargetPort: intstr.FromInt(5566), + }, + To: routev1.RouteTargetReference{ + Kind: "Service", + Name: "component-sample", + }, + }, + }, + }, + { + name: "Simple devfile without inline", + devfileString: kubernetesWithoutInline, + componentName: "component-sample", + image: "image1", + }, + { + name: "Simple devfile from Inline with multiple kubernetes components and only one is referenced by deploy command", + devfileString: multipleKubernetesCompsDevfile, + componentName: "component-sample", + appName: "application-sample", + image: "image1", + wantService: corev1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Port: int32(1111), + TargetPort: intstr.FromInt(1111), + }, + }, + Selector: map[string]string{ + "app.kubernetes.io/instance": "component-sample", + }, + }, + }, + }, + { + name: "Simple devfile from Inline with only one kubernetes component but no deploy command", + devfileString: kubernetesCompsWithNoDeployCmdDevfile, + componentName: "component-sample", + appName: "application-sample", + image: "image1", + wantService: corev1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "component-sample", + Labels: map[string]string{ + "app.kubernetes.io/created-by": "application-service", + "app.kubernetes.io/instance": "component-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "component-sample", + "app.kubernetes.io/part-of": "application-sample", + }, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Port: int32(1111), + TargetPort: intstr.FromInt(1111), + }, + }, + Selector: map[string]string{ + "app.kubernetes.io/instance": "component-sample", + }, + }, + }, + }, + { + name: "No kubernetes components defined.", + devfileString: noKubernetesCompDevfile, + componentName: "component-sample", + image: "image1", + wantErr: true, + }, + { + name: "Bad Memory Limit", + devfileString: kubernetesInlinedDevfileErrCase_BadMemoryLimit, + componentName: "component-sample", + image: "image1", + wantErr: true, + }, + { + name: "Bad Storage Limit", + devfileString: kubernetesInlinedDevfileErrCase_BadStorageLimit, + componentName: "component-sample", + image: "image1", + wantErr: true, + }, + { + name: "Bad CPU Limit", + devfileString: kubernetesInlinedDevfileErrCase_BadCPULimit, + componentName: "component-sample", + image: "image1", + wantErr: true, + }, + { + name: "Bad Memory Request", + devfileString: kubernetesInlinedDevfileErrCase_BadMemoryRequest, + componentName: "component-sample", + image: "image1", + wantErr: true, + }, + { + name: "Bad Storage Request", + devfileString: kubernetesInlinedDevfileErrCase_BadStorageRequest, + componentName: "component-sample", + image: "image1", + wantErr: true, + }, + { + name: "Bad CPU Request", + devfileString: kubernetesInlinedDevfileErrCase_BadCPUrequest, + componentName: "component-sample", + image: "image1", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var devfileSrc DevfileSrc + if tt.devfileString != "" { + devfileSrc = DevfileSrc{ + Data: tt.devfileString, + } + } + + devfileData, err := ParseDevfile(devfileSrc) + if err != nil { + t.Errorf("TestGetResourceFromDevfile() unexpected parse error: %v", err) + } + deployAssociatedComponents, err := parser.GetDeployComponents(devfileData) + if err != nil { + t.Errorf("TestGetResourceFromDevfile() unexpected get deploy components error: %v", err) + } + logger := ctrl.Log.WithName("TestGetResourceFromDevfile") + + actualResources, err := GetResourceFromDevfile(logger, devfileData, deployAssociatedComponents, tt.componentName, tt.appName, tt.image, tt.hostname) + if tt.wantErr && (err == nil) { + t.Error("wanted error but got nil") + } else if !tt.wantErr && err != nil { + t.Errorf("TestGetResourceFromDevfile() unexpected get resource from devfile error: %v", err) + } else if err == nil { + if len(actualResources.Deployments) > 0 { + assert.Equal(t, tt.wantDeploy, actualResources.Deployments[0], "First Deployment did not match") + } + + if len(actualResources.Services) > 0 { + assert.Equal(t, tt.wantService, actualResources.Services[0], "First Service did not match") + } + + if len(actualResources.Ingresses) > 0 { + assert.Equal(t, tt.wantIngress, actualResources.Ingresses[0], "First Ingress did not match") + } + + if len(actualResources.Routes) > 0 { + if tt.name == "Devfile with long component name - route name should be trimmed" { + if len(actualResources.Routes[0].Name) > 30 { + t.Errorf("Expected trimmed route name with length < 30, but got %v", len(actualResources.Routes[0].Name)) + } + if !strings.Contains(actualResources.Routes[0].Name, "component-sample-comp") { + t.Errorf("Expected route name to contain %v, but got %v", "component-sample-comp", actualResources.Routes[0].Name) + } + } else { + assert.Equal(t, tt.wantRoute, actualResources.Routes[0], "First Route did not match") + } + } + } + }) + } +} + +func TestParseDevfileModel(t *testing.T) { + + testServerURL := "127.0.0.1:9080" + + simpleDevfile := ` +metadata: + attributes: + appModelRepository.url: https://github.com/testorg/petclinic-app + gitOpsRepository.url: https://github.com/testorg/petclinic-gitops + name: petclinic +schemaVersion: 2.2.0` + + testServer := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, err := w.Write([]byte(simpleDevfile)) + if err != nil { + t.Errorf("TestParseDevfileModel() unexpected error while writing data: %v", err) + } + })) + // create a listener with the desired port. + l, err := net.Listen("tcp", testServerURL) + if err != nil { + t.Errorf("TestParseDevfileModel() unexpected error while creating listener: %v", err) + return + } + + // NewUnstartedServer creates a listener. Close that listener and replace + // with the one we created. + testServer.Listener.Close() + testServer.Listener = l + + testServer.Start() + defer testServer.Close() + + localPath := "/tmp/testDir" + localDevfilePath := path.Join(localPath, "devfile.yaml") + // prepare for local file + err = os.MkdirAll(localPath, 0755) + if err != nil { + t.Errorf("TestParseDevfileModel() error: failed to create folder: %v, error: %v", localPath, err) + } + err = ioutil.WriteFile(localDevfilePath, []byte(simpleDevfile), 0644) + if err != nil { + t.Errorf("TestParseDevfileModel() error: fail to write to file: %v", err) + } + + if err != nil { + t.Error(err) + } + + defer os.RemoveAll(localPath) + + tests := []struct { + name string + devfileString string + devfileURL string + devfilePath string + wantDevfile *v2.DevfileV2 + wantMetadata devfile.DevfileMetadata + wantSchemaVersion string + }{ + { + name: "Simple devfile from data", + devfileString: simpleDevfile, + wantMetadata: devfile.DevfileMetadata{ + Name: "petclinic", + Attributes: attributes.Attributes{}.PutString("gitOpsRepository.url", "https://github.com/testorg/petclinic-gitops").PutString("appModelRepository.url", "https://github.com/testorg/petclinic-app"), + }, + wantSchemaVersion: string(data.APISchemaVersion220), + }, + { + name: "Simple devfile from URL", + devfileURL: "http://" + testServerURL, + wantMetadata: devfile.DevfileMetadata{ + Name: "petclinic", + Attributes: attributes.Attributes{}.PutString("gitOpsRepository.url", "https://github.com/testorg/petclinic-gitops").PutString("appModelRepository.url", "https://github.com/testorg/petclinic-app"), + }, + wantSchemaVersion: string(data.APISchemaVersion220), + }, + { + name: "Simple devfile from PATH", + devfilePath: localDevfilePath, + wantMetadata: devfile.DevfileMetadata{ + Name: "petclinic", + Attributes: attributes.Attributes{}.PutString("gitOpsRepository.url", "https://github.com/testorg/petclinic-gitops").PutString("appModelRepository.url", "https://github.com/testorg/petclinic-app"), + }, + wantSchemaVersion: string(data.APISchemaVersion220), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var devfileSrc DevfileSrc + if tt.devfileString != "" { + devfileSrc = DevfileSrc{ + Data: tt.devfileString, + } + } else if tt.devfileURL != "" { + devfileSrc = DevfileSrc{ + URL: tt.devfileURL, + } + } else if tt.devfilePath != "" { + devfileSrc = DevfileSrc{ + Path: tt.devfilePath, + } + } + devfile, err := ParseDevfile(devfileSrc) + if err != nil { + t.Errorf("TestParseDevfileModel() unexpected error: %v", err) + } else { + gotMetadata := devfile.GetMetadata() + if !reflect.DeepEqual(gotMetadata, tt.wantMetadata) { + t.Errorf("TestParseDevfileModel() metadata is different") + } + + gotSchemaVersion := devfile.GetSchemaVersion() + if gotSchemaVersion != tt.wantSchemaVersion { + t.Errorf("TestParseDevfileModel() schema version is different") + } + } + }) + } +} diff --git a/gitops-generator/pkg/generate/generate.go b/gitops-generator/pkg/generate/generate.go new file mode 100644 index 00000000..746e6e03 --- /dev/null +++ b/gitops-generator/pkg/generate/generate.go @@ -0,0 +1,367 @@ +// +// Copyright 2023 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generate + +import ( + "context" + "fmt" + "path/filepath" + "reflect" + + devfileParser "github.com/devfile/library/v2/pkg/devfile/parser" + "github.com/devfile/library/v2/pkg/devfile/parser/data" + "github.com/go-logr/logr" + appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" + devfile "github.com/redhat-appstudio/application-service/gitops-generator/pkg/devfile" + "github.com/redhat-appstudio/application-service/gitops-generator/pkg/util" + gitopsgenv1alpha1 "github.com/redhat-developer/gitops-generator/api/v1alpha1" + gitopsgen "github.com/redhat-developer/gitops-generator/pkg" + "github.com/redhat-developer/gitops-generator/pkg/util/ioutils" + "github.com/spf13/afero" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" +) + +type GitOpsGenParams struct { + Generator gitopsgen.Generator + DevfileData data.DevfileData + RemoteURL string + Branch string + Context string + Token string +} + +func GenerateGitopsBase(ctx context.Context, log logr.Logger, client ctrlclient.Client, component appstudiov1alpha1.Component, appFs afero.Afero, gitopsParams GitOpsGenParams) error { + // Create a temp folder to create the gitops resources in + tempDir, err := ioutils.CreateTempPath(component.Name, appFs) + if err != nil { + return fmt.Errorf("unable to create temp directory for GitOps resources due to error: %v", err) + } + + devfileSrc := devfile.DevfileSrc{ + Data: component.Status.Devfile, + } + compDevfileData, err := devfile.ParseDevfile(devfileSrc) + if err != nil { + return err + } + + deployAssociatedComponents, err := devfileParser.GetDeployComponents(compDevfileData) + if err != nil { + log.Error(err, "unable to get deploy components") + return err + } + + kubernetesResources, err := devfile.GetResourceFromDevfile(log, compDevfileData, deployAssociatedComponents, component.Name, component.Spec.Application, component.Spec.ContainerImage, "") + if err != nil { + log.Error(err, "unable to get kubernetes resources from the devfile outerloop components") + return err + } + + // Generate and push the gitops resources + mappedGitOpsComponent := util.GetMappedGitOpsComponent(component, kubernetesResources) + + //add the token name to the metrics. When we add more tokens and rotate, we can determine how evenly distributed the requests are + err = gitopsParams.Generator.CloneGenerateAndPush(tempDir, gitopsParams.RemoteURL, mappedGitOpsComponent, appFs, gitopsParams.Branch, gitopsParams.Context, false) + if err != nil { + log.Error(err, "unable to generate gitops resources due to error") + return err + } + + //Gitops functions return sanitized error messages + err = gitopsParams.Generator.CommitAndPush(tempDir, "", gitopsParams.RemoteURL, mappedGitOpsComponent.Name, gitopsParams.Branch, "Generating GitOps resources") + if err != nil { + log.Error(err, "unable to commit and push gitops resources due to error") + return err + } + + // Get the commit ID for the gitops repository + var commitID string + repoPath := filepath.Join(tempDir, component.Name) + if commitID, err = gitopsParams.Generator.GetCommitIDFromRepo(appFs, repoPath); err != nil { + log.Error(err, "") + return err + } + + component.Status.GitOps.CommitID = commitID + + // Remove the temp folder that was created + return appFs.RemoveAll(tempDir) +} + +func GenerateGitopsOverlays(ctx context.Context, log logr.Logger, client ctrlclient.Client, appSnapshotEnvBinding appstudiov1alpha1.SnapshotEnvironmentBinding, appFs afero.Afero, gitopsParams GitOpsGenParams) error { + // Create a temp folder to create the gitops resources in + + applicationName := appSnapshotEnvBinding.Spec.Application + environmentName := appSnapshotEnvBinding.Spec.Environment + snapshotName := appSnapshotEnvBinding.Spec.Snapshot + components := appSnapshotEnvBinding.Spec.Components + + // Get the Environment CR + environment := appstudiov1alpha1.Environment{} + err := client.Get(ctx, types.NamespacedName{Name: environmentName, Namespace: appSnapshotEnvBinding.Namespace}, &environment) + if err != nil { + return fmt.Errorf("unable to get the Environment %s", environmentName) + } + + // Get the Snapshot CR + appSnapshot := appstudiov1alpha1.Snapshot{} + err = client.Get(ctx, types.NamespacedName{Name: snapshotName, Namespace: appSnapshotEnvBinding.Namespace}, &appSnapshot) + if err != nil { + return fmt.Errorf("unable to get the Application Snapshot %s", snapshotName) + } + if appSnapshot.Spec.Application != applicationName { + return fmt.Errorf("application snapshot %s does not belong to the application %s", snapshotName, applicationName) + } + + componentGeneratedResources := make(map[string][]string) + var tempDir string + clone := true + + for _, component := range components { + componentName := component.Name + + // Get the Component CR + hasComponent := appstudiov1alpha1.Component{} + err = client.Get(ctx, types.NamespacedName{Name: componentName, Namespace: appSnapshotEnvBinding.Namespace}, &hasComponent) + if err != nil { + return fmt.Errorf("unable to get the Component %s", componentName) + } + + if hasComponent.Spec.SkipGitOpsResourceGeneration { + continue + } + + // Sanity check to make sure the binding component has referenced the correct application + if hasComponent.Spec.Application != applicationName { + return fmt.Errorf("component %s does not belong to the application %s", componentName, applicationName) + } + + var clusterIngressDomain string + isKubernetesCluster := isKubernetesCluster(environment) + unsupportedConfig := environment.Spec.UnstableConfigurationFields + if unsupportedConfig != nil { + clusterIngressDomain = unsupportedConfig.IngressDomain + } + + // Safeguard if Ingress Domain is empty on Kubernetes + if isKubernetesCluster && clusterIngressDomain == "" { + err = fmt.Errorf("ingress domain cannot be empty on a Kubernetes cluster") + return err + } + + devfileSrc := devfile.DevfileSrc{ + Data: hasComponent.Status.Devfile, + } + compDevfileData, err := devfile.ParseDevfile(devfileSrc) + if err != nil { + return err + } + + deployAssociatedComponents, err := devfileParser.GetDeployComponents(compDevfileData) + if err != nil { + return err + } + + var hostname string + if isKubernetesCluster { + hostname, err = devfile.GetIngressHostName(hasComponent.Name, appSnapshotEnvBinding.Namespace, clusterIngressDomain) + if err != nil { + return err + } + } + + kubernetesResources, err := devfile.GetResourceFromDevfile(log, compDevfileData, deployAssociatedComponents, hasComponent.Name, hasComponent.Spec.Application, hasComponent.Spec.ContainerImage, hostname) + if err != nil { + return err + } + + // Create a random, generated name for the route + // ToDo: Ideally we wouldn't need to loop here, but since the Component status is a list, we can't avoid it + var routeName string + for _, compStatus := range appSnapshotEnvBinding.Status.Components { + if compStatus.Name == componentName { + if compStatus.GeneratedRouteName != "" { + routeName = compStatus.GeneratedRouteName + log.Info(fmt.Sprintf("route name for component is %s", routeName)) + } + break + } + } + if routeName == "" { + routeName = util.GenerateRandomRouteName(hasComponent.Name) + log.Info(fmt.Sprintf("generated route name %s", routeName)) + } + + // If a route is present, update the first instance's name + if len(kubernetesResources.Routes) > 0 { + kubernetesResources.Routes[0].ObjectMeta.Name = routeName + } + + var imageName string + + for _, snapshotComponent := range appSnapshot.Spec.Components { + if snapshotComponent.Name == componentName { + imageName = snapshotComponent.ContainerImage + break + } + } + + if imageName == "" { + err := fmt.Errorf("application snapshot %s did not reference component %s", snapshotName, componentName) + return err + } + + gitOpsRemoteURL, gitOpsBranch, gitOpsContext, err := util.ProcessGitOpsStatus(hasComponent.Status.GitOps, gitopsParams.Token) + if err != nil { + return err + } + + if clone { + // Create a temp folder to create the gitops resources in + tempDir, err = ioutils.CreateTempPath(appSnapshotEnvBinding.Name, appFs) + if err != nil { + return fmt.Errorf("unable to create temp directory for gitops resources due to error: %v", err) + } + } + + envVars := make([]corev1.EnvVar, 0) + for _, env := range component.Configuration.Env { + envVars = append(envVars, corev1.EnvVar{ + Name: env.Name, + Value: env.Value, + }) + } + + environmentConfigEnvVars := make([]corev1.EnvVar, 0) + for _, env := range environment.Spec.Configuration.Env { + environmentConfigEnvVars = append(environmentConfigEnvVars, corev1.EnvVar{ + Name: env.Name, + Value: env.Value, + }) + } + componentResources := corev1.ResourceRequirements{} + if component.Configuration.Resources != nil { + componentResources = *component.Configuration.Resources + } + + kubeLabels := map[string]string{ + "app.kubernetes.io/name": componentName, + "app.kubernetes.io/instance": component.Name, + "app.kubernetes.io/part-of": applicationName, + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/created-by": "application-service", + } + genOptions := gitopsgenv1alpha1.GeneratorOptions{ + Name: component.Name, + RouteName: routeName, + Resources: componentResources, + BaseEnvVar: envVars, + OverlayEnvVar: environmentConfigEnvVars, + K8sLabels: kubeLabels, + IsKubernetesCluster: isKubernetesCluster, + TargetPort: hasComponent.Spec.TargetPort, // pass the target port to the gitops gen library as they may generate a route/ingress based on the target port if the devfile does not have an ingress/route or an endpoint + } + + if component.Configuration.Replicas != nil { + genOptions.Replicas = *component.Configuration.Replicas + } + + if !reflect.DeepEqual(kubernetesResources, devfileParser.KubernetesResources{}) { + genOptions.KubernetesResources.Routes = append(genOptions.KubernetesResources.Routes, kubernetesResources.Routes...) + genOptions.KubernetesResources.Ingresses = append(genOptions.KubernetesResources.Ingresses, kubernetesResources.Ingresses...) + } + + if isKubernetesCluster && len(genOptions.KubernetesResources.Ingresses) == 0 { + // provide the hostname for the component if there are no ingresses + // Gitops Generator Library will create the Ingress with the hostname + genOptions.Route = hostname + } + + //Gitops functions return sanitized error messages + err = gitopsParams.Generator.GenerateOverlaysAndPush(tempDir, clone, gitOpsRemoteURL, genOptions, applicationName, environmentName, imageName, "", appFs, gitOpsBranch, gitOpsContext, true, componentGeneratedResources) + if err != nil { + return err + } + + // Retrieve the commit ID + var commitID string + repoPath := filepath.Join(tempDir, applicationName) + if commitID, err = gitopsParams.Generator.GetCommitIDFromRepo(appFs, repoPath); err != nil { + return err + } + + // Set the BindingComponent status + componentStatus := appstudiov1alpha1.BindingComponentStatus{ + Name: componentName, + GitOpsRepository: appstudiov1alpha1.BindingComponentGitOpsRepository{ + URL: hasComponent.Status.GitOps.RepositoryURL, + Branch: gitOpsBranch, + Path: filepath.Join(gitOpsContext, "components", componentName, "overlays", environmentName), + CommitID: commitID, + }, + } + + // On OpenShift, we generate a unique route name for each Component, so include that in the status + if !isKubernetesCluster { + componentStatus.GeneratedRouteName = routeName + log.Info(fmt.Sprintf("added RouteName %s for Component %s to status", routeName, componentName)) + } + + if _, ok := componentGeneratedResources[componentName]; ok { + componentStatus.GitOpsRepository.GeneratedResources = componentGeneratedResources[componentName] + } + + isNewComponent := true + for i := range appSnapshotEnvBinding.Status.Components { + if appSnapshotEnvBinding.Status.Components[i].Name == componentStatus.Name { + appSnapshotEnvBinding.Status.Components[i] = componentStatus + isNewComponent = false + break + } + } + if isNewComponent { + appSnapshotEnvBinding.Status.Components = append(appSnapshotEnvBinding.Status.Components, componentStatus) + } + + // Set the clone to false, since we dont want to clone the repo again for the other components + clone = false + + } + + // Update the binding status to reflect the GitOps data + err = client.Status().Update(ctx, &appSnapshotEnvBinding) + if err != nil { + return err + } + + return appFs.RemoveAll(tempDir) +} + +// isKubernetesCluster checks if its either a Kubernetes or an OpenShift cluster +// from the Environment custom resource +func isKubernetesCluster(environment appstudiov1alpha1.Environment) bool { + unstableConfig := environment.Spec.UnstableConfigurationFields + + if unstableConfig != nil { + if unstableConfig.ClusterType == appstudiov1alpha1.ConfigurationClusterType_Kubernetes { + return true + } + } + + return false +} diff --git a/gitops/generate_mock.go b/gitops-generator/pkg/generate/generate_mock.go similarity index 99% rename from gitops/generate_mock.go rename to gitops-generator/pkg/generate/generate_mock.go index 69749959..9447af59 100644 --- a/gitops/generate_mock.go +++ b/gitops-generator/pkg/generate/generate_mock.go @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package gitops +package generate import ( "fmt" diff --git a/gitops-generator/pkg/generate/generate_test.go b/gitops-generator/pkg/generate/generate_test.go new file mode 100644 index 00000000..74abb629 --- /dev/null +++ b/gitops-generator/pkg/generate/generate_test.go @@ -0,0 +1,898 @@ +package generate + +import ( + "context" + "errors" + "testing" + + appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" + "github.com/redhat-developer/gitops-generator/pkg/util/ioutils" + "github.com/spf13/afero" + "go.uber.org/zap/zapcore" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +var kubernetesInlinedDevfile = ` +commands: +- apply: + component: image-build + id: build-image +- apply: + component: kubernetes-deploy + id: deployk8s +- composite: + commands: + - build-image + - deployk8s + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- attributes: + api.devfile.io/k8sLikeComponent-originalURI: deploy.yaml + deployment/container-port: 5566 + deployment/containerENV: + - name: FOO + value: foo11 + - name: BAR + value: bar11 + deployment/cpuLimit: "2" + deployment/cpuRequest: 701m + deployment/memoryLimit: 500Mi + deployment/memoryRequest: 401Mi + deployment/replicas: 5 + deployment/route: route111222 + deployment/storageLimit: 400Mi + deployment/storageRequest: 201Mi + kubernetes: + deployByDefault: false + endpoints: + - name: http-8081 + path: / + secure: false + targetPort: 8081 + inlined: |- + apiVersion: apps/v1 + kind: Deployment + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + maysun: test + name: deploy-sample + spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: component-sample + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/instance: component-sample + spec: + containers: + - env: + - name: FOO + value: foo1 + - name: BARBAR + value: bar1 + image: quay.io/redhat-appstudio/user-workload:application-service-system-component-sample + imagePullPolicy: Always + livenessProbe: + httpGet: + path: / + port: 1111 + initialDelaySeconds: 10 + periodSeconds: 10 + name: container-image + ports: + - containerPort: 1111 + readinessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + tcpSocket: + port: 1111 + resources: + limits: + cpu: "2" + memory: 500Mi + storage: 400Mi + requests: + cpu: 700m + memory: 400Mi + storage: 200Mi + status: {} + --- + apiVersion: apps/v1 + kind: Deployment + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + maysun: test + name: deploy-sample-2 + spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: component-sample + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/instance: component-sample + spec: + containers: + - env: + - name: FOO + value: foo1 + - name: BAR + value: bar1 + image: quay.io/redhat-appstudio/user-workload:application-service-system-component-sample + imagePullPolicy: Always + livenessProbe: + httpGet: + path: / + port: 1111 + initialDelaySeconds: 10 + periodSeconds: 10 + name: container-image + ports: + - containerPort: 1111 + readinessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + tcpSocket: + port: 1111 + resources: + limits: + cpu: "2" + memory: 500Mi + storage: 400Mi + requests: + cpu: 700m + memory: 400Mi + storage: 200Mi + status: {} + --- + apiVersion: v1 + kind: Service + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + maysun: test + name: service-sample + spec: + ports: + - port: 1111 + targetPort: 1111 + selector: + app.kubernetes.io/instance: component-sample + status: + loadBalancer: {} + --- + apiVersion: v1 + kind: Service + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + maysun: test + name: service-sample-2 + spec: + ports: + - port: 1111 + targetPort: 1111 + selector: + app.kubernetes.io/instance: component-sample + status: + loadBalancer: {} + --- + apiVersion: route.openshift.io/v1 + kind: Route + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + maysun: test + name: route-sample + spec: + host: route111 + port: + targetPort: 1111 + tls: + insecureEdgeTerminationPolicy: Redirect + termination: edge + to: + kind: Service + name: component-sample + weight: 100 + status: {} + --- + apiVersion: route.openshift.io/v1 + kind: Route + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + maysun: test + name: route-sample-2 + spec: + host: route111 + port: + targetPort: 1111 + tls: + insecureEdgeTerminationPolicy: Redirect + termination: edge + to: + kind: Service + name: component-sample + weight: 100 + status: {} + --- + apiVersion: networking.k8s.io/v1 + kind: Ingress + metadata: + name: ingress-sample + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + maysun: test + spec: + ingressClassName: nginx-example + rules: + - http: + paths: + - path: /testpath + pathType: Prefix + backend: + service: + name: test + port: + number: 80 + --- + apiVersion: networking.k8s.io/v1 + kind: Ingress + metadata: + name: ingress-sample-2 + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + maysun: test + spec: + ingressClassName: nginx-example + rules: + - http: + paths: + - path: /testpath + pathType: Prefix + backend: + service: + name: test + port: + number: 80 + --- + apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: pvc-sample + labels: + maysun: test + spec: + accessModes: + - ReadWriteOnce + volumeMode: Filesystem + resources: + requests: + storage: 8Gi + storageClassName: slow + selector: + matchLabels: + release: "stable" + matchExpressions: + - {key: environment, operator: In, values: [dev]} + --- + apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: pvc-sample-2 + labels: + maysun: test + spec: + accessModes: + - ReadWriteOnce + volumeMode: Filesystem + resources: + requests: + storage: 8Gi + storageClassName: slow + selector: + matchLabels: + release: "stable" + matchExpressions: + - {key: environment, operator: In, values: [dev]} + name: kubernetes-deploy +metadata: + name: java-springboot +schemaVersion: 2.2.0` + +var noDeployDevfile = ` +metadata: + name: java-springboot +schemaVersion: 2.2.0` + +var invalidDevfile = `safdsfsdl32432423\n\t` + +func TestGenerateGitopsBase(t *testing.T) { + + appFS := ioutils.NewMemoryFilesystem() + readOnlyFs := ioutils.NewReadOnlyFs() + ctx := context.Background() + fakeClient := fake.NewClientBuilder().Build() + + errGen := NewMockGenerator() + errGen.Errors.Push(errors.New("Fatal error")) + + componentSpec := appstudiov1alpha1.ComponentSpec{ + ComponentName: "test-component", + Application: "test-app", + Source: appstudiov1alpha1.ComponentSource{ + ComponentSourceUnion: appstudiov1alpha1.ComponentSourceUnion{ + GitSource: &appstudiov1alpha1.GitSource{ + URL: "git@github.com:testing/testing.git", + }, + }, + }, + } + + tests := []struct { + name string + fs afero.Afero + component *appstudiov1alpha1.Component + gitopsParams GitOpsGenParams + wantErr bool + }{ + { + name: "Simple application component, no errors", + fs: appFS, + component: &appstudiov1alpha1.Component{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "appstudio.redhat.com/v1alpha1", + Kind: "Component", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-component", + Namespace: "test-namespace", + }, + Spec: componentSpec, + Status: appstudiov1alpha1.ComponentStatus{ + GitOps: appstudiov1alpha1.GitOpsStatus{ + RepositoryURL: "https://github.com/test/repo", + Branch: "main", + Context: "/test", + }, + Devfile: kubernetesInlinedDevfile, + }, + }, + gitopsParams: GitOpsGenParams{ + Generator: NewMockGenerator(), + }, + wantErr: false, + }, + { + name: "Simple application component - missing devfile", + fs: appFS, + component: &appstudiov1alpha1.Component{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "appstudio.redhat.com/v1alpha1", + Kind: "Component", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-component", + Namespace: "test-namespace", + }, + Spec: componentSpec, + Status: appstudiov1alpha1.ComponentStatus{ + GitOps: appstudiov1alpha1.GitOpsStatus{ + RepositoryURL: "https://github.com/test/repo", + Branch: "main", + Context: "/test", + }, + }, + }, + gitopsParams: GitOpsGenParams{ + Generator: NewMockGenerator(), + }, + wantErr: true, + }, + { + name: "Generation error, Read only file system", + fs: readOnlyFs, + component: &appstudiov1alpha1.Component{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "appstudio.redhat.com/v1alpha1", + Kind: "Component", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-component", + Namespace: "test-namespace", + }, + Spec: componentSpec, + Status: appstudiov1alpha1.ComponentStatus{ + GitOps: appstudiov1alpha1.GitOpsStatus{ + RepositoryURL: "https://github.com/test/repo", + Branch: "main", + Context: "/test", + }, + Devfile: kubernetesInlinedDevfile, + }, + }, + gitopsParams: GitOpsGenParams{ + Generator: NewMockGenerator(), + }, + wantErr: true, + }, + { + name: "Generation error", + fs: appFS, + component: &appstudiov1alpha1.Component{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "appstudio.redhat.com/v1alpha1", + Kind: "Component", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-component", + Namespace: "test-namespace", + }, + Spec: componentSpec, + Status: appstudiov1alpha1.ComponentStatus{ + GitOps: appstudiov1alpha1.GitOpsStatus{ + RepositoryURL: "https://github.com/test/repo", + Branch: "main", + Context: "/test", + }, + Devfile: kubernetesInlinedDevfile, + }, + }, + gitopsParams: GitOpsGenParams{ + Generator: errGen, + }, + wantErr: true, + }, + { + name: "Error retrieving resources from devfile", + fs: appFS, + component: &appstudiov1alpha1.Component{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "appstudio.redhat.com/v1alpha1", + Kind: "Component", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-component", + Namespace: "test-namespace", + }, + Spec: componentSpec, + Status: appstudiov1alpha1.ComponentStatus{ + GitOps: appstudiov1alpha1.GitOpsStatus{ + RepositoryURL: "https://github.com/test/repo", + Branch: "main", + Context: "/test", + }, + Devfile: noDeployDevfile, + }, + }, + gitopsParams: GitOpsGenParams{ + Generator: errGen, + }, + wantErr: true, + }, + { + name: "Error from invalid devfile", + fs: appFS, + component: &appstudiov1alpha1.Component{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "appstudio.redhat.com/v1alpha1", + Kind: "Component", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-component", + Namespace: "test-namespace", + }, + Spec: componentSpec, + Status: appstudiov1alpha1.ComponentStatus{ + GitOps: appstudiov1alpha1.GitOpsStatus{ + RepositoryURL: "https://github.com/test/repo", + Branch: "main", + Context: "/test", + }, + Devfile: invalidDevfile, + }, + }, + gitopsParams: GitOpsGenParams{ + Generator: errGen, + }, + wantErr: true, + }, + { + name: "Commit ID retrieval error", + fs: appFS, + component: &appstudiov1alpha1.Component{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "appstudio.redhat.com/v1alpha1", + Kind: "Component", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-git-error", + Namespace: "test-namespace", + }, + Spec: componentSpec, + Status: appstudiov1alpha1.ComponentStatus{ + GitOps: appstudiov1alpha1.GitOpsStatus{ + RepositoryURL: "https://github.com/test-git-error/test-git-error", + Branch: "main", + Context: "/test", + }, + Devfile: kubernetesInlinedDevfile, + }, + }, + gitopsParams: GitOpsGenParams{ + Generator: NewMockGenerator(), + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + log := zap.New(zap.UseFlagOptions(&zap.Options{ + Development: true, + TimeEncoder: zapcore.ISO8601TimeEncoder, + })) + + err := GenerateGitopsBase(ctx, log, fakeClient, *tt.component, tt.fs, tt.gitopsParams) + if (err != nil) != tt.wantErr { + t.Errorf("TestGenerateGitops() unexpected error: %v", err) + } + }) + } +} + +func TestGenerateGitopsOverlays(t *testing.T) { + appFS := ioutils.NewMemoryFilesystem() + readOnlyFs := ioutils.NewReadOnlyFs() + ctx := context.Background() + scheme := runtime.NewScheme() + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + appstudiov1alpha1.AddToScheme(scheme) + fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build() + + errGen := NewMockGenerator() + errGen.Errors.Push(errors.New("Fatal error")) + + // Before the test runs, make sure that Application, Component and associated resources all exist + setUpResources(t, &fakeClient, ctx) + newComponent := appstudiov1alpha1.Component{} + err := fakeClient.Get(ctx, types.NamespacedName{Namespace: "test-namespace", Name: "test-component"}, &newComponent) + if err != nil { + t.Error(err) + } + + // After the prerequisite resources have been set up, make sure it exists + snapshotEnvironmentBinding := appstudiov1alpha1.SnapshotEnvironmentBinding{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "appstudio.redhat.com/v1alpha1", + Kind: "SnapshotEnvironmentBinding", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-seb", + Namespace: "test-namespace", + }, + Spec: appstudiov1alpha1.SnapshotEnvironmentBindingSpec{ + Application: "test-application", + Environment: "test-environment", + Snapshot: "test-snapshot", + Components: []appstudiov1alpha1.BindingComponent{ + { + Name: "test-component", + Configuration: appstudiov1alpha1.BindingComponentConfiguration{ + Env: []appstudiov1alpha1.EnvVarPair{ + { + Name: "FOO", + Value: "BAR", + }, + }, + }, + }, + }, + }, + } + err = fakeClient.Create(ctx, &snapshotEnvironmentBinding) + if err != nil { + t.Error(err) + } + + tests := []struct { + name string + fs afero.Afero + seb *appstudiov1alpha1.SnapshotEnvironmentBinding + gitopsParams GitOpsGenParams + wantErr bool + }{ + { + name: "Gitops generation succeeds", + fs: appFS, + seb: &snapshotEnvironmentBinding, + gitopsParams: GitOpsGenParams{ + Generator: NewMockGenerator(), + }, + }, + { + name: "Gitops generation succeeds - seb doesn't exist", + fs: appFS, + seb: &appstudiov1alpha1.SnapshotEnvironmentBinding{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "appstudio.redhat.com/v1alpha1", + Kind: "SnapshotEnvironmentBinding", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "fake-seb", + Namespace: "fake-namespace", + }, + }, + gitopsParams: GitOpsGenParams{ + Generator: NewMockGenerator(), + }, + wantErr: true, + }, + { + name: "Gitops generation error - file system error", + fs: readOnlyFs, + seb: &snapshotEnvironmentBinding, + gitopsParams: GitOpsGenParams{ + Generator: NewMockGenerator(), + }, + wantErr: true, + }, + { + name: "Gitops generation error - application doesn't exist", + fs: appFS, + seb: &appstudiov1alpha1.SnapshotEnvironmentBinding{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "appstudio.redhat.com/v1alpha1", + Kind: "SnapshotEnvironmentBinding", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "fake-seb", + Namespace: "test-namespace", + }, + Spec: appstudiov1alpha1.SnapshotEnvironmentBindingSpec{ + Application: "app-that-doesnt-exist", + Environment: "test-environment", + Snapshot: "test-snapshot", + }, + }, + gitopsParams: GitOpsGenParams{ + Generator: NewMockGenerator(), + }, + wantErr: true, + }, + { + name: "Gitops generation error - environment doesn't exist", + fs: appFS, + seb: &appstudiov1alpha1.SnapshotEnvironmentBinding{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "appstudio.redhat.com/v1alpha1", + Kind: "SnapshotEnvironmentBinding", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "fake-seb", + Namespace: "test-namespace", + }, + Spec: appstudiov1alpha1.SnapshotEnvironmentBindingSpec{ + Application: "app-that-doesnt-exist", + Snapshot: "test-snapshot", + }, + }, + gitopsParams: GitOpsGenParams{ + Generator: NewMockGenerator(), + }, + wantErr: true, + }, + { + name: "Gitops generation error - snapshot doesn't exist", + fs: appFS, + seb: &appstudiov1alpha1.SnapshotEnvironmentBinding{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "appstudio.redhat.com/v1alpha1", + Kind: "SnapshotEnvironmentBinding", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "fake-seb", + Namespace: "test-namespace", + }, + Spec: appstudiov1alpha1.SnapshotEnvironmentBindingSpec{ + Application: "app-that-doesnt-exist", + Environment: "test-environment", + }, + }, + gitopsParams: GitOpsGenParams{ + Generator: NewMockGenerator(), + }, + wantErr: true, + }, + { + name: "Gitops generation error - snapshot doesn't exist", + fs: appFS, + seb: &appstudiov1alpha1.SnapshotEnvironmentBinding{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "appstudio.redhat.com/v1alpha1", + Kind: "SnapshotEnvironmentBinding", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "fake-seb", + Namespace: "test-namespace", + }, + Spec: appstudiov1alpha1.SnapshotEnvironmentBindingSpec{ + Application: "test-application", + Environment: "test-environment", + Snapshot: "test-snapshot", + Components: []appstudiov1alpha1.BindingComponent{ + { + Name: "non-existent-component", + }, + }, + }, + }, + gitopsParams: GitOpsGenParams{ + Generator: NewMockGenerator(), + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + log := zap.New(zap.UseFlagOptions(&zap.Options{ + Development: true, + TimeEncoder: zapcore.ISO8601TimeEncoder, + })) + err := GenerateGitopsOverlays(ctx, log, fakeClient, *tt.seb, tt.fs, tt.gitopsParams) + if (err != nil) != tt.wantErr { + t.Errorf("TestGenerateGitops() unexpected error: %v", err) + } + }) + } +} + +// setUpResources sets up the necessary Kubernetes resources for the TestGenerateGitopsOverlays test +// The following resources need to be created before the test can be run: +// Component, Environment, Snapshot, SnapshotEnvironmentBinding +func setUpResources(t *testing.T, client *client.WithWatch, ctx context.Context) { + // Create the Component + kubeClient := *client + component := appstudiov1alpha1.Component{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "appstudio.redhat.com/v1alpha1", + Kind: "Component", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-component", + Namespace: "test-namespace", + }, + Spec: appstudiov1alpha1.ComponentSpec{ + ComponentName: "test-component", + Application: "test-application", + }, + Status: appstudiov1alpha1.ComponentStatus{ + GitOps: appstudiov1alpha1.GitOpsStatus{ + RepositoryURL: "https://github.com/testorg/repo", + Branch: "main", + Context: "/", + }, + Devfile: kubernetesInlinedDevfile, + }, + } + err := kubeClient.Create(ctx, &component) + if err != nil { + t.Error(err) + } + + // Create the Environment + environment := appstudiov1alpha1.Environment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "appstudio.redhat.com/v1alpha1", + Kind: "Environment", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-environment", + Namespace: "test-namespace", + }, + Spec: appstudiov1alpha1.EnvironmentSpec{ + Type: appstudiov1alpha1.EnvironmentType_POC, + DisplayName: "Staging Environment", + DeploymentStrategy: appstudiov1alpha1.DeploymentStrategy_AppStudioAutomated, + Configuration: appstudiov1alpha1.EnvironmentConfiguration{ + Env: []appstudiov1alpha1.EnvVarPair{ + { + Name: "Test", + Value: "Value", + }, + }, + }, + }, + } + err = kubeClient.Create(ctx, &environment) + if err != nil { + t.Error(err) + } + + // Create the Snapshot + snapshot := appstudiov1alpha1.Snapshot{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "appstudio.redhat.com/v1alpha1", + Kind: "Snapshot", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-snapshot", + Namespace: "test-namespace", + }, + Spec: appstudiov1alpha1.SnapshotSpec{ + Application: "test-application", + DisplayName: "Test Snapshot", + DisplayDescription: "My First Snapshot", + Components: []appstudiov1alpha1.SnapshotComponent{ + { + Name: "test-component", + ContainerImage: "quay.io/redhat-appstudio/user-workload:application-service-system-test-component", + }, + }, + }, + } + err = kubeClient.Create(ctx, &snapshot) + if err != nil { + t.Error(err) + } +} diff --git a/gitops-generator/pkg/util/util.go b/gitops-generator/pkg/util/util.go new file mode 100644 index 00000000..2f4dd8ad --- /dev/null +++ b/gitops-generator/pkg/util/util.go @@ -0,0 +1,159 @@ +// +// Copyright 2023 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package util + +import ( + "fmt" + "math/rand" + "net/url" + "reflect" + "strings" + + "github.com/devfile/library/v2/pkg/devfile/parser" + appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" + gitopsgenv1alpha1 "github.com/redhat-developer/gitops-generator/api/v1alpha1" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" +) + +var RevisionHistoryLimit = int32(0) + +const schemaBytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + +// GetMappedGitOpsComponent gets a mapped GeneratorOptions from the Component for GitOps resource generation +func GetMappedGitOpsComponent(component appstudiov1alpha1.Component, kubernetesResources parser.KubernetesResources) gitopsgenv1alpha1.GeneratorOptions { + customK8sLabels := map[string]string{ + "app.kubernetes.io/name": component.Spec.ComponentName, + "app.kubernetes.io/instance": component.Name, + "app.kubernetes.io/part-of": component.Spec.Application, + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/created-by": "application-service", + } + gitopsMapComponent := gitopsgenv1alpha1.GeneratorOptions{ + Name: component.ObjectMeta.Name, + Application: component.Spec.Application, + Secret: component.Spec.Secret, + Resources: component.Spec.Resources, + Replicas: GetIntValue(component.Spec.Replicas), + TargetPort: component.Spec.TargetPort, + Route: component.Spec.Route, + BaseEnvVar: component.Spec.Env, + ContainerImage: component.Spec.ContainerImage, + K8sLabels: customK8sLabels, + RevisionHistoryLimit: &RevisionHistoryLimit, + } + if component.Spec.Source.ComponentSourceUnion.GitSource != nil { + gitopsMapComponent.GitSource = &gitopsgenv1alpha1.GitSource{ + URL: component.Spec.Source.ComponentSourceUnion.GitSource.URL, + } + } else { + gitopsMapComponent.GitSource = &gitopsgenv1alpha1.GitSource{} + } + + // If the resource requests or limits were unset, set default values + if gitopsMapComponent.Resources.Requests == nil { + gitopsMapComponent.Resources.Requests = v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("10m"), + v1.ResourceMemory: resource.MustParse("50Mi"), + } + } + if gitopsMapComponent.Resources.Limits == nil { + gitopsMapComponent.Resources.Limits = v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("1"), + v1.ResourceMemory: resource.MustParse("512Mi"), + } + } + + if !reflect.DeepEqual(kubernetesResources, parser.KubernetesResources{}) { + gitopsMapComponent.KubernetesResources.Deployments = append(gitopsMapComponent.KubernetesResources.Deployments, kubernetesResources.Deployments...) + gitopsMapComponent.KubernetesResources.Services = append(gitopsMapComponent.KubernetesResources.Services, kubernetesResources.Services...) + gitopsMapComponent.KubernetesResources.Routes = append(gitopsMapComponent.KubernetesResources.Routes, kubernetesResources.Routes...) + gitopsMapComponent.KubernetesResources.Ingresses = append(gitopsMapComponent.KubernetesResources.Ingresses, kubernetesResources.Ingresses...) + gitopsMapComponent.KubernetesResources.Others = append(gitopsMapComponent.KubernetesResources.Others, kubernetesResources.Others...) + } + + return gitopsMapComponent +} + +// GetIntValue returns the value of an int pointer, with the default of 0 if nil +func GetIntValue(intPtr *int) int { + if intPtr != nil { + return *intPtr + } + + return 0 +} + +// GenerateRandomRouteName returns a random, trimmed route name based on the Component name based on the following criteria +// 1. Under 30 characters +// 2. Contains 4 random characters +func GenerateRandomRouteName(componentName string) string { + routeName := componentName + if len(componentName) > 25 { + routeName = componentName[0:25] + } else { + routeName = componentName + } + + // Append random characters to the route name + routeName = routeName + GetRandomString(4, true) + return routeName +} + +// GetRandomString returns a random string which is n characters long. +// If lower is set to true a lower case string is returned. +func GetRandomString(n int, lower bool) string { + b := make([]byte, n) + for i := range b { + /* #nosec G404 -- not used for cryptographic purposes*/ + b[i] = schemaBytes[rand.Intn(len(schemaBytes)-1)] + } + randomString := string(b) + if lower { + randomString = strings.ToLower(randomString) + } + return randomString +} + +// ProcessGitOpsStatus processes the GitOps status and returns the remote url, branch, context and the error +func ProcessGitOpsStatus(gitopsStatus appstudiov1alpha1.GitOpsStatus, gitToken string) (string, string, string, error) { + var gitOpsURL, gitOpsBranch, gitOpsContext string + gitOpsURL = gitopsStatus.RepositoryURL + if gitOpsURL == "" { + err := fmt.Errorf("unable to process GitOps status, GitOps Repository URL cannot be empty") + return "", "", "", err + } + if gitopsStatus.Branch != "" { + gitOpsBranch = gitopsStatus.Branch + } else { + gitOpsBranch = "main" + } + if gitopsStatus.Context != "" { + gitOpsContext = gitopsStatus.Context + } else { + gitOpsContext = "/" + } + + // Construct the remote URL for the gitops repository + parsedURL, err := url.Parse(gitOpsURL) + if err != nil { + return "", "", "", err + } + parsedURL.User = url.User(gitToken) + remoteURL := parsedURL.String() + + return remoteURL, gitOpsBranch, gitOpsContext, nil +} diff --git a/gitops-generator/pkg/util/util_test.go b/gitops-generator/pkg/util/util_test.go new file mode 100644 index 00000000..121a3808 --- /dev/null +++ b/gitops-generator/pkg/util/util_test.go @@ -0,0 +1,529 @@ +package util + +import ( + "reflect" + "strings" + "testing" + + "github.com/devfile/library/v2/pkg/devfile/parser" + appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" + gitopsgenv1alpha1 "github.com/redhat-developer/gitops-generator/api/v1alpha1" + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/api/resource" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestGetIntValue(t *testing.T) { + + value := 7 + + tests := []struct { + name string + replica *int + wantValue int + wantErr bool + }{ + { + name: "Unset value, expect default 0", + replica: nil, + wantValue: 0, + }, + { + name: "set value, expect set number", + replica: &value, + wantValue: 7, + }, + } + + for _, tt := range tests { + val := GetIntValue(tt.replica) + assert.True(t, val == tt.wantValue, "Expected int value %d got %d", tt.wantValue, val) + } +} + +func TestGenerateRandomRouteName(t *testing.T) { + + tests := []struct { + name string + componentName string + }{ + { + name: "Simple component name, less than 25 characters", + componentName: "test-comp", + }, + { + name: "long component name", + componentName: "test-test-test-test-test-test-test-test-test", + }, + } + + for _, tt := range tests { + routeName := GenerateRandomRouteName(tt.componentName) + if len(routeName) >= 30 { + t.Errorf("TestGenerateRandomRouteName() error: expected generated route name %s to be less than 30 chars", routeName) + } + if tt.name == "long component name" { + if !strings.Contains(routeName, tt.componentName[0:25]) { + t.Errorf("TestGenerateRandomRouteName() error: expected generated route name %s to contain first 25 chars of component name %s", routeName, tt.componentName) + } + if routeName == tt.componentName[0:25] { + t.Errorf("TestGenerateRandomRouteName() error: expected generated route name %s to contain 25 char slice from component name %s", routeName, tt.componentName) + } + } else { + if !strings.Contains(routeName, tt.componentName) { + t.Errorf("TestGenerateRandomRouteName() error: expected generated route name %s to contain component name %s", routeName, tt.componentName) + } + if routeName == tt.componentName { + t.Errorf("TestGenerateRandomRouteName() error: expected generated route name %s to be unique from component name %s", routeName, tt.componentName) + } + } + + } +} + +func TestGetRandomString(t *testing.T) { + tests := []struct { + name string + length int + lower bool + }{ + { + name: "all lower case string", + length: 5, + lower: true, + }, + { + name: "contain upper case string", + length: 10, + lower: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotString := GetRandomString(tt.length, tt.lower) + assert.Equal(t, tt.length, len(gotString), "the values should match") + + if tt.lower == true { + assert.Equal(t, strings.ToLower(gotString), gotString, "the values should match") + } + + gotString2 := GetRandomString(tt.length, tt.lower) + assert.NotEqual(t, gotString, gotString2, "the two random string should not be the same") + }) + } +} + +func TestProcessGitOpsStatus(t *testing.T) { + tests := []struct { + name string + gitopsStatus appstudiov1alpha1.GitOpsStatus + gitToken string + wantURL string + wantBranch string + wantContext string + wantErr bool + }{ + { + name: "gitops status processed as expected", + gitopsStatus: appstudiov1alpha1.GitOpsStatus{ + RepositoryURL: "https://github.com/myrepo", + Branch: "notmain", + Context: "context", + }, + gitToken: "token", + wantURL: "https://token@github.com/myrepo", + wantBranch: "notmain", + wantContext: "context", + }, + { + name: "gitops url is empty", + gitopsStatus: appstudiov1alpha1.GitOpsStatus{ + RepositoryURL: "", + }, + wantErr: true, + }, + { + name: "gitops branch and context not set", + gitopsStatus: appstudiov1alpha1.GitOpsStatus{ + RepositoryURL: "https://github.com/myrepo", + }, + gitToken: "token", + wantURL: "https://token@github.com/myrepo", + wantBranch: "main", + wantContext: "/", + }, + { + name: "gitops url parse err", + gitopsStatus: appstudiov1alpha1.GitOpsStatus{ + RepositoryURL: "http://foo.com/?foo\nbar", + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gitopsURL, gitopsBranch, gitopsContext, err := ProcessGitOpsStatus(tt.gitopsStatus, tt.gitToken) + if tt.wantErr && (err == nil) { + t.Error("wanted error but got nil") + } else if !tt.wantErr && err != nil { + t.Errorf("got unexpected error %v", err) + } else { + assert.Equal(t, tt.wantURL, gitopsURL, "should be equal") + assert.Equal(t, tt.wantBranch, gitopsBranch, "should be equal") + assert.Equal(t, tt.wantContext, gitopsContext, "should be equal") + } + }) + } +} + +func TestGetMappedComponent(t *testing.T) { + + other := make([]interface{}, 1) + other[0] = appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment1", + }, + } + + tests := []struct { + name string + component appstudiov1alpha1.Component + kubernetesResources parser.KubernetesResources + want gitopsgenv1alpha1.GeneratorOptions + }{ + { + name: "Test01ComponentSpecFilledIn", + component: appstudiov1alpha1.Component{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testcomponent", + }, + Spec: appstudiov1alpha1.ComponentSpec{ + ComponentName: "frontEnd", + Application: "AppTest001", + Secret: "Secret", + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceLimitsCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Gi"), + }, + }, + TargetPort: 8080, + Route: "https://testroute", + Env: []corev1.EnvVar{ + { + Name: "env1", + Value: "env1Value", + }, + }, + ContainerImage: "myimage:image", + SkipGitOpsResourceGeneration: false, + Source: appstudiov1alpha1.ComponentSource{ + ComponentSourceUnion: appstudiov1alpha1.ComponentSourceUnion{ + GitSource: &appstudiov1alpha1.GitSource{ + URL: "https://host/git-repo.git", + Revision: "1.0", + Context: "/context", + DevfileURL: "https://mydevfileurl", + DockerfileURL: "https://mydockerfileurl", + }, + }, + }, + }, + }, + want: gitopsgenv1alpha1.GeneratorOptions{ + Name: "testcomponent", + Application: "AppTest001", + Secret: "Secret", + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("10m"), + corev1.ResourceMemory: resource.MustParse("50Mi"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceLimitsCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Gi"), + }, + }, + TargetPort: 8080, + Route: "https://testroute", + BaseEnvVar: []corev1.EnvVar{ + { + Name: "env1", + Value: "env1Value", + }, + }, + ContainerImage: "myimage:image", + GitSource: &gitopsgenv1alpha1.GitSource{ + URL: "https://host/git-repo.git", + }, + }, + }, + { + name: "Test02EmptyComponentSource", + component: appstudiov1alpha1.Component{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testcomponent", + }, + Spec: appstudiov1alpha1.ComponentSpec{ + ComponentName: "frontEnd", + Application: "AppTest002", + Source: appstudiov1alpha1.ComponentSource{}, + }, + }, + want: gitopsgenv1alpha1.GeneratorOptions{ + Name: "testcomponent", + Application: "AppTest002", + GitSource: &gitopsgenv1alpha1.GitSource{}, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("10m"), + corev1.ResourceMemory: resource.MustParse("50Mi"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("512Mi"), + }, + }, + }, + }, + { + name: "Test03NoSource", + component: appstudiov1alpha1.Component{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testcomponent", + }, + Spec: appstudiov1alpha1.ComponentSpec{ + ComponentName: "frontEnd", + Application: "AppTest003", + }, + }, + want: gitopsgenv1alpha1.GeneratorOptions{ + Name: "testcomponent", + Application: "AppTest003", + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("10m"), + corev1.ResourceMemory: resource.MustParse("50Mi"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("512Mi"), + }, + }, + }, + }, + { + name: "Test04EmptyComponentSourceUnion", + component: appstudiov1alpha1.Component{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testcomponent", + }, + Spec: appstudiov1alpha1.ComponentSpec{ + ComponentName: "frontEnd", + Application: "AppTest004", + Source: appstudiov1alpha1.ComponentSource{ + ComponentSourceUnion: appstudiov1alpha1.ComponentSourceUnion{}, + }, + }, + }, + want: gitopsgenv1alpha1.GeneratorOptions{ + Name: "testcomponent", + Application: "AppTest004", + GitSource: &gitopsgenv1alpha1.GitSource{}, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("10m"), + corev1.ResourceMemory: resource.MustParse("50Mi"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("512Mi"), + }, + }, + }, + }, + { + name: "Test05EmptyGitSource", + component: appstudiov1alpha1.Component{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testcomponent", + }, + Spec: appstudiov1alpha1.ComponentSpec{ + ComponentName: "frontEnd", + Application: "AppTest005", + Source: appstudiov1alpha1.ComponentSource{ + ComponentSourceUnion: appstudiov1alpha1.ComponentSourceUnion{ + GitSource: &appstudiov1alpha1.GitSource{}, + }, + }, + }, + }, + want: gitopsgenv1alpha1.GeneratorOptions{ + Name: "testcomponent", + Application: "AppTest005", + GitSource: &gitopsgenv1alpha1.GitSource{}, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("10m"), + corev1.ResourceMemory: resource.MustParse("50Mi"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("512Mi"), + }, + }, + }, + }, + { + name: "Test06KubernetesResources", + component: appstudiov1alpha1.Component{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testcomponent", + }, + Spec: appstudiov1alpha1.ComponentSpec{ + ComponentName: "frontEnd", + Application: "AppTest005", + Source: appstudiov1alpha1.ComponentSource{ + ComponentSourceUnion: appstudiov1alpha1.ComponentSourceUnion{ + GitSource: &appstudiov1alpha1.GitSource{ + URL: "url", + }, + }, + }, + }, + }, + kubernetesResources: parser.KubernetesResources{ + Deployments: []appsv1.Deployment{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment1", + }, + }, + }, + Services: []corev1.Service{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "service1", + }, + }, + }, + Others: other, + }, + want: gitopsgenv1alpha1.GeneratorOptions{ + Name: "testcomponent", + Application: "AppTest005", + GitSource: &gitopsgenv1alpha1.GitSource{ + URL: "url", + }, + KubernetesResources: gitopsgenv1alpha1.KubernetesResources{ + Deployments: []appsv1.Deployment{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment1", + }, + }, + }, + Services: []corev1.Service{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "service1", + }, + }, + }, + Others: other, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("10m"), + corev1.ResourceMemory: resource.MustParse("50Mi"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("512Mi"), + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mappedComponent := GetMappedGitOpsComponent(tt.component, tt.kubernetesResources) + assert.True(t, tt.want.Name == mappedComponent.Name, "Expected ObjectMeta.Name: %s, is different than actual: %s", tt.want.Name, mappedComponent.Name) + assert.True(t, tt.want.Application == mappedComponent.Application, "Expected Spec.Application: %s, is different than actual: %s", tt.want.Application, mappedComponent.Application) + assert.True(t, tt.want.Secret == mappedComponent.Secret, "Expected Spec.Secret: %s, is different than actual: %s", tt.want.Secret, mappedComponent.Secret) + assert.True(t, reflect.DeepEqual(tt.want.Resources, mappedComponent.Resources), "Expected Spec.Resources: %s, is different than actual: %s", tt.want.Resources, mappedComponent.Resources) + assert.True(t, tt.want.Route == mappedComponent.Route, "Expected Spec.Route: %s, is different than actual: %s", tt.want.Route, mappedComponent.Route) + assert.True(t, reflect.DeepEqual(tt.want.BaseEnvVar, mappedComponent.BaseEnvVar), "Expected Spec.Env: %s, is different than actual: %s", tt.want.BaseEnvVar, mappedComponent.BaseEnvVar) + assert.True(t, tt.want.ContainerImage == mappedComponent.ContainerImage, "Expected Spec.ContainerImage: %s, is different than actual: %s", tt.want.ContainerImage, mappedComponent.ContainerImage) + + if tt.want.GitSource != nil { + assert.True(t, tt.want.GitSource.URL == mappedComponent.GitSource.URL, "Expected GitSource URL: %s, is different than actual: %s", tt.want.GitSource.URL, mappedComponent.GitSource.URL) + } + + if !reflect.DeepEqual(tt.want.KubernetesResources, gitopsgenv1alpha1.KubernetesResources{}) { + for _, wantDeployment := range tt.want.KubernetesResources.Deployments { + matched := false + for _, gotDeployment := range mappedComponent.KubernetesResources.Deployments { + if wantDeployment.Name == gotDeployment.Name { + matched = true + break + } + } + assert.True(t, matched, "Expected Deployment: %s, but didnt find in actual", wantDeployment.Name) + } + + for _, wantService := range tt.want.KubernetesResources.Services { + matched := false + for _, gotService := range mappedComponent.KubernetesResources.Services { + if wantService.Name == gotService.Name { + matched = true + break + } + } + assert.True(t, matched, "Expected Service: %s, but didnt find in actual", wantService.Name) + } + + for _, wantRoute := range tt.want.KubernetesResources.Routes { + matched := false + for _, gotRoute := range mappedComponent.KubernetesResources.Routes { + if wantRoute.Name == gotRoute.Name { + matched = true + break + } + } + assert.True(t, matched, "Expected Route: %s, but didnt find in actual", wantRoute.Name) + } + + for _, wantIngress := range tt.want.KubernetesResources.Ingresses { + matched := false + for _, gotIngress := range mappedComponent.KubernetesResources.Ingresses { + if wantIngress.Name == gotIngress.Name { + matched = true + break + } + } + assert.True(t, matched, "Expected Ingress: %s, but didnt find in actual", wantIngress.Name) + } + + for _, wantOther := range tt.want.KubernetesResources.Others { + matched := false + wantDeployment := wantOther.(appsv1.Deployment) + + for _, gotOther := range mappedComponent.KubernetesResources.Others { + gotDeployment := gotOther.(appsv1.Deployment) + if wantDeployment.Name == gotDeployment.Name { + matched = true + break + } + } + assert.True(t, matched, "Expected Other: %s, but didnt find in actual", wantDeployment.Name) + } + } + }) + } +} diff --git a/gitops/generate.go b/gitops/generate.go deleted file mode 100644 index c05c1f11..00000000 --- a/gitops/generate.go +++ /dev/null @@ -1,52 +0,0 @@ -// -// Copyright 2021-2022 Red Hat, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gitops - -import ( - "fmt" - "path/filepath" - - appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" - "github.com/redhat-appstudio/application-service/gitops/prepare" - gitopsgen "github.com/redhat-developer/gitops-generator/pkg" - "github.com/redhat-developer/gitops-generator/pkg/util" - "github.com/spf13/afero" -) - -const ( - kustomizeFileName = "kustomization.yaml" -) - -// GenerateTektonBuild writes a set of YAML configuration files into outputPath for the component. -func GenerateTektonBuild(outputPath string, component appstudiov1alpha1.Component, appFs afero.Afero, context string, gitopsConfig prepare.GitopsConfig) error { - componentName := component.Name - repoPath := filepath.Join(outputPath, componentName) - gitopsFolder := filepath.Join(repoPath, context) - componentPath := filepath.Join(gitopsFolder, "components", componentName, "base") - - if component.Spec.Source.GitSource != nil && component.Spec.Source.GitSource.URL != "" { - tektonResourcesDirName := ".tekton" - - if err := GenerateBuild(appFs, filepath.Join(componentPath, tektonResourcesDirName), component, gitopsConfig); err != nil { - return util.SanitizeErrorMessage(fmt.Errorf("failed to generate tekton build in %q for component %q: %s", componentPath, componentName, err)) - } - // Update the kustomize file and return - if err := gitopsgen.UpdateExistingKustomize(appFs, componentPath); err != nil { - return util.SanitizeErrorMessage(fmt.Errorf("failed to update kustomize file for tekton build in %q for component %q: %s", componentPath, componentName, err)) - } - } - return nil -} diff --git a/gitops/generate_build.go b/gitops/generate_build.go deleted file mode 100644 index 9558da00..00000000 --- a/gitops/generate_build.go +++ /dev/null @@ -1,202 +0,0 @@ -/* -Copyright 2021 Red Hat, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package gitops - -import ( - "fmt" - "net/url" - "regexp" - "strings" - - "github.com/redhat-developer/gitops-generator/pkg/resources" - "github.com/redhat-developer/gitops-generator/pkg/yaml" - - pacv1alpha1 "github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1" - appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" - gitopsprepare "github.com/redhat-appstudio/application-service/gitops/prepare" - "github.com/spf13/afero" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ( - buildTriggerTemplateFileName = "trigger-template.yaml" - buildEventListenerFileName = "event-listener.yaml" - buildWebhookRouteFileName = "build-webhook-route.yaml" - buildRepositoryFileName = "pac-repository.yaml" - - PaCAnnotation = "pipelinesascode" - GitProviderAnnotationName = "git-provider" - PipelinesAsCodeWebhooksSecretName = "pipelines-as-code-webhooks-secret" - PipelinesAsCode_githubAppIdKey = "github-application-id" - PipelinesAsCode_githubPrivateKey = "github-private-key" -) - -func GenerateBuild(fs afero.Fs, outputFolder string, component appstudiov1alpha1.Component, gitopsConfig gitopsprepare.GitopsConfig) error { - repository, err := GeneratePACRepository(component, gitopsConfig.PipelinesAsCodeCredentials) - if err != nil { - return err - } - - buildResources := map[string]interface{}{ - buildRepositoryFileName: repository, - } - - kustomize := resources.Kustomization{} - for fileName := range buildResources { - kustomize.AddResources(fileName) - } - - buildResources[kustomizeFileName] = kustomize - - if _, err := yaml.WriteResources(fs, outputFolder, buildResources); err != nil { - return err - } - return nil -} - -func getBuildCommonLabelsForComponent(component *appstudiov1alpha1.Component) map[string]string { - labels := map[string]string{ - "pipelines.appstudio.openshift.io/type": "build", - "build.appstudio.openshift.io/build": "true", - "build.appstudio.openshift.io/type": "build", - "build.appstudio.openshift.io/version": "0.1", - "appstudio.openshift.io/component": component.Name, - "appstudio.openshift.io/application": component.Spec.Application, - } - return labels -} - -// GeneratePACRepository creates configuration of Pipelines as Code repository object. -func GeneratePACRepository(component appstudiov1alpha1.Component, config map[string][]byte) (*pacv1alpha1.Repository, error) { - gitProvider, err := GetGitProvider(component) - if err != nil { - return nil, err - } - - isAppUsed := IsPaCApplicationConfigured(gitProvider, config) - - var gitProviderConfig *pacv1alpha1.GitProvider = nil - if !isAppUsed { - // Webhook is used - gitProviderConfig = &pacv1alpha1.GitProvider{ - Secret: &pacv1alpha1.Secret{ - Name: gitopsprepare.PipelinesAsCodeSecretName, - Key: GetProviderTokenKey(gitProvider), - }, - WebhookSecret: &pacv1alpha1.Secret{ - Name: PipelinesAsCodeWebhooksSecretName, - Key: GetWebhookSecretKeyForComponent(component), - }, - } - - if gitProvider == "gitlab" { - gitProviderConfig.URL = "https://gitlab.com" - } - } - - repository := &pacv1alpha1.Repository{ - TypeMeta: metav1.TypeMeta{ - Kind: "Repository", - APIVersion: "pipelinesascode.tekton.dev/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: component.Name, - Namespace: component.Namespace, - Annotations: getBuildCommonLabelsForComponent(&component), - }, - Spec: pacv1alpha1.RepositorySpec{ - URL: strings.TrimSuffix(strings.TrimSuffix(component.Spec.Source.GitSource.URL, ".git"), "/"), - GitProvider: gitProviderConfig, - }, - } - - return repository, nil -} - -// GetProviderTokenKey returns key (field name) of the given provider access token in the Pipelines as Code k8s secret -func GetProviderTokenKey(gitProvider string) string { - return gitProvider + ".token" -} - -func GetWebhookSecretKeyForComponent(component appstudiov1alpha1.Component) string { - gitRepoUrl := strings.TrimSuffix(component.Spec.Source.GitSource.URL, ".git") - - notAllowedCharRegex, _ := regexp.Compile("[^-._a-zA-Z0-9]{1}") - return notAllowedCharRegex.ReplaceAllString(gitRepoUrl, "_") -} - -// GetGitProvider returns git provider name based on the repository url, e.g. github, gitlab, etc or git-privider annotation -func GetGitProvider(component appstudiov1alpha1.Component) (string, error) { - allowedGitProviders := map[string]bool{"github": true, "gitlab": true, "bitbucket": true} - gitProvider := "" - - sourceUrl := component.Spec.Source.GitSource.URL - - if strings.HasPrefix(sourceUrl, "git@") { - // git@github.com:redhat-appstudio/application-service.git - sourceUrl = strings.TrimPrefix(sourceUrl, "git@") - host := strings.Split(sourceUrl, ":")[0] - gitProvider = strings.Split(host, ".")[0] - } else { - // https://github.com/redhat-appstudio/application-service - u, err := url.Parse(sourceUrl) - if err != nil { - return "", err - } - uParts := strings.Split(u.Hostname(), ".") - if len(uParts) == 1 { - gitProvider = uParts[0] - } else { - gitProvider = uParts[len(uParts)-2] - } - } - - var err error - if !allowedGitProviders[gitProvider] { - // Self-hosted git provider, check for git-provider annotation on the component - gitProviderAnnotationValue := component.GetAnnotations()[GitProviderAnnotationName] - if gitProviderAnnotationValue != "" { - if allowedGitProviders[gitProviderAnnotationValue] { - gitProvider = gitProviderAnnotationValue - } else { - err = fmt.Errorf("unsupported \"%s\" annotation value: %s", GitProviderAnnotationName, gitProviderAnnotationValue) - } - } else { - err = fmt.Errorf("self-hosted git provider is not specified via \"%s\" annotation in the component", GitProviderAnnotationName) - } - } - - return gitProvider, err -} - -// IsPaCApplicationConfigured checks if Pipelines as Code credentials configured for given provider. -// Application is preffered over webhook if possible. -func IsPaCApplicationConfigured(gitProvider string, config map[string][]byte) bool { - isAppUsed := false - - switch gitProvider { - case "github": - if len(config[PipelinesAsCode_githubAppIdKey]) != 0 || len(config[PipelinesAsCode_githubPrivateKey]) != 0 { - isAppUsed = true - } - default: - // Application is not supported - isAppUsed = false - } - - return isAppUsed -} diff --git a/gitops/generate_build_test.go b/gitops/generate_build_test.go deleted file mode 100644 index 5085dee0..00000000 --- a/gitops/generate_build_test.go +++ /dev/null @@ -1,507 +0,0 @@ -/* -Copyright 2021 Red Hat, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package gitops - -import ( - "path/filepath" - "reflect" - "strings" - "testing" - - "github.com/mitchellh/go-homedir" - pacv1alpha1 "github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1" - appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" - gitopsprepare "github.com/redhat-appstudio/application-service/gitops/prepare" - "github.com/redhat-appstudio/application-service/pkg/util/ioutils" - "github.com/redhat-developer/gitops-generator/pkg/testutils" - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestGenerateBuild(t *testing.T) { - outoutFolder := "output" - emptyGitopsConfig := gitopsprepare.GitopsConfig{} - - tests := []struct { - name string - fs afero.Afero - component appstudiov1alpha1.Component - gitopsConfig gitopsprepare.GitopsConfig - want []string - }{ - { - name: "Check pipeline as code resources with annotation", - fs: ioutils.NewMemoryFilesystem(), - component: appstudiov1alpha1.Component{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testcomponent", - Namespace: "workspace-name", - Annotations: map[string]string{ - PaCAnnotation: "1", - }, - }, - Spec: appstudiov1alpha1.ComponentSpec{ - Source: appstudiov1alpha1.ComponentSource{ - ComponentSourceUnion: appstudiov1alpha1.ComponentSourceUnion{ - GitSource: &appstudiov1alpha1.GitSource{ - URL: "https://github.com/user/git-repo.git", - }, - }, - }, - }, - }, - gitopsConfig: emptyGitopsConfig, - want: []string{ - kustomizeFileName, - buildRepositoryFileName, - }, - }, - { - name: "Check pipeline as code resources on HACBS", - fs: ioutils.NewMemoryFilesystem(), - component: appstudiov1alpha1.Component{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testcomponent", - Namespace: "workspace-name", - }, - Spec: appstudiov1alpha1.ComponentSpec{ - Source: appstudiov1alpha1.ComponentSource{ - ComponentSourceUnion: appstudiov1alpha1.ComponentSourceUnion{ - GitSource: &appstudiov1alpha1.GitSource{ - URL: "https://github.com/user/git-repo.git", - }, - }, - }, - }, - }, - gitopsConfig: emptyGitopsConfig, - want: []string{ - kustomizeFileName, - buildRepositoryFileName, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := GenerateBuild(tt.fs, outoutFolder, tt.component, tt.gitopsConfig); err != nil { - t.Errorf("Failed to generate builf gitops resources. Cause: %v", err) - } - - // Ensure that needed resources generated - path, err := homedir.Expand(outoutFolder) - testutils.AssertNoError(t, err) - - for _, item := range tt.want { - exist, err := tt.fs.Exists(filepath.Join(path, item)) - testutils.AssertNoError(t, err) - assert.True(t, exist, "Expected file %s missing in gitops", item) - } - }) - } -} - -func TestGeneratePACRepository(t *testing.T) { - getComponent := func(repoUrl string) appstudiov1alpha1.Component { - return appstudiov1alpha1.Component{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testcomponent", - Namespace: "workspace-name", - }, - Spec: appstudiov1alpha1.ComponentSpec{ - Source: appstudiov1alpha1.ComponentSource{ - ComponentSourceUnion: appstudiov1alpha1.ComponentSourceUnion{ - GitSource: &appstudiov1alpha1.GitSource{ - URL: repoUrl, - }, - }, - }, - }, - } - } - - tests := []struct { - name string - repoUrl string - pacConfig map[string][]byte - expectedGitProviderConfig *pacv1alpha1.GitProvider - }{ - { - name: "should create PaC repository for Github application", - repoUrl: "https://github.com/user/test-component-repository", - pacConfig: map[string][]byte{ - PipelinesAsCode_githubAppIdKey: []byte("12345"), - PipelinesAsCode_githubPrivateKey: []byte("private-key"), - }, - expectedGitProviderConfig: nil, - }, - { - name: "should create PaC repository for Github application even if Github webhook configured", - repoUrl: "https://github.com/user/test-component-repository", - pacConfig: map[string][]byte{ - PipelinesAsCode_githubAppIdKey: []byte("12345"), - PipelinesAsCode_githubPrivateKey: []byte("private-key"), - "github.token": []byte("ghp_token"), - }, - expectedGitProviderConfig: nil, - }, - { - name: "should create PaC repository for Github webhook", - repoUrl: "https://github.com/user/test-component-repository", - pacConfig: map[string][]byte{ - "github.token": []byte("ghp_token"), - "gitlab.token": []byte("glpat-token"), - }, - expectedGitProviderConfig: &pacv1alpha1.GitProvider{ - Secret: &pacv1alpha1.Secret{ - Name: gitopsprepare.PipelinesAsCodeSecretName, - Key: "github.token", - }, - WebhookSecret: &pacv1alpha1.Secret{ - Name: PipelinesAsCodeWebhooksSecretName, - Key: GetWebhookSecretKeyForComponent(getComponent("https://github.com/user/test-component-repository")), - }, - }, - }, - { - name: "should create PaC repository for GitLab webhook", - repoUrl: "https://gitlab.com/user/test-component-repository/", - pacConfig: map[string][]byte{ - "github.token": []byte("ghp_token"), - "gitlab.token": []byte("glpat-token"), - }, - expectedGitProviderConfig: &pacv1alpha1.GitProvider{ - Secret: &pacv1alpha1.Secret{ - Name: gitopsprepare.PipelinesAsCodeSecretName, - Key: "gitlab.token", - }, - WebhookSecret: &pacv1alpha1.Secret{ - Name: PipelinesAsCodeWebhooksSecretName, - Key: GetWebhookSecretKeyForComponent(getComponent("https://gitlab.com/user/test-component-repository/")), - }, - URL: "https://gitlab.com", - }, - }, - { - name: "should create PaC repository for GitLab webhook even if GitHub application configured", - repoUrl: "https://gitlab.com/user/test-component-repository.git", - pacConfig: map[string][]byte{ - PipelinesAsCode_githubAppIdKey: []byte("12345"), - PipelinesAsCode_githubPrivateKey: []byte("private-key"), - "gitlab.token": []byte("glpat-token"), - }, - expectedGitProviderConfig: &pacv1alpha1.GitProvider{ - Secret: &pacv1alpha1.Secret{ - Name: gitopsprepare.PipelinesAsCodeSecretName, - Key: "gitlab.token", - }, - WebhookSecret: &pacv1alpha1.Secret{ - Name: PipelinesAsCodeWebhooksSecretName, - Key: GetWebhookSecretKeyForComponent(getComponent("https://gitlab.com/user/test-component-repository")), - }, - URL: "https://gitlab.com", - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - component := getComponent(tt.repoUrl) - - pacRepo, err := GeneratePACRepository(component, tt.pacConfig) - - if err != nil { - t.Errorf("Failed to generate PaC repository object. Cause: %v", err) - } - - if pacRepo.Name != component.Name { - t.Errorf("Generated PaC repository must have the same name as corresponding component") - } - if pacRepo.Namespace != component.Namespace { - t.Errorf("Generated PaC repository must have the same namespace as corresponding component") - } - if len(pacRepo.Annotations) == 0 { - t.Errorf("Generated PaC repository must have annotations") - } - if pacRepo.Annotations["appstudio.openshift.io/component"] != component.Name { - t.Errorf("Generated PaC repository must have component annotation") - } - expectedRepo := strings.TrimSuffix(strings.TrimSuffix(tt.repoUrl, ".git"), "/") - if pacRepo.Spec.URL != expectedRepo { - t.Errorf("Wrong git repository URL in PaC repository: %s, want %s", pacRepo.Spec.URL, expectedRepo) - } - if !reflect.DeepEqual(pacRepo.Spec.GitProvider, tt.expectedGitProviderConfig) { - t.Errorf("Wrong git provider config in PaC repository: %#v, want %#v", pacRepo.Spec.GitProvider, tt.expectedGitProviderConfig) - } - }) - } -} - -func TestGetProviderTokenKey(t *testing.T) { - tests := []struct { - name string - provider string - want string - }{ - { - name: "check github key", - provider: "github", - want: "github.token", - }, - { - name: "check gitlab key", - provider: "gitlab", - want: "gitlab.token", - }, - { - name: "check bitbucket key", - provider: "bitbucket", - want: "bitbucket.token", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := GetProviderTokenKey(tt.provider); got != tt.want { - t.Errorf("Wrong git provider access token key: %s, want %s", got, tt.want) - } - }) - } -} - -func TestGetWebhookSecretKeyForComponent(t *testing.T) { - getComponent := func(repoUrl string) appstudiov1alpha1.Component { - return appstudiov1alpha1.Component{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testcomponent", - Namespace: "workspace-name", - }, - Spec: appstudiov1alpha1.ComponentSpec{ - Source: appstudiov1alpha1.ComponentSource{ - ComponentSourceUnion: appstudiov1alpha1.ComponentSourceUnion{ - GitSource: &appstudiov1alpha1.GitSource{ - URL: repoUrl, - }, - }, - }, - }, - } - } - - tests := []struct { - name string - component appstudiov1alpha1.Component - want string - }{ - { - name: "should return key for the url", - component: getComponent("https://github.com/user/test-component-repository"), - want: "https___github.com_user_test-component-repository", - }, - { - name: "should ignore .git suffix", - component: getComponent("https://github.com/user/test-component-repository.git"), - want: "https___github.com_user_test-component-repository", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := GetWebhookSecretKeyForComponent(tt.component); got != tt.want { - t.Errorf("Expected '%s', but got '%s'", tt.want, got) - } - }) - } -} - -func TestGetGitProvider(t *testing.T) { - getComponent := func(repoUrl, annotationValue string) appstudiov1alpha1.Component { - componentMeta := metav1.ObjectMeta{ - Name: "testcomponent", - Namespace: "workspace-name", - } - if annotationValue != "" { - componentMeta.Annotations = map[string]string{ - GitProviderAnnotationName: annotationValue, - } - } - - component := appstudiov1alpha1.Component{ - ObjectMeta: componentMeta, - Spec: appstudiov1alpha1.ComponentSpec{ - Source: appstudiov1alpha1.ComponentSource{ - ComponentSourceUnion: appstudiov1alpha1.ComponentSourceUnion{ - GitSource: &appstudiov1alpha1.GitSource{ - URL: repoUrl, - }, - }, - }, - }, - } - return component - } - - tests := []struct { - name string - componentRepoUrl string - componentGitProviderAnnotation string - want string - expectError bool - }{ - { - name: "should detect github provider via http url", - componentRepoUrl: "https://github.com/user/test-component-repository", - want: "github", - }, - { - name: "should detect github provider via git url", - componentRepoUrl: "git@github.com:user/test-component-repository", - want: "github", - }, - { - name: "should detect gitlab provider via http url", - componentRepoUrl: "https://gitlab.com/user/test-component-repository", - want: "gitlab", - }, - { - name: "should detect gitlab provider via git url", - componentRepoUrl: "git@gitlab.com:user/test-component-repository", - want: "gitlab", - }, - { - name: "should detect bitbucket provider via http url", - componentRepoUrl: "https://bitbucket.org/user/test-component-repository", - want: "bitbucket", - }, - { - name: "should detect bitbucket provider via git url", - componentRepoUrl: "git@bitbucket.org:user/test-component-repository", - want: "bitbucket", - }, - { - name: "should detect github provider via annotation", - componentRepoUrl: "https://mydomain.com/user/test-component-repository", - componentGitProviderAnnotation: "github", - want: "github", - }, - { - name: "should detect gitlab provider via annotation", - componentRepoUrl: "https://mydomain.com/user/test-component-repository", - componentGitProviderAnnotation: "gitlab", - want: "gitlab", - }, - { - name: "should detect bitbucket provider via annotation", - componentRepoUrl: "https://mydomain.com/user/test-component-repository", - componentGitProviderAnnotation: "bitbucket", - want: "bitbucket", - }, - { - name: "should fail to detect git provider for self-hosted instance if annotation is not set", - componentRepoUrl: "https://mydomain.com/user/test-component-repository", - expectError: true, - }, - { - name: "should fail to detect git provider for self-hosted instance if annotation is set to invalid value", - componentRepoUrl: "https://mydomain.com/user/test-component-repository", - componentGitProviderAnnotation: "mylab", - expectError: true, - }, - { - name: "should fail to detect git provider component repository URL is invalid", - componentRepoUrl: "12345", - expectError: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - component := getComponent(tt.componentRepoUrl, tt.componentGitProviderAnnotation) - got, err := GetGitProvider(component) - if tt.expectError { - if err == nil { - t.Errorf("Detecting git provider for component with '%s' url and '%s' annotation value should fail", tt.componentRepoUrl, tt.componentGitProviderAnnotation) - } - } else { - if got != tt.want { - t.Errorf("Expected git provider is: %s, but got %s", tt.want, got) - } - } - }) - } -} - -func TestIsPaCApplicationConfigured(t *testing.T) { - tests := []struct { - name string - gitProvider string - config map[string][]byte - want bool - }{ - { - name: "should detect github application configured", - gitProvider: "github", - config: map[string][]byte{ - PipelinesAsCode_githubAppIdKey: []byte("12345"), - PipelinesAsCode_githubPrivateKey: []byte("private-key"), - }, - want: true, - }, - { - name: "should prefer github application if both github application and webhook configured", - gitProvider: "github", - config: map[string][]byte{ - PipelinesAsCode_githubAppIdKey: []byte("12345"), - PipelinesAsCode_githubPrivateKey: []byte("private-key"), - "github.token": []byte("ghp_token"), - }, - want: true, - }, - { - name: "should not detect application if it is not configured", - gitProvider: "github", - config: map[string][]byte{ - "github.token": []byte("ghp_token"), - }, - want: false, - }, - { - name: "should not detect application if configuration empty", - gitProvider: "github", - config: map[string][]byte{}, - want: false, - }, - { - name: "should not detect GitHub application if gilab webhook configured", - gitProvider: "gitlab", - config: map[string][]byte{ - PipelinesAsCode_githubAppIdKey: []byte("12345"), - PipelinesAsCode_githubPrivateKey: []byte("private-key"), - "gitlab.token": []byte("glpat-token"), - }, - want: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := IsPaCApplicationConfigured(tt.gitProvider, tt.config); got != tt.want { - t.Errorf("want %t, but got %t", tt.want, got) - } - }) - } -} diff --git a/gitops/generate_test.go b/gitops/generate_test.go deleted file mode 100644 index b81c2a0e..00000000 --- a/gitops/generate_test.go +++ /dev/null @@ -1,124 +0,0 @@ -// -// Copyright 2021-2022 Red Hat, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gitops - -import ( - "path/filepath" - "testing" - - "github.com/mitchellh/go-homedir" - appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" - "github.com/redhat-appstudio/application-service/gitops/prepare" - "github.com/redhat-appstudio/application-service/pkg/util/ioutils" - "github.com/redhat-developer/gitops-generator/pkg/testutils" - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestGenerateTektonBuild(t *testing.T) { - outputPathBase := "test/" - fs := ioutils.NewMemoryFilesystem() - - tests := []struct { - name string - fs afero.Afero - testFolder string - component appstudiov1alpha1.Component - errors *testutils.ErrorStack - want []string - wantErrString string - expectFail bool - testMessageToDisplay string - }{ - { - name: "Check pipeline as code resources", - fs: fs, - testFolder: "test2", - component: appstudiov1alpha1.Component{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testcomponent", - Namespace: "workspace-name", - Annotations: map[string]string{ - PaCAnnotation: "1", - }, - }, - Spec: appstudiov1alpha1.ComponentSpec{ - Source: appstudiov1alpha1.ComponentSource{ - ComponentSourceUnion: appstudiov1alpha1.ComponentSourceUnion{ - GitSource: &appstudiov1alpha1.GitSource{ - URL: "https://github.com/user/git-repo.git", - }, - }, - }, - }, - }, - errors: &testutils.ErrorStack{}, - want: []string{ - kustomizeFileName, - buildRepositoryFileName, - }, - }, - { - name: "Fail build generation because of readonly fs.", - fs: ioutils.NewReadOnlyFs(), - component: appstudiov1alpha1.Component{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testcomponent", - Namespace: "workspace-name", - }, - Spec: appstudiov1alpha1.ComponentSpec{ - Source: appstudiov1alpha1.ComponentSource{ - ComponentSourceUnion: appstudiov1alpha1.ComponentSourceUnion{ - GitSource: &appstudiov1alpha1.GitSource{ - URL: "invalid-url-here", - }, - }, - }, - }, - }, - testMessageToDisplay: "Failure build generation is expected by readonly fs, but seems no error is returned", - expectFail: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - outputPath := outputPathBase + tt.testFolder - - if tt.expectFail { - err := GenerateTektonBuild(outputPath, tt.component, tt.fs, "/", prepare.GitopsConfig{}) - if err == nil { - t.Errorf(tt.testMessageToDisplay) - } - } else { - if err := GenerateTektonBuild(outputPath, tt.component, tt.fs, "/", prepare.GitopsConfig{}); err != nil { - t.Errorf("Failed to generate build gitops resources. Cause: %v", err) - } - } - - // Ensure that needed resources generated - path, err := homedir.Expand(outputPath) - testutils.AssertNoError(t, err) - - for _, item := range tt.want { - exist, err := tt.fs.Exists(filepath.Join(path, tt.component.Name, "/components/", tt.component.Name, "/base/.tekton/", item)) - testutils.AssertNoError(t, err) - assert.True(t, exist, "Expected file %s missing in gitops", item) - } - }) - } -} diff --git a/gitops/prepare/prepare.go b/gitops/prepare/prepare.go deleted file mode 100644 index 75051bf7..00000000 --- a/gitops/prepare/prepare.go +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright 2021 Red Hat, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package prepare - -import ( - "context" - - appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -const ( - // Pipelines as Code global configuration secret name - PipelinesAsCodeSecretName = "pipelines-as-code-secret" - // Pipelines as Code global configuration secret namespace - buildServiceNamespaceName = "build-service" -) - -// Holds data that needs to be queried from the cluster in order for the gitops generation function to work -// This struct is left here so more data can be added as needed -type GitopsConfig struct { - // Contains data from Pipelies as Code configuration k8s secret - PipelinesAsCodeCredentials map[string][]byte -} - -func PrepareGitopsConfig(ctx context.Context, cli client.Client, component appstudiov1alpha1.Component) GitopsConfig { - data := GitopsConfig{} - - data.PipelinesAsCodeCredentials = getPipelinesAsCodeConfigurationSecretData(ctx, cli, component) - - return data -} - -func getPipelinesAsCodeConfigurationSecretData(ctx context.Context, cli client.Client, component appstudiov1alpha1.Component) map[string][]byte { - pacSecret := &corev1.Secret{} - err := cli.Get(ctx, types.NamespacedName{Name: PipelinesAsCodeSecretName, Namespace: buildServiceNamespaceName}, pacSecret) - if err != nil { - return make(map[string][]byte) - } - return pacSecret.Data -} diff --git a/gitops/prepare/prepare_test.go b/gitops/prepare/prepare_test.go deleted file mode 100644 index 3bfa01f8..00000000 --- a/gitops/prepare/prepare_test.go +++ /dev/null @@ -1,135 +0,0 @@ -/* -Copyright 2021 Red Hat, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package prepare - -import ( - "context" - "reflect" - "testing" - - appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - crclient "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" -) - -func TestPrepareGitopsConfig(t *testing.T) { - - component := appstudiov1alpha1.Component{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "Component", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "myName", - Namespace: "myNamespace", - }, - } - tests := []struct { - name string - pacSecret corev1.Secret - want GitopsConfig - }{ - { - name: "should resolve the build bundle in case a configmap exists in the component's namespace", - pacSecret: corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "Secret", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: PipelinesAsCodeSecretName, - Namespace: buildServiceNamespaceName, - }, - Data: map[string][]byte{ - "github.token": []byte("ghp_token"), - }, - }, - want: GitopsConfig{ - PipelinesAsCodeCredentials: map[string][]byte{ - "github.token": []byte("ghp_token"), - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - client := fake.NewClientBuilder().WithRuntimeObjects(&tt.pacSecret).Build() - if got := PrepareGitopsConfig(context.TODO(), client, component); !reflect.DeepEqual(got, tt.want) { - t.Errorf("PrepareGitopsConfig() = %v, want %v", got, tt.want) - } - }) - } - -} - -func TestGetPipelinesAsCodeConfigurationSecretData(t *testing.T) { - ctx := context.TODO() - - component := appstudiov1alpha1.Component{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "Component", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "myName", - Namespace: "myNamespace", - }, - } - - tests := []struct { - name string - data *corev1.Secret - want map[string][]byte - }{ - { - name: "secret exists", - data: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: buildServiceNamespaceName, - Name: PipelinesAsCodeSecretName, - }, - Data: map[string][]byte{ - "github.token": []byte("ghp_token"), - }, - }, - want: map[string][]byte{ - "github.token": []byte("ghp_token"), - }, - }, - { - name: "secret does not exist", - want: map[string][]byte{}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var client crclient.WithWatch - client = fake.NewClientBuilder().Build() - if tt.data != nil { - client = fake.NewClientBuilder().WithRuntimeObjects(tt.data).Build() - } - - if got := getPipelinesAsCodeConfigurationSecretData(ctx, client, component); !reflect.DeepEqual(got, tt.want) { - t.Errorf("got %v, want %v", got, tt.want) - } - }) - } - -} diff --git a/go.work b/go.work new file mode 100644 index 00000000..b1bf9e4a --- /dev/null +++ b/go.work @@ -0,0 +1,7 @@ +go 1.19 + +use ( + . + ./cdq-analysis + ./gitops-generator +) diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 00000000..7625b3df --- /dev/null +++ b/go.work.sum @@ -0,0 +1,4 @@ +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/devfile/library v1.2.1-0.20220308191614-f0f7e11b17de h1:jImHtiAxjyul1UkPmf6C3EMS5wqNz+k84LKkCXkeqws= +github.com/emicklei/go-restful v2.15.0+incompatible h1:8KpYO/Xl/ZudZs5RNOEhWMBY4hmzlZhhRd9cu+jrZP4= +github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= diff --git a/main.go b/main.go index 52cf4077..8bb605fd 100644 --- a/main.go +++ b/main.go @@ -139,6 +139,9 @@ func main() { ghTokenClient := github.GitHubTokenClient{} setupLog.Info(fmt.Sprintf("There are %v token(s) available", len(github.Clients))) + doGitOpsJob := os.Getenv("DO_GITOPS_JOB") == "true" + allowLocalGitOpsGen := os.Getenv("ALLOW_LOCAL_GITOPS_GEN") == "true" + if err = (&controllers.ApplicationReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), @@ -150,13 +153,15 @@ func main() { os.Exit(1) } if err = (&controllers.ComponentReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Log: ctrl.Log.WithName("controllers").WithName("Component"), - Generator: gitopsgen.NewGitopsGen(), - AppFS: ioutils.NewFilesystem(), - GitHubTokenClient: ghTokenClient, - SPIClient: spi.SPIClient{}, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Log: ctrl.Log.WithName("controllers").WithName("Component"), + Generator: gitopsgen.NewGitopsGen(), + AppFS: ioutils.NewFilesystem(), + GitHubTokenClient: ghTokenClient, + SPIClient: spi.SPIClient{}, + DoGitOpsJob: doGitOpsJob, + AllowLocalGitopsGen: allowLocalGitOpsGen, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Component") os.Exit(1) @@ -187,12 +192,14 @@ func main() { } if err = (&controllers.SnapshotEnvironmentBindingReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Log: ctrl.Log.WithName("controllers").WithName("SnapshotEnvironmentBinding"), - Generator: gitopsgen.NewGitopsGen(), - AppFS: ioutils.NewFilesystem(), - GitHubTokenClient: ghTokenClient, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Log: ctrl.Log.WithName("controllers").WithName("SnapshotEnvironmentBinding"), + Generator: gitopsgen.NewGitopsGen(), + AppFS: ioutils.NewFilesystem(), + GitHubTokenClient: ghTokenClient, + DoGitOpsJob: doGitOpsJob, + AllowLocalGitopsGen: allowLocalGitOpsGen, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "SnapshotEnvironmentBinding") os.Exit(1) diff --git a/pkg/util/util.go b/pkg/util/util.go index cf7dbe91..306610a0 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -323,19 +323,3 @@ func GenerateUniqueHashForWorkloadImageTag(namespace string) string { h.Write([]byte(namespace)) return base64.URLEncoding.EncodeToString(h.Sum(nil))[0:5] } - -// GenerateRandomRouteName returns a random, trimmed route name based on the Component name based on the following criteria -// 1. Under 30 characters -// 2. Contains 4 random characters -func GenerateRandomRouteName(componentName string) string { - routeName := componentName - if len(componentName) > 25 { - routeName = componentName[0:25] - } else { - routeName = componentName - } - - // Append random characters to the route name - routeName = routeName + GetRandomString(4, true) - return routeName -} diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go index ee087428..f65d1565 100644 --- a/pkg/util/util_test.go +++ b/pkg/util/util_test.go @@ -873,43 +873,3 @@ func TestGetIntValue(t *testing.T) { assert.True(t, val == tt.wantValue, "Expected int value %d got %d", tt.wantValue, val) } } - -func TestGenerateRandomRouteName(t *testing.T) { - - tests := []struct { - name string - componentName string - }{ - { - name: "Simple component name, less than 25 characters", - componentName: "test-comp", - }, - { - name: "long component name", - componentName: "test-test-test-test-test-test-test-test-test", - }, - } - - for _, tt := range tests { - routeName := GenerateRandomRouteName(tt.componentName) - if len(routeName) >= 30 { - t.Errorf("TestGenerateRandomRouteName() error: expected generated route name %s to be less than 30 chars", routeName) - } - if tt.name == "long component name" { - if !strings.Contains(routeName, tt.componentName[0:25]) { - t.Errorf("TestGenerateRandomRouteName() error: expected generated route name %s to contain first 25 chars of component name %s", routeName, tt.componentName) - } - if routeName == tt.componentName[0:25] { - t.Errorf("TestGenerateRandomRouteName() error: expected generated route name %s to contain 25 char slice from component name %s", routeName, tt.componentName) - } - } else { - if !strings.Contains(routeName, tt.componentName) { - t.Errorf("TestGenerateRandomRouteName() error: expected generated route name %s to contain component name %s", routeName, tt.componentName) - } - if routeName == tt.componentName { - t.Errorf("TestGenerateRandomRouteName() error: expected generated route name %s to be unique from component name %s", routeName, tt.componentName) - } - } - - } -} From 59971804eee5edd7cb57175f5772ee6b0c2b7365 Mon Sep 17 00:00:00 2001 From: John Collier Date: Fri, 4 Aug 2023 17:25:10 -0400 Subject: [PATCH 2/9] Go fmt Signed-off-by: John Collier --- gitops-generator/pkg/devfile/devfile_test.go | 15 +++++++++++++++ gitops-generator/pkg/generate/generate_test.go | 15 +++++++++++++++ gitops-generator/pkg/util/util_test.go | 15 +++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/gitops-generator/pkg/devfile/devfile_test.go b/gitops-generator/pkg/devfile/devfile_test.go index e61b8d4c..6b62a35e 100644 --- a/gitops-generator/pkg/devfile/devfile_test.go +++ b/gitops-generator/pkg/devfile/devfile_test.go @@ -1,3 +1,18 @@ +// +// Copyright 2023 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package devfile import ( diff --git a/gitops-generator/pkg/generate/generate_test.go b/gitops-generator/pkg/generate/generate_test.go index 74abb629..cec282bc 100644 --- a/gitops-generator/pkg/generate/generate_test.go +++ b/gitops-generator/pkg/generate/generate_test.go @@ -1,3 +1,18 @@ +// +// Copyright 2023 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package generate import ( diff --git a/gitops-generator/pkg/util/util_test.go b/gitops-generator/pkg/util/util_test.go index 121a3808..82897cc4 100644 --- a/gitops-generator/pkg/util/util_test.go +++ b/gitops-generator/pkg/util/util_test.go @@ -1,3 +1,18 @@ +// +// Copyright 2023 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package util import ( From 989959b9d197b5505ae7b5fe81b1d8bea5e9adea Mon Sep 17 00:00:00 2001 From: John Collier Date: Fri, 4 Aug 2023 17:30:54 -0400 Subject: [PATCH 3/9] Cleanup Signed-off-by: John Collier --- ...onsnapshotenvironmentbinding_controller.go | 16 - go.mod | 8 +- go.sum | 7 +- go.work.sum | 4 +- pkg/util/util.go | 61 --- pkg/util/util_test.go | 357 ------------------ 6 files changed, 10 insertions(+), 443 deletions(-) diff --git a/controllers/applicationsnapshotenvironmentbinding_controller.go b/controllers/applicationsnapshotenvironmentbinding_controller.go index 025c268c..bcb534df 100644 --- a/controllers/applicationsnapshotenvironmentbinding_controller.go +++ b/controllers/applicationsnapshotenvironmentbinding_controller.go @@ -61,8 +61,6 @@ type SnapshotEnvironmentBindingReconciler struct { AllowLocalGitopsGen bool } -const asebName = "SnapshotEnvironmentBinding" - //+kubebuilder:rbac:groups=appstudio.redhat.com,resources=snapshotenvironmentbindings,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=appstudio.redhat.com,resources=snapshotenvironmentbindings/status,verbs=get;update;patch //+kubebuilder:rbac:groups=appstudio.redhat.com,resources=snapshotenvironmentbindings/finalizers,verbs=update @@ -174,20 +172,6 @@ func (r *SnapshotEnvironmentBindingReconciler) Reconcile(ctx context.Context, re return ctrl.Result{}, nil } -// isKubernetesCluster checks if its either a Kubernetes or an OpenShift cluster -// from the Environment custom resource -func isKubernetesCluster(environment appstudiov1alpha1.Environment) bool { - unstableConfig := environment.Spec.UnstableConfigurationFields - - if unstableConfig != nil { - if unstableConfig.ClusterType == appstudiov1alpha1.ConfigurationClusterType_Kubernetes { - return true - } - } - - return false -} - // SetupWithManager sets up the controller with the Manager. func (r *SnapshotEnvironmentBindingReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { log := ctrl.LoggerFrom(ctx).WithName("controllers").WithName("Environment") diff --git a/go.mod b/go.mod index 4b086d84..2d4574b0 100644 --- a/go.mod +++ b/go.mod @@ -11,15 +11,14 @@ require ( github.com/golang/mock v1.6.0 github.com/google/go-github/v52 v52.0.1-0.20230514113659-60429b4ba0ba github.com/migueleliasweb/go-github-mock v0.0.17 - github.com/mitchellh/go-homedir v1.1.0 github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.24.1 - github.com/openshift-pipelines/pipelines-as-code v0.0.0-20220622161720-2a6007e17200 github.com/openshift/api v0.0.0-20210503193030-25175d9d392d github.com/pact-foundation/pact-go v1.7.0 github.com/prometheus/client_golang v1.14.0 github.com/redhat-appstudio/application-api v0.0.0-20230704143842-035c661f115f github.com/redhat-appstudio/application-service/cdq-analysis v0.0.0 + github.com/redhat-appstudio/application-service/gitops-generator v0.0.0 github.com/redhat-appstudio/service-provider-integration-scm-file-retriever v0.8.3 github.com/redhat-developer/gitops-generator v0.0.0-20230614175323-aff86c6bc55e github.com/spf13/afero v1.8.0 @@ -108,6 +107,7 @@ require ( github.com/mattn/go-isatty v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/buildkit v0.10.6 // indirect github.com/moby/locker v1.0.1 // indirect @@ -161,6 +161,7 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + gotest.tools/v3 v3.1.0 // indirect k8s.io/apiextensions-apiserver v0.26.1 // indirect k8s.io/component-base v0.26.1 // indirect k8s.io/klog v1.0.0 // indirect @@ -168,7 +169,6 @@ require ( k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect k8s.io/pod-security-admission v0.26.1 // indirect k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect - knative.dev/pkg v0.0.0-20221011175852-714b7630a836 // indirect oras.land/oras-go v1.1.0 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect @@ -177,3 +177,5 @@ require ( replace github.com/antlr/antlr4 => github.com/antlr/antlr4 v0.0.0-20211106181442-e4c1a74c66bd replace github.com/redhat-appstudio/application-service/cdq-analysis => ./cdq-analysis + +replace github.com/redhat-appstudio/application-service/gitops-generator => ./gitops-generator diff --git a/go.sum b/go.sum index 2b457bf0..d280595d 100644 --- a/go.sum +++ b/go.sum @@ -330,6 +330,7 @@ github.com/devfile/api/v2 v2.2.0/go.mod h1:dN7xFrOVG+iPqn4UKGibXLd5oVsdE8XyK9OEb github.com/devfile/api/v2 v2.2.1-alpha.0.20230413012049-a6c32fca0dbd h1:HpGR728CfB6BB9ZuFtQb0UeTIYNFgpuGsuoMOJNMUTM= github.com/devfile/api/v2 v2.2.1-alpha.0.20230413012049-a6c32fca0dbd/go.mod h1:qp8jcw12y1JdCsxjK/7LJ7uWaJOxcY1s2LUk5PhbkbM= github.com/devfile/library v1.2.1-0.20211104222135-49d635cb492f/go.mod h1:uFZZdTuRqA68FVe/JoJHP92CgINyQkyWnM2Qyiim+50= +github.com/devfile/library v1.2.1-0.20220308191614-f0f7e11b17de h1:jImHtiAxjyul1UkPmf6C3EMS5wqNz+k84LKkCXkeqws= github.com/devfile/library v1.2.1-0.20220308191614-f0f7e11b17de/go.mod h1:GSPfJaBg0+bBjBHbwBE5aerJLH6tWGQu2q2rHYd9czM= github.com/devfile/library/v2 v2.0.1/go.mod h1:paJ0PARAVy0br13VpBEQ4fO3rZVDxWtooQ29+23PNBk= github.com/devfile/library/v2 v2.2.1-0.20230418160146-e75481b7eebd h1:YHSwUdfWsG9Qk7Vn+NfafELv6+G6a43RRE/NjS0TfK0= @@ -914,8 +915,6 @@ github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mo github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -github.com/openshift-pipelines/pipelines-as-code v0.0.0-20220622161720-2a6007e17200 h1:zu9Z85uKHVMEg/nAd0J9xNLUnu6C3mtRf5l7xJiTilM= -github.com/openshift-pipelines/pipelines-as-code v0.0.0-20220622161720-2a6007e17200/go.mod h1:C2xZcwv55w1W74LjkEN7NRp2z0UOj0/VXMi8ZuNBPGI= github.com/openshift/api v0.0.0-20200930075302-db52bc4ef99f/go.mod h1:Si/I9UGeRR3qzg01YWPmtlr0GeGk2fnuggXJRmjAZ6U= github.com/openshift/api v0.0.0-20210503193030-25175d9d392d h1:eKs5lGkavtfolWeUBJCyirqopVSheGPHgikqlfVmq1s= github.com/openshift/api v0.0.0-20210503193030-25175d9d392d/go.mod h1:dZ4kytOo3svxJHNYd0J55hwe/6IQG5gAUHUE0F3Jkio= @@ -1790,11 +1789,11 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= gotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk= +gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1871,8 +1870,6 @@ k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/ k8s.io/utils v0.0.0-20210722164352-7f3ee0f31471/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -knative.dev/pkg v0.0.0-20221011175852-714b7630a836 h1:0N7Zo/O+xeUUebJPm9keBaGclrUoEbljr3J1MsqtaIM= -knative.dev/pkg v0.0.0-20221011175852-714b7630a836/go.mod h1:DMTRDJ5WRxf/DrlOPzohzfhSuJggscLZ8EavOq9O/x8= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= oras.land/oras-go v1.1.0 h1:tfWM1RT7PzUwWphqHU6ptPU3ZhwVnSw/9nEGf519rYg= oras.land/oras-go v1.1.0/go.mod h1:1A7vR/0KknT2UkJVWh+xMi95I/AhK8ZrxrnUSmXN0bQ= diff --git a/go.work.sum b/go.work.sum index 7625b3df..7dcd3677 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,4 +1,6 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/devfile/library v1.2.1-0.20220308191614-f0f7e11b17de h1:jImHtiAxjyul1UkPmf6C3EMS5wqNz+k84LKkCXkeqws= github.com/emicklei/go-restful v2.15.0+incompatible h1:8KpYO/Xl/ZudZs5RNOEhWMBY4hmzlZhhRd9cu+jrZP4= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= +github.com/openshift-pipelines/pipelines-as-code v0.0.0-20220622161720-2a6007e17200 h1:zu9Z85uKHVMEg/nAd0J9xNLUnu6C3mtRf5l7xJiTilM= +github.com/openshift-pipelines/pipelines-as-code v0.0.0-20220622161720-2a6007e17200/go.mod h1:C2xZcwv55w1W74LjkEN7NRp2z0UOj0/VXMi8ZuNBPGI= +knative.dev/pkg v0.0.0-20220131144930-f4b57aef0006 h1:0Kv7dIOimyHno/7jbQ6LCi15ME+e/b5tY0pwi99kanQ= diff --git a/pkg/util/util.go b/pkg/util/util.go index 306610a0..c2f8fe54 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -25,17 +25,11 @@ import ( "net/url" "os" "os/exec" - "reflect" "regexp" "strings" "time" appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" - gitopsgenv1alpha1 "github.com/redhat-developer/gitops-generator/api/v1alpha1" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - - "github.com/devfile/library/v2/pkg/devfile/parser" ) var RevisionHistoryLimit = int32(0) @@ -261,61 +255,6 @@ func GetRandomString(n int, lower bool) string { return randomString } -// GetMappedGitOpsComponent gets a mapped GeneratorOptions from the Component for GitOps resource generation -func GetMappedGitOpsComponent(component appstudiov1alpha1.Component, kubernetesResources parser.KubernetesResources) gitopsgenv1alpha1.GeneratorOptions { - customK8sLabels := map[string]string{ - "app.kubernetes.io/name": component.Spec.ComponentName, - "app.kubernetes.io/instance": component.Name, - "app.kubernetes.io/part-of": component.Spec.Application, - "app.kubernetes.io/managed-by": "kustomize", - "app.kubernetes.io/created-by": "application-service", - } - gitopsMapComponent := gitopsgenv1alpha1.GeneratorOptions{ - Name: component.ObjectMeta.Name, - Application: component.Spec.Application, - Secret: component.Spec.Secret, - Resources: component.Spec.Resources, - Replicas: GetIntValue(component.Spec.Replicas), - TargetPort: component.Spec.TargetPort, - Route: component.Spec.Route, - BaseEnvVar: component.Spec.Env, - ContainerImage: component.Spec.ContainerImage, - K8sLabels: customK8sLabels, - RevisionHistoryLimit: &RevisionHistoryLimit, - } - if component.Spec.Source.ComponentSourceUnion.GitSource != nil { - gitopsMapComponent.GitSource = &gitopsgenv1alpha1.GitSource{ - URL: component.Spec.Source.ComponentSourceUnion.GitSource.URL, - } - } else { - gitopsMapComponent.GitSource = &gitopsgenv1alpha1.GitSource{} - } - - // If the resource requests or limits were unset, set default values - if gitopsMapComponent.Resources.Requests == nil { - gitopsMapComponent.Resources.Requests = v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("10m"), - v1.ResourceMemory: resource.MustParse("50Mi"), - } - } - if gitopsMapComponent.Resources.Limits == nil { - gitopsMapComponent.Resources.Limits = v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1"), - v1.ResourceMemory: resource.MustParse("512Mi"), - } - } - - if !reflect.DeepEqual(kubernetesResources, parser.KubernetesResources{}) { - gitopsMapComponent.KubernetesResources.Deployments = append(gitopsMapComponent.KubernetesResources.Deployments, kubernetesResources.Deployments...) - gitopsMapComponent.KubernetesResources.Services = append(gitopsMapComponent.KubernetesResources.Services, kubernetesResources.Services...) - gitopsMapComponent.KubernetesResources.Routes = append(gitopsMapComponent.KubernetesResources.Routes, kubernetesResources.Routes...) - gitopsMapComponent.KubernetesResources.Ingresses = append(gitopsMapComponent.KubernetesResources.Ingresses, kubernetesResources.Ingresses...) - gitopsMapComponent.KubernetesResources.Others = append(gitopsMapComponent.KubernetesResources.Others, kubernetesResources.Others...) - } - - return gitopsMapComponent -} - // GenerateUniqueHashForWorkloadImageTag generates a unique hash from the namespace // in order to not expose user's username via namespace of a particular resource potentially. func GenerateUniqueHashForWorkloadImageTag(namespace string) string { diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go index f65d1565..03982e2c 100644 --- a/pkg/util/util_test.go +++ b/pkg/util/util_test.go @@ -17,18 +17,9 @@ package util import ( "os" - "reflect" "strings" "testing" - "github.com/devfile/library/v2/pkg/devfile/parser" - gitopsgenv1alpha1 "github.com/redhat-developer/gitops-generator/api/v1alpha1" - - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" "github.com/stretchr/testify/assert" ) @@ -498,354 +489,6 @@ func TestGetRandomString(t *testing.T) { } } -func TestGetMappedComponent(t *testing.T) { - - other := make([]interface{}, 1) - other[0] = appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "deployment1", - }, - } - - tests := []struct { - name string - component appstudiov1alpha1.Component - kubernetesResources parser.KubernetesResources - want gitopsgenv1alpha1.GeneratorOptions - }{ - { - name: "Test01ComponentSpecFilledIn", - component: appstudiov1alpha1.Component{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testcomponent", - }, - Spec: appstudiov1alpha1.ComponentSpec{ - ComponentName: "frontEnd", - Application: "AppTest001", - Secret: "Secret", - Resources: corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceLimitsCPU: resource.MustParse("1"), - corev1.ResourceMemory: resource.MustParse("1Gi"), - }, - }, - TargetPort: 8080, - Route: "https://testroute", - Env: []corev1.EnvVar{ - { - Name: "env1", - Value: "env1Value", - }, - }, - ContainerImage: "myimage:image", - SkipGitOpsResourceGeneration: false, - Source: appstudiov1alpha1.ComponentSource{ - ComponentSourceUnion: appstudiov1alpha1.ComponentSourceUnion{ - GitSource: &appstudiov1alpha1.GitSource{ - URL: "https://host/git-repo.git", - Revision: "1.0", - Context: "/context", - DevfileURL: "https://mydevfileurl", - DockerfileURL: "https://mydockerfileurl", - }, - }, - }, - }, - }, - want: gitopsgenv1alpha1.GeneratorOptions{ - Name: "testcomponent", - Application: "AppTest001", - Secret: "Secret", - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("10m"), - corev1.ResourceMemory: resource.MustParse("50Mi"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceLimitsCPU: resource.MustParse("1"), - corev1.ResourceMemory: resource.MustParse("1Gi"), - }, - }, - TargetPort: 8080, - Route: "https://testroute", - BaseEnvVar: []corev1.EnvVar{ - { - Name: "env1", - Value: "env1Value", - }, - }, - ContainerImage: "myimage:image", - GitSource: &gitopsgenv1alpha1.GitSource{ - URL: "https://host/git-repo.git", - }, - }, - }, - { - name: "Test02EmptyComponentSource", - component: appstudiov1alpha1.Component{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testcomponent", - }, - Spec: appstudiov1alpha1.ComponentSpec{ - ComponentName: "frontEnd", - Application: "AppTest002", - Source: appstudiov1alpha1.ComponentSource{}, - }, - }, - want: gitopsgenv1alpha1.GeneratorOptions{ - Name: "testcomponent", - Application: "AppTest002", - GitSource: &gitopsgenv1alpha1.GitSource{}, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("10m"), - corev1.ResourceMemory: resource.MustParse("50Mi"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - corev1.ResourceMemory: resource.MustParse("512Mi"), - }, - }, - }, - }, - { - name: "Test03NoSource", - component: appstudiov1alpha1.Component{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testcomponent", - }, - Spec: appstudiov1alpha1.ComponentSpec{ - ComponentName: "frontEnd", - Application: "AppTest003", - }, - }, - want: gitopsgenv1alpha1.GeneratorOptions{ - Name: "testcomponent", - Application: "AppTest003", - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("10m"), - corev1.ResourceMemory: resource.MustParse("50Mi"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - corev1.ResourceMemory: resource.MustParse("512Mi"), - }, - }, - }, - }, - { - name: "Test04EmptyComponentSourceUnion", - component: appstudiov1alpha1.Component{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testcomponent", - }, - Spec: appstudiov1alpha1.ComponentSpec{ - ComponentName: "frontEnd", - Application: "AppTest004", - Source: appstudiov1alpha1.ComponentSource{ - ComponentSourceUnion: appstudiov1alpha1.ComponentSourceUnion{}, - }, - }, - }, - want: gitopsgenv1alpha1.GeneratorOptions{ - Name: "testcomponent", - Application: "AppTest004", - GitSource: &gitopsgenv1alpha1.GitSource{}, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("10m"), - corev1.ResourceMemory: resource.MustParse("50Mi"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - corev1.ResourceMemory: resource.MustParse("512Mi"), - }, - }, - }, - }, - { - name: "Test05EmptyGitSource", - component: appstudiov1alpha1.Component{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testcomponent", - }, - Spec: appstudiov1alpha1.ComponentSpec{ - ComponentName: "frontEnd", - Application: "AppTest005", - Source: appstudiov1alpha1.ComponentSource{ - ComponentSourceUnion: appstudiov1alpha1.ComponentSourceUnion{ - GitSource: &appstudiov1alpha1.GitSource{}, - }, - }, - }, - }, - want: gitopsgenv1alpha1.GeneratorOptions{ - Name: "testcomponent", - Application: "AppTest005", - GitSource: &gitopsgenv1alpha1.GitSource{}, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("10m"), - corev1.ResourceMemory: resource.MustParse("50Mi"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - corev1.ResourceMemory: resource.MustParse("512Mi"), - }, - }, - }, - }, - { - name: "Test06KubernetesResources", - component: appstudiov1alpha1.Component{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testcomponent", - }, - Spec: appstudiov1alpha1.ComponentSpec{ - ComponentName: "frontEnd", - Application: "AppTest005", - Source: appstudiov1alpha1.ComponentSource{ - ComponentSourceUnion: appstudiov1alpha1.ComponentSourceUnion{ - GitSource: &appstudiov1alpha1.GitSource{ - URL: "url", - }, - }, - }, - }, - }, - kubernetesResources: parser.KubernetesResources{ - Deployments: []appsv1.Deployment{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "deployment1", - }, - }, - }, - Services: []corev1.Service{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "service1", - }, - }, - }, - Others: other, - }, - want: gitopsgenv1alpha1.GeneratorOptions{ - Name: "testcomponent", - Application: "AppTest005", - GitSource: &gitopsgenv1alpha1.GitSource{ - URL: "url", - }, - KubernetesResources: gitopsgenv1alpha1.KubernetesResources{ - Deployments: []appsv1.Deployment{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "deployment1", - }, - }, - }, - Services: []corev1.Service{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "service1", - }, - }, - }, - Others: other, - }, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("10m"), - corev1.ResourceMemory: resource.MustParse("50Mi"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - corev1.ResourceMemory: resource.MustParse("512Mi"), - }, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mappedComponent := GetMappedGitOpsComponent(tt.component, tt.kubernetesResources) - assert.True(t, tt.want.Name == mappedComponent.Name, "Expected ObjectMeta.Name: %s, is different than actual: %s", tt.want.Name, mappedComponent.Name) - assert.True(t, tt.want.Application == mappedComponent.Application, "Expected Spec.Application: %s, is different than actual: %s", tt.want.Application, mappedComponent.Application) - assert.True(t, tt.want.Secret == mappedComponent.Secret, "Expected Spec.Secret: %s, is different than actual: %s", tt.want.Secret, mappedComponent.Secret) - assert.True(t, reflect.DeepEqual(tt.want.Resources, mappedComponent.Resources), "Expected Spec.Resources: %s, is different than actual: %s", tt.want.Resources, mappedComponent.Resources) - assert.True(t, tt.want.Route == mappedComponent.Route, "Expected Spec.Route: %s, is different than actual: %s", tt.want.Route, mappedComponent.Route) - assert.True(t, reflect.DeepEqual(tt.want.BaseEnvVar, mappedComponent.BaseEnvVar), "Expected Spec.Env: %s, is different than actual: %s", tt.want.BaseEnvVar, mappedComponent.BaseEnvVar) - assert.True(t, tt.want.ContainerImage == mappedComponent.ContainerImage, "Expected Spec.ContainerImage: %s, is different than actual: %s", tt.want.ContainerImage, mappedComponent.ContainerImage) - - if tt.want.GitSource != nil { - assert.True(t, tt.want.GitSource.URL == mappedComponent.GitSource.URL, "Expected GitSource URL: %s, is different than actual: %s", tt.want.GitSource.URL, mappedComponent.GitSource.URL) - } - - if !reflect.DeepEqual(tt.want.KubernetesResources, gitopsgenv1alpha1.KubernetesResources{}) { - for _, wantDeployment := range tt.want.KubernetesResources.Deployments { - matched := false - for _, gotDeployment := range mappedComponent.KubernetesResources.Deployments { - if wantDeployment.Name == gotDeployment.Name { - matched = true - break - } - } - assert.True(t, matched, "Expected Deployment: %s, but didnt find in actual", wantDeployment.Name) - } - - for _, wantService := range tt.want.KubernetesResources.Services { - matched := false - for _, gotService := range mappedComponent.KubernetesResources.Services { - if wantService.Name == gotService.Name { - matched = true - break - } - } - assert.True(t, matched, "Expected Service: %s, but didnt find in actual", wantService.Name) - } - - for _, wantRoute := range tt.want.KubernetesResources.Routes { - matched := false - for _, gotRoute := range mappedComponent.KubernetesResources.Routes { - if wantRoute.Name == gotRoute.Name { - matched = true - break - } - } - assert.True(t, matched, "Expected Route: %s, but didnt find in actual", wantRoute.Name) - } - - for _, wantIngress := range tt.want.KubernetesResources.Ingresses { - matched := false - for _, gotIngress := range mappedComponent.KubernetesResources.Ingresses { - if wantIngress.Name == gotIngress.Name { - matched = true - break - } - } - assert.True(t, matched, "Expected Ingress: %s, but didnt find in actual", wantIngress.Name) - } - - for _, wantOther := range tt.want.KubernetesResources.Others { - matched := false - wantDeployment := wantOther.(appsv1.Deployment) - - for _, gotOther := range mappedComponent.KubernetesResources.Others { - gotDeployment := gotOther.(appsv1.Deployment) - if wantDeployment.Name == gotDeployment.Name { - matched = true - break - } - } - assert.True(t, matched, "Expected Other: %s, but didnt find in actual", wantDeployment.Name) - } - } - }) - } -} - func TestGetIntValue(t *testing.T) { value := 7 From 78ac7c0bc7323c68b5aeb7f9ebe4a90d29d8b316 Mon Sep 17 00:00:00 2001 From: John Collier Date: Wed, 9 Aug 2023 10:49:52 -0400 Subject: [PATCH 4/9] Go mod tidy Signed-off-by: John Collier --- gitops-generator/go.mod | 147 +++- gitops-generator/go.sum | 1726 +++++++++++++++++++++++++++++++++++++++ go.mod | 3 +- go.sum | 6 +- go.work.sum | 38 + 5 files changed, 1898 insertions(+), 22 deletions(-) create mode 100644 gitops-generator/go.sum diff --git a/gitops-generator/go.mod b/gitops-generator/go.mod index ae1788dd..6776315d 100644 --- a/gitops-generator/go.mod +++ b/gitops-generator/go.mod @@ -3,34 +3,147 @@ module github.com/redhat-appstudio/application-service/gitops-generator go 1.19 require ( - github.com/brianvoe/gofakeit/v6 v6.9.0 github.com/devfile/api/v2 v2.2.1-alpha.0.20230413012049-a6c32fca0dbd + github.com/devfile/library v1.2.1-0.20220308191614-f0f7e11b17de github.com/devfile/library/v2 v2.2.1-0.20230418160146-e75481b7eebd github.com/go-logr/logr v1.2.3 - github.com/gofri/go-github-ratelimit v1.0.3-0.20230428184158-a500e14de53f - github.com/golang/mock v1.6.0 - github.com/google/go-github/v52 v52.0.1-0.20230514113659-60429b4ba0ba - github.com/migueleliasweb/go-github-mock v0.0.17 - github.com/mitchellh/go-homedir v1.1.0 - github.com/onsi/ginkgo v1.16.5 - github.com/onsi/gomega v1.24.1 - github.com/openshift-pipelines/pipelines-as-code v0.0.0-20220622161720-2a6007e17200 github.com/openshift/api v0.0.0-20210503193030-25175d9d392d - github.com/pact-foundation/pact-go v1.7.0 - github.com/prometheus/client_golang v1.14.0 github.com/redhat-appstudio/application-api v0.0.0-20230704143842-035c661f115f - github.com/redhat-appstudio/application-service/cdq-analysis v0.0.0 - github.com/redhat-appstudio/service-provider-integration-scm-file-retriever v0.8.3 - github.com/redhat-developer/gitops-generator v0.0.0-20230614175323-aff86c6bc55e + github.com/redhat-appstudio/application-service v0.0.0-20230803130557-ccbca3cb10fd + github.com/redhat-developer/gitops-generator v0.0.0-20230801134438-01747a27dcbf github.com/spf13/afero v1.8.0 github.com/stretchr/testify v1.8.1 go.uber.org/zap v1.24.0 golang.org/x/exp v0.0.0-20230206171751-46f607a40771 - golang.org/x/oauth2 v0.7.0 k8s.io/api v0.26.1 k8s.io/apimachinery v0.26.1 k8s.io/client-go v0.26.1 sigs.k8s.io/controller-runtime v0.14.4 - sigs.k8s.io/yaml v1.3.0 -) \ No newline at end of file +) + +require ( + code.gitea.io/sdk/gitea v0.15.1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230417170513-8ee5748c52b5 // indirect + github.com/acomagu/bufpipe v1.0.4 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bluekeyes/go-gitdiff v0.7.0 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cloudflare/circl v1.3.3 // indirect + github.com/containerd/containerd v1.6.10 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/devfile/registry-support/index/generator v0.0.0-20221018203505-df96d34d4273 // indirect + github.com/devfile/registry-support/registry-library v0.0.0-20221018213054-47b3ffaeadba // indirect + github.com/docker/cli v20.10.13+incompatible // indirect + github.com/docker/distribution v2.8.1+incompatible // indirect + github.com/docker/docker v20.10.11+incompatible // indirect + github.com/docker/docker-credential-helpers v0.6.4 // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-metrics v0.0.1 // indirect + github.com/docker/go-units v0.4.0 // indirect + github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/evanphx/json-patch v4.12.0+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.6.0 // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-git/gcfg v1.5.0 // indirect + github.com/go-git/go-billy/v5 v5.4.1 // indirect + github.com/go-git/go-git/v5 v5.5.1 // indirect + github.com/go-logr/zapr v1.2.3 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/gobwas/glob v0.2.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/mock v1.6.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/btree v1.0.1 // indirect + github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect + github.com/imdario/mergo v0.3.15 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jenkins-x/go-scm v1.11.35 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/klauspost/compress v1.15.11 // indirect + github.com/kr/pretty v0.3.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/moby/locker v1.0.1 // indirect + github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0-rc2 // indirect + github.com/peterbourgon/diskv v2.0.1+incompatible // indirect + github.com/pjbgf/sha1cd v0.2.3 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.14.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.37.0 // indirect + github.com/prometheus/procfs v0.8.0 // indirect + github.com/rogpeppe/go-internal v1.6.2 // indirect + github.com/sergi/go-diff v1.3.1 // indirect + github.com/shurcooL/githubv4 v0.0.0-20190718010115-4ba037080260 // indirect + github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f // indirect + github.com/sirupsen/logrus v1.9.0 // indirect + github.com/skeema/knownhosts v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.8.0 // indirect + golang.org/x/crypto v0.8.0 // indirect + golang.org/x/mod v0.10.0 // indirect + golang.org/x/net v0.9.0 // indirect + golang.org/x/oauth2 v0.7.0 // indirect + golang.org/x/sync v0.2.0 // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/term v0.7.0 // indirect + golang.org/x/text v0.9.0 // indirect + golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.8.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a // indirect + google.golang.org/grpc v1.50.1 // indirect + google.golang.org/protobuf v1.30.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gotest.tools/v3 v3.1.0 // indirect + k8s.io/apiextensions-apiserver v0.26.1 // indirect + k8s.io/component-base v0.26.1 // indirect + k8s.io/klog v1.0.0 // indirect + k8s.io/klog/v2 v2.80.1 // indirect + k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect + k8s.io/pod-security-admission v0.26.1 // indirect + k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect + oras.land/oras-go v1.1.0 // indirect + sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/gitops-generator/go.sum b/gitops-generator/go.sum new file mode 100644 index 00000000..72b733fe --- /dev/null +++ b/gitops-generator/go.sum @@ -0,0 +1,1726 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go/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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +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= +code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE= +code.gitea.io/sdk/gitea v0.15.1 h1:WJreC7YYuxbn0UDaPuWIe/mtiNKTvLN8MLkaw71yx/M= +code.gitea.io/sdk/gitea v0.15.1/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMVoHRBA= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v56.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= +github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= +github.com/Azure/go-autorest/autorest v0.11.20/go.mod h1:o3tqFY+QR40VOlk+pV4d77mORO64jOXSgEnPQgLK6JY= +github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +github.com/Azure/go-autorest/autorest/adal v0.9.15/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +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/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= +github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= +github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= +github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= +github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= +github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= +github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= +github.com/Microsoft/hcsshim v0.9.1/go.mod h1:Y/0uV2jUab5kBI7SQgl62at0AVX7uaruzADAVmxm3eM= +github.com/Microsoft/hcsshim v0.9.5 h1:AbV+VPfTrIVffukazHcpxmz/sRiE6YaMDzHWR9BXZHo= +github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= +github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= +github.com/ProtonMail/go-crypto v0.0.0-20230417170513-8ee5748c52b5 h1:QXMwHM/lB4ZQhdEF7JUTNgYOJR/gWoFbgQ/2Aj1h3Dk= +github.com/ProtonMail/go-crypto v0.0.0-20230417170513-8ee5748c52b5/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= +github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= +github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/aws/aws-sdk-go v1.34.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bluekeyes/go-gitdiff v0.7.0 h1:w4SrRFcufU0/tEpWx3VurDBAnWfpxsmwS7yWr14meQk= +github.com/bluekeyes/go-gitdiff v0.7.0/go.mod h1:QpfYYO1E0fTVHVZAZKiRjtSGY9823iCdvGXBcEzHGbM= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= +github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= +github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= +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/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= +github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= +github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= +github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= +github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= +github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= +github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= +github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= +github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= +github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= +github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= +github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= +github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= +github.com/containerd/cgroups v1.0.2/go.mod h1:qpbpJ1jmlqsR9f2IyaLPsdkCdnt0rbDVqIDlhuu5tRY= +github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= +github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= +github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= +github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= +github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= +github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= +github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= +github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= +github.com/containerd/containerd v1.5.9/go.mod h1:fvQqCfadDGga5HZyn3j4+dx56qj2I9YwBrlSdalvJYQ= +github.com/containerd/containerd v1.6.10 h1:8aiav7I2ZyQLbTlNMcBXyAU1FtFvp6VuyuW13qSd6Hk= +github.com/containerd/containerd v1.6.10/go.mod h1:CVqfxdJ95PDgORwA219AwwLrREZgrTFybXu2HfMKRG0= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= +github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= +github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= +github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= +github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= +github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= +github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= +github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= +github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= +github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= +github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= +github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= +github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= +github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= +github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= +github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= +github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= +github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= +github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= +github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= +github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= +github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= +github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= +github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= +github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= +github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= +github.com/devfile/api/v2 v2.0.0-20211021164004-dabee4e633ed/go.mod h1:d99eTN6QxgzihOOFyOZA+VpUyD4Q1pYRYHZ/ci9J96Q= +github.com/devfile/api/v2 v2.0.0-20220117162434-6e6e6a8bc14c/go.mod h1:d99eTN6QxgzihOOFyOZA+VpUyD4Q1pYRYHZ/ci9J96Q= +github.com/devfile/api/v2 v2.2.0/go.mod h1:dN7xFrOVG+iPqn4UKGibXLd5oVsdE8XyK9OEb5JL3aI= +github.com/devfile/api/v2 v2.2.1-alpha.0.20230413012049-a6c32fca0dbd h1:HpGR728CfB6BB9ZuFtQb0UeTIYNFgpuGsuoMOJNMUTM= +github.com/devfile/api/v2 v2.2.1-alpha.0.20230413012049-a6c32fca0dbd/go.mod h1:qp8jcw12y1JdCsxjK/7LJ7uWaJOxcY1s2LUk5PhbkbM= +github.com/devfile/library v1.2.1-0.20211104222135-49d635cb492f/go.mod h1:uFZZdTuRqA68FVe/JoJHP92CgINyQkyWnM2Qyiim+50= +github.com/devfile/library v1.2.1-0.20220308191614-f0f7e11b17de h1:jImHtiAxjyul1UkPmf6C3EMS5wqNz+k84LKkCXkeqws= +github.com/devfile/library v1.2.1-0.20220308191614-f0f7e11b17de/go.mod h1:GSPfJaBg0+bBjBHbwBE5aerJLH6tWGQu2q2rHYd9czM= +github.com/devfile/library/v2 v2.0.1/go.mod h1:paJ0PARAVy0br13VpBEQ4fO3rZVDxWtooQ29+23PNBk= +github.com/devfile/library/v2 v2.2.1-0.20230418160146-e75481b7eebd h1:YHSwUdfWsG9Qk7Vn+NfafELv6+G6a43RRE/NjS0TfK0= +github.com/devfile/library/v2 v2.2.1-0.20230418160146-e75481b7eebd/go.mod h1:jXIVOBkEqh7YJddFcZ+vak47rlbCW//f+qqG5hUozhM= +github.com/devfile/registry-support/index/generator v0.0.0-20220222194908-7a90a4214f3e/go.mod h1:iRPBxs+ZjfLEduVXpCCIOzdD2588Zv9OCs/CcXMcCCY= +github.com/devfile/registry-support/index/generator v0.0.0-20220527155645-8328a8a883be/go.mod h1:1fyDJL+fPHtcrYA6yjSVWeLmXmjCNth0d5Rq1rvtryc= +github.com/devfile/registry-support/index/generator v0.0.0-20221018203505-df96d34d4273 h1:DXENQSRTEDsk9com38njPg5511DD12HPIgzyFUErnpM= +github.com/devfile/registry-support/index/generator v0.0.0-20221018203505-df96d34d4273/go.mod h1:ZJnaSLjTKCvGJhWmYgQoQ1O3g78qBe4Va6ZugLmi4dE= +github.com/devfile/registry-support/registry-library v0.0.0-20220627163229-4aa39fcb0c0a/go.mod h1:kmEjH5oO465vh36kcYdZLYeG8edVD6N/ZgzyLs1x7qs= +github.com/devfile/registry-support/registry-library v0.0.0-20221018213054-47b3ffaeadba h1:7Ag9guD3qOSzg3PcbSMqTVHDjqZojqb5JoIIonjRjDQ= +github.com/devfile/registry-support/registry-library v0.0.0-20221018213054-47b3ffaeadba/go.mod h1:NOtmnbozFn15w/DPD/Urc+KDlNRP4JH5m+KC5GZoAWA= +github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684 h1:DBZ2sN7CK6dgvHVpQsQj4sRMCbWTmd17l+5SUCjnQSY= +github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684/go.mod h1:UfCu3YXJJCI+IdnqGgYP82dk2+Joxmv+mUTVBES6wac= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.11+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.13+incompatible h1:o/STAn7e+b/pQ6keReGRoewVmAgXUkZAMQ8st4vHdKg= +github.com/docker/cli v20.10.13+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= +github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= +github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.11+incompatible h1:OqzI/g/W54LczvhnccGqniFoQghHx3pklbLuhfXpqGo= +github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= +github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= +github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= +github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= +github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= +github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= +github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= +github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= +github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= +github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= +github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ= +github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= +github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= +github.com/go-git/go-git/v5 v5.5.1 h1:5vtv2TB5PM/gPM+EvsHJ16hJh4uAkdGcKilcwY7FYwo= +github.com/go-git/go-git/v5 v5.5.1/go.mod h1:uz5PQ3d0gz7mSgzZhSJToM6ALPaKCdSnl58/Xb5hzr8= +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-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +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.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= +github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= +github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= +github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +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.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +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.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= +github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= +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/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/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.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/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/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= +github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +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 v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +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.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= +github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= +github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jenkins-x/go-scm v1.11.35 h1:DcxonfFMf8jqqSWZHCsj40Cp1++hTAmlFtIf7IEl3vk= +github.com/jenkins-x/go-scm v1.11.35/go.mod h1:JyL6p5dNNeJZt41yfysDTBe0Q51cLa5MVK0du7JnXt0= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +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.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= +github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= +github.com/lucasjones/reggen v0.0.0-20200904144131-37ba4fa293bb/go.mod h1:5ELEyG+X8f+meRWHuqUOewBOhvHkl7M76pdGEansxW4= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= +github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= +github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= +github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +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/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= +github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI= +github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= +github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= +github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= +github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI= +github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc= +github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +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.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= +github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= +github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= +github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +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.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= +github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= +github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= +github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= +github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= +github.com/openshift/api v0.0.0-20200930075302-db52bc4ef99f/go.mod h1:Si/I9UGeRR3qzg01YWPmtlr0GeGk2fnuggXJRmjAZ6U= +github.com/openshift/api v0.0.0-20210503193030-25175d9d392d h1:eKs5lGkavtfolWeUBJCyirqopVSheGPHgikqlfVmq1s= +github.com/openshift/api v0.0.0-20210503193030-25175d9d392d/go.mod h1:dZ4kytOo3svxJHNYd0J55hwe/6IQG5gAUHUE0F3Jkio= +github.com/openshift/build-machinery-go v0.0.0-20200819073603-48aa266c95f7/go.mod h1:b1BuldmJlbA/xYtdZvKi+7j5YGB44qJUJDZ9zwiNCfE= +github.com/openshift/build-machinery-go v0.0.0-20210209125900-0da259a2c359/go.mod h1:b1BuldmJlbA/xYtdZvKi+7j5YGB44qJUJDZ9zwiNCfE= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= +github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI= +github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/pmezard/go-difflib v1.0.0/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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/redhat-appstudio/application-api v0.0.0-20230704143842-035c661f115f h1:XeHZfA9nZQjLBfjwWo2EvL8TiF3vUZLj7AlY19yTUoM= +github.com/redhat-appstudio/application-api v0.0.0-20230704143842-035c661f115f/go.mod h1:LpcTNLCDMf1CUNTw2bgTiz8M49sUsN2F9T7bOSKcJEo= +github.com/redhat-appstudio/application-service v0.0.0-20230803130557-ccbca3cb10fd h1:Trsbbn5LGIrPOAR5CllzYR4tMrfnSaCCTRIc10rFG5Y= +github.com/redhat-appstudio/application-service v0.0.0-20230803130557-ccbca3cb10fd/go.mod h1:UocA+lvLwEgpjyCRs3xJBk0PugdQYTotroz1cbSZOcE= +github.com/redhat-developer/gitops-generator v0.0.0-20230801134438-01747a27dcbf h1:uRlhXqcVhbvp9YawKoggQoMLZwRP0/kpdLE3wkzqMuI= +github.com/redhat-developer/gitops-generator v0.0.0-20230801134438-01747a27dcbf/go.mod h1:pKJez873Y9lZwQAgm99i/y1SmPI2dMd19ID74C+CxpU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0= +github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= +github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/shurcooL/githubv4 v0.0.0-20190718010115-4ba037080260 h1:xKXiRdBUtMVp64NaxACcyX4kvfmHJ9KrLU+JvyB1mdM= +github.com/shurcooL/githubv4 v0.0.0-20190718010115-4ba037080260/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo= +github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f h1:tygelZueB1EtXkPI6mQ4o9DQ0+FKW41hTbunoXZCTqk= +github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0= +github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.8.0 h1:5MmtuhAgYeU6qpa7w7bP0dv6MBYuup0vekhSpSkoq60= +github.com/spf13/afero v1.8.0/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +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.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +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-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/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-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +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-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg= +golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +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/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/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.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +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-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/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-20200520004742-59133d7f0dd7/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-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= +golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/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= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/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-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/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-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/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-20200217220822-9197077df867/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-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/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-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/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-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/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-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/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-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-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-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.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.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.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.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/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-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/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-20190624222133-a101b041ded4/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-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +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-20191112195655-aa38f8e97acc/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-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +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-20200505023115-26f46d2f7ef8/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-20200616133436-c1934b75d054/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-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +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-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.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.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y= +golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= +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.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= +gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= +google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= +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-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +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-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a h1:GH6UPn3ixhWcKDhpnEC55S75cerLPdpp3hrhfKYjZgw= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +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.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +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.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +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/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= +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.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +gotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk= +gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= +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.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw= +k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= +k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= +k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= +k8s.io/api v0.21.0-rc.0/go.mod h1:Dkc/ZauWJrgZhjOjeBgW89xZQiTBJA2RaBKYHXPsi2Y= +k8s.io/api v0.21.3/go.mod h1:hUgeYHUbBp23Ue4qdX9tR8/ANi/g3ehylAqDn9NWVOg= +k8s.io/api v0.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ= +k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg= +k8s.io/apiextensions-apiserver v0.21.3/go.mod h1:kl6dap3Gd45+21Jnh6utCx8Z2xxLm8LGDkprcd+KbsE= +k8s.io/apiextensions-apiserver v0.26.1 h1:cB8h1SRk6e/+i3NOrQgSFij1B2S0Y0wDoNl66bn8RMI= +k8s.io/apiextensions-apiserver v0.26.1/go.mod h1:AptjOSXDGuE0JICx/Em15PaoO7buLwTs0dGleIHixSM= +k8s.io/apimachinery v0.19.0/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= +k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= +k8s.io/apimachinery v0.21.0-rc.0/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= +k8s.io/apimachinery v0.21.3/go.mod h1:H/IM+5vH9kZRNJ4l3x/fXP/5bOPJaVP/guptnZPeCFI= +k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ= +k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= +k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= +k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= +k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= +k8s.io/apiserver v0.21.3/go.mod h1:eDPWlZG6/cCCMj/JBcEpDoK+I+6i3r9GsChYBHSbAzU= +k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= +k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= +k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= +k8s.io/client-go v0.21.3/go.mod h1:+VPhCgTsaFmGILxR/7E1N0S+ryO010QBeNCv5JwRGYU= +k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU= +k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE= +k8s.io/code-generator v0.19.0/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk= +k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= +k8s.io/code-generator v0.21.0-rc.0/go.mod h1:hUlps5+9QaTrKx+jiM4rmq7YmH8wPOIko64uZCHDh6Q= +k8s.io/code-generator v0.21.3/go.mod h1:K3y0Bv9Cz2cOW2vXUrNZlFbflhuPvuadW6JdnN6gGKo= +k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= +k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= +k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= +k8s.io/component-base v0.21.3/go.mod h1:kkuhtfEHeZM6LkX0saqSK8PbdO7A0HigUngmhhrwfGQ= +k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= +k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU= +k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= +k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= +k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= +k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= +k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= +k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= +k8s.io/pod-security-admission v0.26.1 h1:EDIxsYFeKMzNvN/JB0PgQcuwBP6fIkIG2O8ZWJhzOp4= +k8s.io/pod-security-admission v0.26.1/go.mod h1:hCbYTG5UtLlivmukkMPjAWf23PUBUHzEvR60xNVWN4c= +k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20210722164352-7f3ee0f31471/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= +k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +oras.land/oras-go v1.1.0 h1:tfWM1RT7PzUwWphqHU6ptPU3ZhwVnSw/9nEGf519rYg= +oras.land/oras-go v1.1.0/go.mod h1:1A7vR/0KknT2UkJVWh+xMi95I/AhK8ZrxrnUSmXN0bQ= +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/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.19/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/controller-runtime v0.9.5/go.mod h1:q6PpkM5vqQubEKUKOM6qr06oXGzOBcCby1DA9FbyZeA= +sigs.k8s.io/controller-runtime v0.14.4 h1:Kd/Qgx5pd2XUL08eOV2vwIq3L9GhIbJ5Nxengbd4/0M= +sigs.k8s.io/controller-runtime v0.14.4/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/go.mod b/go.mod index 2d4574b0..906898c6 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/redhat-appstudio/application-service/cdq-analysis v0.0.0 github.com/redhat-appstudio/application-service/gitops-generator v0.0.0 github.com/redhat-appstudio/service-provider-integration-scm-file-retriever v0.8.3 - github.com/redhat-developer/gitops-generator v0.0.0-20230614175323-aff86c6bc55e + github.com/redhat-developer/gitops-generator v0.0.0-20230801134438-01747a27dcbf github.com/spf13/afero v1.8.0 github.com/stretchr/testify v1.8.1 go.uber.org/zap v1.24.0 @@ -161,7 +161,6 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gotest.tools/v3 v3.1.0 // indirect k8s.io/apiextensions-apiserver v0.26.1 // indirect k8s.io/component-base v0.26.1 // indirect k8s.io/klog v1.0.0 // indirect diff --git a/go.sum b/go.sum index d280595d..3ea1994b 100644 --- a/go.sum +++ b/go.sum @@ -999,8 +999,8 @@ github.com/redhat-appstudio/service-provider-integration-scm-file-retriever v0.8 github.com/redhat-appstudio/service-provider-integration-scm-file-retriever v0.8.3/go.mod h1:nGIC0XrYoMm63q1qHS7h/7ktUliMzgQcOOd2XLpNDbA= github.com/redhat-developer/alizer/go v0.0.0-20230516215932-135a2bb3fb90 h1:pFKn+Vqg3xXrueGboXYRkmKPJ6ObaVBNbKF1M7NG/mA= github.com/redhat-developer/alizer/go v0.0.0-20230516215932-135a2bb3fb90/go.mod h1:y7XToX/Yq1ASZY8hatAkmXJSzEXlHZV8lf59wliUfOw= -github.com/redhat-developer/gitops-generator v0.0.0-20230614175323-aff86c6bc55e h1:YIADVYENP/Qy6XFcON9W4VMyxj1tiWYIPEtzW1DLF9A= -github.com/redhat-developer/gitops-generator v0.0.0-20230614175323-aff86c6bc55e/go.mod h1:pKJez873Y9lZwQAgm99i/y1SmPI2dMd19ID74C+CxpU= +github.com/redhat-developer/gitops-generator v0.0.0-20230801134438-01747a27dcbf h1:uRlhXqcVhbvp9YawKoggQoMLZwRP0/kpdLE3wkzqMuI= +github.com/redhat-developer/gitops-generator v0.0.0-20230801134438-01747a27dcbf/go.mod h1:pKJez873Y9lZwQAgm99i/y1SmPI2dMd19ID74C+CxpU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -1789,11 +1789,11 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= gotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk= -gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/go.work.sum b/go.work.sum index 7dcd3677..47de7255 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,6 +1,44 @@ +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +code.gitea.io/sdk/gitea v0.14.0/go.mod h1:89WiyOX1KEcvjP66sRHdu0RafojGo60bT9UqW17VbWs= +github.com/bluekeyes/go-gitdiff v0.4.0/go.mod h1:QpfYYO1E0fTVHVZAZKiRjtSGY9823iCdvGXBcEzHGbM= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= github.com/emicklei/go-restful v2.15.0+incompatible h1:8KpYO/Xl/ZudZs5RNOEhWMBY4hmzlZhhRd9cu+jrZP4= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/jenkins-x/go-scm v1.10.10/go.mod h1:z7xTO9/VzqW3xEbEMH2z5cpOGrZ8+nOHOWfU1ngFGxs= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= +github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= +github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw= +github.com/onsi/ginkgo/v2 v2.6.0/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc= +github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= +github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= github.com/openshift-pipelines/pipelines-as-code v0.0.0-20220622161720-2a6007e17200 h1:zu9Z85uKHVMEg/nAd0J9xNLUnu6C3mtRf5l7xJiTilM= github.com/openshift-pipelines/pipelines-as-code v0.0.0-20220622161720-2a6007e17200/go.mod h1:C2xZcwv55w1W74LjkEN7NRp2z0UOj0/VXMi8ZuNBPGI= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/h2non/gentleman.v1 v1.0.4/go.mod h1:JYuHVdFzS4MKOXe0o+chKJ4hCe6tqKKw9XH9YP6WFrg= +gopkg.in/h2non/gock.v1 v1.0.16/go.mod h1:XVuDAssexPLwgxCLMvDTWNU5eqklsydR6I5phZ9oPB8= +gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= +gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +k8s.io/apimachinery v0.0.0-20190703205208-4cfb76a8bf76/go.mod h1:M2fZgZL9DbLfeJaPBCDqSqNsdsmLN+V29knYJnIXlMA= +k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= +k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= knative.dev/pkg v0.0.0-20220131144930-f4b57aef0006 h1:0Kv7dIOimyHno/7jbQ6LCi15ME+e/b5tY0pwi99kanQ= +knative.dev/pkg v0.0.0-20221011175852-714b7630a836 h1:0N7Zo/O+xeUUebJPm9keBaGclrUoEbljr3J1MsqtaIM= From 88f87f5bf470105f5e259425eda88b84f7a03fc6 Mon Sep 17 00:00:00 2001 From: John Collier Date: Wed, 9 Aug 2023 13:30:51 -0400 Subject: [PATCH 5/9] Staticcheck fixes Signed-off-by: John Collier --- .../applicationsnapshotenvironmentbinding_controller.go | 5 +---- controllers/component_controller.go | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/controllers/applicationsnapshotenvironmentbinding_controller.go b/controllers/applicationsnapshotenvironmentbinding_controller.go index bcb534df..4b445f4d 100644 --- a/controllers/applicationsnapshotenvironmentbinding_controller.go +++ b/controllers/applicationsnapshotenvironmentbinding_controller.go @@ -27,7 +27,6 @@ import ( "github.com/go-logr/logr" appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" - "github.com/redhat-appstudio/application-service/gitops-generator/pkg/generate" github "github.com/redhat-appstudio/application-service/pkg/github" logutil "github.com/redhat-appstudio/application-service/pkg/log" "github.com/redhat-appstudio/application-service/pkg/util/ioutils" @@ -154,7 +153,7 @@ func (r *SnapshotEnvironmentBindingReconciler) Reconcile(ctx context.Context, re localGitopsGen := (!r.DoGitOpsJob) || (r.AllowLocalGitopsGen && appSnapshotEnvBinding.Annotations["allowLocalGitopsGen"] == "true") if localGitopsGen { - err = gitopsjoblib.GenerateGitopsOverlays(context.Background(), log, r.Client, appSnapshotEnvBinding, ioutils.NewFilesystem(), generate.GitOpsGenParams{ + err = gitopsjoblib.GenerateGitopsOverlays(context.Background(), log, r.Client, appSnapshotEnvBinding, ioutils.NewFilesystem(), gitopsjoblib.GitOpsGenParams{ Generator: r.Generator, Token: ghClient.Token, }) @@ -162,8 +161,6 @@ func (r *SnapshotEnvironmentBindingReconciler) Reconcile(ctx context.Context, re r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) return ctrl.Result{}, err } - } else { - // ToDo: Add support for GitOps job } r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, nil) diff --git a/controllers/component_controller.go b/controllers/component_controller.go index 20366bf7..fead67a7 100644 --- a/controllers/component_controller.go +++ b/controllers/component_controller.go @@ -25,7 +25,6 @@ import ( "github.com/prometheus/client_golang/prometheus" cdqanalysis "github.com/redhat-appstudio/application-service/cdq-analysis/pkg" - "github.com/redhat-appstudio/application-service/gitops-generator/pkg/generate" gitopsjoblib "github.com/redhat-appstudio/application-service/gitops-generator/pkg/generate" "github.com/redhat-appstudio/application-service/pkg/metrics" corev1 "k8s.io/api/core/v1" @@ -562,7 +561,7 @@ func (r *ComponentReconciler) generateGitops(ctx context.Context, ghClient *gith localGitopsGen := (!r.DoGitOpsJob) || (r.AllowLocalGitopsGen && component.Annotations["allowLocalGitopsGen"] == "true") if localGitopsGen { - err = gitopsjoblib.GenerateGitopsBase(context.Background(), log, r.Client, *component, ioutils.NewFilesystem(), generate.GitOpsGenParams{ + err = gitopsjoblib.GenerateGitopsBase(context.Background(), log, r.Client, *component, ioutils.NewFilesystem(), gitopsjoblib.GitOpsGenParams{ Generator: r.Generator, Token: ghClient.Token, RemoteURL: gitOpsURL, @@ -572,8 +571,6 @@ func (r *ComponentReconciler) generateGitops(ctx context.Context, ghClient *gith if err != nil { return err } - } else { - // ToDo: Implement GitOps generation in Kubernetes jobs } return nil From 14c0f2807af46089ac932730215ccaf002edea36 Mon Sep 17 00:00:00 2001 From: John Collier Date: Wed, 9 Aug 2023 13:31:44 -0400 Subject: [PATCH 6/9] Fix dockerfile Signed-off-by: John Collier --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 0f954b32..acd059e3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /workspace COPY go.mod go.mod COPY go.sum go.sum COPY cdq-analysis/ cdq-analysis/ +COPY gitops-generator/ gitops-generator/ # cache deps before building and copying source so that we don't need to re-download as much # and so that source changes don't invalidate our downloaded layer RUN go mod download From 7ed43914d6e953848a66b077c90272eab7781234 Mon Sep 17 00:00:00 2001 From: John Collier Date: Wed, 9 Aug 2023 13:34:18 -0400 Subject: [PATCH 7/9] Cleanup Signed-off-by: John Collier --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index acd059e3..fe7fddec 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,6 @@ COPY main.go main.go # ToDo: Uncomment once API added COPY controllers/ controllers/ COPY pkg pkg/ -COPY gitops gitops/ # Build RUN CGO_ENABLED=0 GOOS=linux go build -a -o manager main.go From 8ddba5f1b0297a8ae0e502caf238d88930a96353 Mon Sep 17 00:00:00 2001 From: John Collier Date: Wed, 9 Aug 2023 14:33:06 -0400 Subject: [PATCH 8/9] Fix tests Signed-off-by: John Collier --- controllers/component_controller_unit_test.go | 554 +++++++++++++----- 1 file changed, 396 insertions(+), 158 deletions(-) diff --git a/controllers/component_controller_unit_test.go b/controllers/component_controller_unit_test.go index e4019a74..2a87d116 100644 --- a/controllers/component_controller_unit_test.go +++ b/controllers/component_controller_unit_test.go @@ -22,28 +22,371 @@ import ( "reflect" "testing" - "github.com/golang/mock/gomock" gitopsjoblib "github.com/redhat-appstudio/application-service/gitops-generator/pkg/generate" "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" "github.com/devfile/api/v2/pkg/attributes" data "github.com/devfile/library/v2/pkg/devfile/parser/data" v2 "github.com/devfile/library/v2/pkg/devfile/parser/data/v2" - "github.com/devfile/library/v2/pkg/devfile/parser/data/v2/common" appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" - devfile "github.com/redhat-appstudio/application-service/pkg/devfile" "github.com/redhat-appstudio/application-service/pkg/github" "github.com/redhat-appstudio/application-service/pkg/util/ioutils" "github.com/spf13/afero" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/yaml" devfileApi "github.com/devfile/api/v2/pkg/devfile" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" //+kubebuilder:scaffold:imports ) +var kubernetesInlinedDevfile = ` +commands: +- apply: + component: image-build + id: build-image +- apply: + component: kubernetes-deploy + id: deployk8s +- composite: + commands: + - build-image + - deployk8s + group: + isDefault: true + kind: deploy + parallel: false + id: deploy +components: +- image: + autoBuild: false + dockerfile: + buildContext: . + rootRequired: false + uri: docker/Dockerfile + imageName: java-springboot-image:latest + name: image-build +- attributes: + api.devfile.io/k8sLikeComponent-originalURI: deploy.yaml + deployment/container-port: 5566 + deployment/containerENV: + - name: FOO + value: foo11 + - name: BAR + value: bar11 + deployment/cpuLimit: "2" + deployment/cpuRequest: 701m + deployment/memoryLimit: 500Mi + deployment/memoryRequest: 401Mi + deployment/replicas: 5 + deployment/route: route111222 + deployment/storageLimit: 400Mi + deployment/storageRequest: 201Mi + kubernetes: + deployByDefault: false + endpoints: + - name: http-8081 + path: / + secure: false + targetPort: 8081 + inlined: |- + apiVersion: apps/v1 + kind: Deployment + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + maysun: test + name: deploy-sample + spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: component-sample + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/instance: component-sample + spec: + containers: + - env: + - name: FOO + value: foo1 + - name: BARBAR + value: bar1 + image: quay.io/redhat-appstudio/user-workload:application-service-system-component-sample + imagePullPolicy: Always + livenessProbe: + httpGet: + path: / + port: 1111 + initialDelaySeconds: 10 + periodSeconds: 10 + name: container-image + ports: + - containerPort: 1111 + readinessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + tcpSocket: + port: 1111 + resources: + limits: + cpu: "2" + memory: 500Mi + storage: 400Mi + requests: + cpu: 700m + memory: 400Mi + storage: 200Mi + status: {} + --- + apiVersion: apps/v1 + kind: Deployment + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + maysun: test + name: deploy-sample-2 + spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: component-sample + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/instance: component-sample + spec: + containers: + - env: + - name: FOO + value: foo1 + - name: BAR + value: bar1 + image: quay.io/redhat-appstudio/user-workload:application-service-system-component-sample + imagePullPolicy: Always + livenessProbe: + httpGet: + path: / + port: 1111 + initialDelaySeconds: 10 + periodSeconds: 10 + name: container-image + ports: + - containerPort: 1111 + readinessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + tcpSocket: + port: 1111 + resources: + limits: + cpu: "2" + memory: 500Mi + storage: 400Mi + requests: + cpu: 700m + memory: 400Mi + storage: 200Mi + status: {} + --- + apiVersion: v1 + kind: Service + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + maysun: test + name: service-sample + spec: + ports: + - port: 1111 + targetPort: 1111 + selector: + app.kubernetes.io/instance: component-sample + status: + loadBalancer: {} + --- + apiVersion: v1 + kind: Service + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + maysun: test + name: service-sample-2 + spec: + ports: + - port: 1111 + targetPort: 1111 + selector: + app.kubernetes.io/instance: component-sample + status: + loadBalancer: {} + --- + apiVersion: route.openshift.io/v1 + kind: Route + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + maysun: test + name: route-sample + spec: + host: route111 + port: + targetPort: 1111 + tls: + insecureEdgeTerminationPolicy: Redirect + termination: edge + to: + kind: Service + name: component-sample + weight: 100 + status: {} + --- + apiVersion: route.openshift.io/v1 + kind: Route + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + maysun: test + name: route-sample-2 + spec: + host: route111 + port: + targetPort: 1111 + tls: + insecureEdgeTerminationPolicy: Redirect + termination: edge + to: + kind: Service + name: component-sample + weight: 100 + status: {} + --- + apiVersion: networking.k8s.io/v1 + kind: Ingress + metadata: + name: ingress-sample + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + maysun: test + spec: + ingressClassName: nginx-example + rules: + - http: + paths: + - path: /testpath + pathType: Prefix + backend: + service: + name: test + port: + number: 80 + --- + apiVersion: networking.k8s.io/v1 + kind: Ingress + metadata: + name: ingress-sample-2 + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + maysun: test + spec: + ingressClassName: nginx-example + rules: + - http: + paths: + - path: /testpath + pathType: Prefix + backend: + service: + name: test + port: + number: 80 + --- + apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: pvc-sample + labels: + maysun: test + spec: + accessModes: + - ReadWriteOnce + volumeMode: Filesystem + resources: + requests: + storage: 8Gi + storageClassName: slow + selector: + matchLabels: + release: "stable" + matchExpressions: + - {key: environment, operator: In, values: [dev]} + --- + apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: pvc-sample-2 + labels: + maysun: test + spec: + accessModes: + - ReadWriteOnce + volumeMode: Filesystem + resources: + requests: + storage: 8Gi + storageClassName: slow + selector: + matchLabels: + release: "stable" + matchExpressions: + - {key: environment, operator: In, values: [dev]} + name: kubernetes-deploy +metadata: + name: java-springboot +schemaVersion: 2.2.0` + +var noDeployDevfile = ` +metadata: + name: java-springboot +schemaVersion: 2.2.0` + +var invalidDevfile = `safdsfsdl32432423\n\t` + func TestSetGitOpsStatus(t *testing.T) { tests := []struct { name string @@ -191,144 +534,66 @@ func TestGenerateGitops(t *testing.T) { }, } - componentNames := []string{"testcomp0", "testcomp1", "testcomp2"} - isDefault := true - notDefault := false - - applyCommands := []v1alpha2.Command{ - { - Id: "apply0", - CommandUnion: v1alpha2.CommandUnion{ - Apply: &v1alpha2.ApplyCommand{ - Component: componentNames[0], - LabeledCommand: v1alpha2.LabeledCommand{ - BaseCommand: v1alpha2.BaseCommand{ - Group: &v1alpha2.CommandGroup{ - Kind: v1alpha2.DeployCommandGroupKind, - IsDefault: &isDefault, - }, - }, - }, - }, - }, - }, - { - Id: "apply1", - CommandUnion: v1alpha2.CommandUnion{ - Apply: &v1alpha2.ApplyCommand{ - Component: componentNames[1], - }, - }, - }, + tests := []struct { + name string + reconciler *ComponentReconciler + fs afero.Afero + component *appstudiov1alpha1.Component + devfile string + wantErr bool + }{ { - Id: "apply2", - CommandUnion: v1alpha2.CommandUnion{ - Apply: &v1alpha2.ApplyCommand{ - Component: componentNames[2], + name: "Simple application component, no errors", + reconciler: r, + fs: appFS, + component: &appstudiov1alpha1.Component{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "appstudio.redhat.com/v1alpha1", + Kind: "Component", }, - }, - }, - } - deployCommands := []v1alpha2.Command{ - { - Id: "applynotdefault", - CommandUnion: v1alpha2.CommandUnion{ - Apply: &v1alpha2.ApplyCommand{ - Component: componentNames[0], - LabeledCommand: v1alpha2.LabeledCommand{ - BaseCommand: v1alpha2.BaseCommand{ - Group: &v1alpha2.CommandGroup{ - Kind: v1alpha2.DeployCommandGroupKind, - IsDefault: ¬Default, - }, - }, - }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-component", + Namespace: "test-namespace", }, - }, - }, - { - Id: "apply0", - CommandUnion: v1alpha2.CommandUnion{ - Apply: &v1alpha2.ApplyCommand{ - Component: componentNames[0], - LabeledCommand: v1alpha2.LabeledCommand{ - BaseCommand: v1alpha2.BaseCommand{ - Group: &v1alpha2.CommandGroup{ - Kind: v1alpha2.DeployCommandGroupKind, - }, - }, + Spec: componentSpec, + Status: appstudiov1alpha1.ComponentStatus{ + GitOps: appstudiov1alpha1.GitOpsStatus{ + RepositoryURL: "https://github.com/test/repo", + Branch: "main", + Context: "/test", }, }, }, + devfile: kubernetesInlinedDevfile, + wantErr: false, }, { - Id: "composite1", - CommandUnion: v1alpha2.CommandUnion{ - Composite: &v1alpha2.CompositeCommand{ - Commands: []string{"apply0", "apply2"}, - LabeledCommand: v1alpha2.LabeledCommand{ - BaseCommand: v1alpha2.BaseCommand{ - Group: &v1alpha2.CommandGroup{ - Kind: v1alpha2.DeployCommandGroupKind, - IsDefault: &isDefault, - }, - }, - }, + name: "Simple application component invalid devfile", + reconciler: r, + fs: appFS, + component: &appstudiov1alpha1.Component{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "appstudio.redhat.com/v1alpha1", + Kind: "Component", }, - }, - }, - { - Id: "compositenotdefault", - CommandUnion: v1alpha2.CommandUnion{ - Composite: &v1alpha2.CompositeCommand{ - Commands: []string{"apply0", "apply2"}, - LabeledCommand: v1alpha2.LabeledCommand{ - BaseCommand: v1alpha2.BaseCommand{ - Group: &v1alpha2.CommandGroup{ - Kind: v1alpha2.DeployCommandGroupKind, - IsDefault: ¬Default, - }, - }, - }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-component", + Namespace: "test-namespace", }, - }, - }, - } - - compName := "component" - applicationName := "application" - image := "image" - - deploymentTemplate := devfile.GenerateDeploymentTemplate(compName, applicationName, image) - deploymentTemplateBytes, err := yaml.Marshal(deploymentTemplate) - if err != nil { - t.Errorf("TestConvertImageComponentToDevfile() unexpected error: %v", err) - return - } - kubernetesComponents := []v1alpha2.Component{ - { - Name: "kubernetes-deploy", - ComponentUnion: v1alpha2.ComponentUnion{ - Kubernetes: &v1alpha2.KubernetesComponent{ - K8sLikeComponent: v1alpha2.K8sLikeComponent{ - K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{ - Inlined: string(deploymentTemplateBytes), - }, + Spec: componentSpec, + Status: appstudiov1alpha1.ComponentStatus{ + GitOps: appstudiov1alpha1.GitOpsStatus{ + RepositoryURL: "https://github.com/test/repo", + Branch: "main", + Context: "/test", }, }, }, + devfile: invalidDevfile, + wantErr: true, }, - } - - tests := []struct { - name string - reconciler *ComponentReconciler - fs afero.Afero - component *appstudiov1alpha1.Component - wantErr bool - }{ { - name: "Simple application component, no errors", + name: "Simple application component no outerloop deploy", reconciler: r, fs: appFS, component: &appstudiov1alpha1.Component{ @@ -349,7 +614,8 @@ func TestGenerateGitops(t *testing.T) { }, }, }, - wantErr: false, + devfile: noDeployDevfile, + wantErr: true, }, { name: "Invalid application component, no labels", @@ -429,6 +695,7 @@ func TestGenerateGitops(t *testing.T) { }, }, }, + devfile: kubernetesInlinedDevfile, wantErr: false, }, { @@ -525,40 +792,11 @@ func TestGenerateGitops(t *testing.T) { tt.reconciler.AppFS = tt.fs t.Run(tt.name, func(t *testing.T) { - goMockCtrl := gomock.NewController(t) - defer goMockCtrl.Finish() - mockDevfileData := data.NewMockDevfileData(goMockCtrl) - - // set up the mock data - deployCommandFilter := common.DevfileOptions{ - CommandOptions: common.CommandOptions{ - CommandGroupKind: v1alpha2.DeployCommandGroupKind, - }, - } - mockDeployCommands := mockDevfileData.EXPECT().GetCommands(deployCommandFilter) - mockDeployCommands.Return(deployCommands, nil).AnyTimes() - - applyCommandFilter := common.DevfileOptions{ - CommandOptions: common.CommandOptions{ - CommandType: v1alpha2.ApplyCommandType, - }, - } - mockApplyCommands := mockDevfileData.EXPECT().GetCommands(applyCommandFilter) - mockApplyCommands.Return(applyCommands, nil).AnyTimes() - - kubernetesComponentFilter := common.DevfileOptions{ - ComponentOptions: common.ComponentOptions{ - ComponentType: v1alpha2.KubernetesComponentType, - }, - } - mockKubernetesComponents := mockDevfileData.EXPECT().GetComponents(kubernetesComponentFilter) - mockKubernetesComponents.Return(kubernetesComponents, nil).AnyTimes() + tt.component.Status.Devfile = string(tt.devfile) mockedClient := &github.GitHubClient{ Client: github.GetMockedClient(), TokenName: "some-token", } - devfileYaml, _ := yaml.Marshal(mockDevfileData) - tt.component.Status.Devfile = string(devfileYaml) err := tt.reconciler.generateGitops(ctx, mockedClient, tt.component) if (err != nil) != tt.wantErr { t.Errorf("TestGenerateGitops() unexpected error: %v", err) From 45f2e6c9a389dc3489ab9304e472e42ddaeac5d8 Mon Sep 17 00:00:00 2001 From: John Collier Date: Thu, 10 Aug 2023 10:57:42 -0400 Subject: [PATCH 9/9] Go mod tidy again Signed-off-by: John Collier --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 906898c6..d274fc5e 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/devfile/library/v2 v2.2.1-0.20230418160146-e75481b7eebd github.com/go-logr/logr v1.2.3 github.com/gofri/go-github-ratelimit v1.0.3-0.20230428184158-a500e14de53f - github.com/golang/mock v1.6.0 github.com/google/go-github/v52 v52.0.1-0.20230514113659-60429b4ba0ba github.com/migueleliasweb/go-github-mock v0.0.17 github.com/onsi/ginkgo v1.16.5 @@ -74,6 +73,7 @@ require ( github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect