From fd81dfea9d82b12152409593d19dfedfeef6f5b0 Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Wed, 9 Oct 2024 13:05:19 -0700 Subject: [PATCH] Client (mesh egress) discovery for Jobs and Numaflow (vertices and monovertices) (#339) ### Description What does this change do and why? This change introduces client discovery where there is no ingress but only mesh egress required for types k8s Jobs, numaflow Vertices and Monovertices. Signed-off-by: Anil Attuluri --- Makefile | 2 + admiral/cmd/admiral/cmd/root.go | 4 + admiral/pkg/client/loader/client_loader.go | 4 + admiral/pkg/client/loader/fake_loader.go | 17 + admiral/pkg/client/loader/kube_loader.go | 14 + .../pkg/clusters/clientdiscovery_handler.go | 86 +++ .../clusters/clientdiscovery_handler_test.go | 179 +++++ admiral/pkg/clusters/dependency_handler.go | 12 +- .../pkg/clusters/dependency_handler_test.go | 9 +- admiral/pkg/clusters/handler.go | 30 + admiral/pkg/clusters/handler_test.go | 32 +- admiral/pkg/clusters/registry.go | 37 +- admiral/pkg/clusters/registry_test.go | 1 + admiral/pkg/clusters/serviceentry.go | 3 +- admiral/pkg/clusters/types.go | 3 + admiral/pkg/controller/admiral/controller.go | 4 + admiral/pkg/controller/admiral/dependency.go | 2 +- .../pkg/controller/admiral/dependency_test.go | 18 +- admiral/pkg/controller/admiral/job.go | 258 +++++++ admiral/pkg/controller/admiral/job_test.go | 509 ++++++++++++++ admiral/pkg/controller/admiral/monovertex.go | 266 ++++++++ .../pkg/controller/admiral/monovertex_test.go | 508 ++++++++++++++ admiral/pkg/controller/admiral/vertex.go | 267 ++++++++ admiral/pkg/controller/admiral/vertex_test.go | 510 ++++++++++++++ admiral/pkg/controller/common/common.go | 54 ++ admiral/pkg/controller/common/common_test.go | 184 +++++ admiral/pkg/controller/common/config.go | 18 + admiral/pkg/controller/common/config_test.go | 59 +- admiral/pkg/controller/common/types.go | 14 + admiral/pkg/test/mock.go | 8 + go.mod | 49 +- go.sum | 642 ++---------------- install/admiral/base/deployments.yaml | 4 + .../demosinglecluster/envconfig_values.yaml | 4 + install/admiralremote/base/remote.yaml | 8 +- install/sample/overlays/job/job.yaml | 20 + .../sample/overlays/job/kustomization.yaml | 13 + install/sample/overlays/job/webapp.yaml | 35 + .../overlays/numaflow/kustomization.yaml | 13 + .../sample/overlays/numaflow/monovertex.yaml | 22 + install/sample/overlays/numaflow/webapp.yaml | 35 + install/sample/sample_dep.yaml | 29 +- install/scripts/install_numaflow.sh | 17 + install/scripts/install_sample_services.sh | 33 +- tests/client_discovery_test.sh | 27 + tests/install_istio.sh | 3 - tests/run.sh | 6 +- tests/test7.sh | 31 - 48 files changed, 3357 insertions(+), 746 deletions(-) create mode 100644 admiral/pkg/clusters/clientdiscovery_handler.go create mode 100644 admiral/pkg/clusters/clientdiscovery_handler_test.go create mode 100644 admiral/pkg/controller/admiral/job.go create mode 100644 admiral/pkg/controller/admiral/job_test.go create mode 100644 admiral/pkg/controller/admiral/monovertex.go create mode 100644 admiral/pkg/controller/admiral/monovertex_test.go create mode 100644 admiral/pkg/controller/admiral/vertex.go create mode 100644 admiral/pkg/controller/admiral/vertex_test.go create mode 100644 install/sample/overlays/job/job.yaml create mode 100644 install/sample/overlays/job/kustomization.yaml create mode 100644 install/sample/overlays/job/webapp.yaml create mode 100644 install/sample/overlays/numaflow/kustomization.yaml create mode 100644 install/sample/overlays/numaflow/monovertex.yaml create mode 100644 install/sample/overlays/numaflow/webapp.yaml create mode 100755 install/scripts/install_numaflow.sh create mode 100755 tests/client_discovery_test.sh delete mode 100755 tests/test7.sh diff --git a/Makefile b/Makefile index 2af5264a..065ced55 100644 --- a/Makefile +++ b/Makefile @@ -162,6 +162,8 @@ gen-yaml: kustomize build ./install/sample/overlays/rollout-canary > ./out/yaml/sample-greeting-rollout-canary.yaml kustomize build ./install/sample/overlays/rollout-bluegreen > ./out/yaml/sample-greeting-rollout-bluegreen.yaml kustomize build ./install/sample/overlays/remote > ./out/yaml/remotecluster_sample.yaml + kustomize build ./install/sample/overlays/job > ./out/yaml/job_sample.yaml + kustomize build ./install/sample/overlays/numaflow > ./out/yaml/numaflow_sample.yaml cp ./install/sample/proxy.yaml ./out/yaml/proxy.yaml cp ./install/sample/sample_dep.yaml ./out/yaml/sample_dep.yaml cp ./install/sample/depProxyExample.yaml ./out/yaml/depProxyExample.yaml diff --git a/admiral/cmd/admiral/cmd/root.go b/admiral/cmd/admiral/cmd/root.go index dd59de1f..c703e73c 100644 --- a/admiral/cmd/admiral/cmd/root.go +++ b/admiral/cmd/admiral/cmd/root.go @@ -259,6 +259,10 @@ func GetRootCmd(args []string) *cobra.Command { rootCmd.PersistentFlags().StringSliceVar(¶ms.IngressVSExportToNamespaces, "ingress_vs_export_to_namespaces", []string{"istio-system"}, "List of namespaces where the ingress VS should be exported") rootCmd.PersistentFlags().StringVar(¶ms.IngressLBPolicy, "ingress_lb_policy", "round_robin", "loadbalancer policy for ingress destination rule (round_robin/random/passthrough/least_request)") + rootCmd.PersistentFlags().BoolVar(¶ms.EnableClientDiscovery, "enable_client_discovery", true, "Enable/Disable Client (mesh egress) Discovery") + rootCmd.PersistentFlags().StringArrayVar(¶ms.ClientDiscoveryClustersForJobs, "client_discovery_clusters_for_jobs", []string{}, "List of clusters for client discovery for k8s jobs") + rootCmd.PersistentFlags().StringArrayVar(¶ms.DiscoveryClustersForNumaflow, "client_discovery_clusters_for_numaflow", []string{}, "List of clusters for client discovery for numaflow types") + return rootCmd } diff --git a/admiral/pkg/client/loader/client_loader.go b/admiral/pkg/client/loader/client_loader.go index 8890ffb5..fc403db9 100644 --- a/admiral/pkg/client/loader/client_loader.go +++ b/admiral/pkg/client/loader/client_loader.go @@ -4,6 +4,7 @@ import ( argo "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned" admiralapi "github.com/istio-ecosystem/admiral-api/pkg/client/clientset/versioned" admiral "github.com/istio-ecosystem/admiral/admiral/pkg/client/clientset/versioned" + numaflow "github.com/numaproj/numaflow/pkg/client/clientset/versioned" istio "istio.io/client-go/pkg/clientset/versioned" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" @@ -26,4 +27,7 @@ type ClientLoader interface { LoadKubeClientFromPath(path string) (kubernetes.Interface, error) LoadKubeClientFromConfig(config *rest.Config) (kubernetes.Interface, error) + + LoadNumaflowClientFromPath(path string) (numaflow.Interface, error) + LoadNumaflowClientFromConfig(config *rest.Config) (numaflow.Interface, error) } diff --git a/admiral/pkg/client/loader/fake_loader.go b/admiral/pkg/client/loader/fake_loader.go index a6901ae4..4a06f985 100644 --- a/admiral/pkg/client/loader/fake_loader.go +++ b/admiral/pkg/client/loader/fake_loader.go @@ -9,6 +9,8 @@ import ( admiralfake "github.com/istio-ecosystem/admiral/admiral/pkg/client/clientset/versioned/fake" istio "istio.io/client-go/pkg/clientset/versioned" istiofake "istio.io/client-go/pkg/clientset/versioned/fake" + numaflow "github.com/numaproj/numaflow/pkg/client/clientset/versioned" + numaflowfake "github.com/numaproj/numaflow/pkg/client/clientset/versioned/fake" "k8s.io/client-go/kubernetes" kubefake "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/rest" @@ -22,6 +24,7 @@ var FakeAdmiralApiClient admiralapi.Interface = admiralapifake.NewSimpleClientse var FakeIstioClient istio.Interface = istiofake.NewSimpleClientset() var FakeKubeClient kubernetes.Interface = kubefake.NewSimpleClientset() var FakeArgoClient argo.Interface = argofake.NewSimpleClientset() +var FakeNumaflowClient numaflow.Interface = numaflowfake.NewSimpleClientset() // fake clients for dependent clusters var FakeAdmiralClientMap map[string]admiral.Interface = make(map[string]admiral.Interface) @@ -29,6 +32,7 @@ var FakeAdmiralApiClientMap map[string]admiralapi.Interface = make(map[string]ad var FakeIstioClientMap map[string]istio.Interface = make(map[string]istio.Interface) var FakeKubeClientMap map[string]kubernetes.Interface = make(map[string]kubernetes.Interface) var FakeArgoClientMap map[string]argo.Interface = make(map[string]argo.Interface) +var FakeNumaflowClientMap map[string]numaflow.Interface = make(map[string]numaflow.Interface) type FakeClientLoader struct{} @@ -91,6 +95,19 @@ func (loader *FakeClientLoader) LoadArgoClientFromConfig(config *rest.Config) (a return argoClient, nil } +func (loader *FakeClientLoader) LoadNumaflowClientFromPath(path string) (numaflow.Interface, error) { + return FakeNumaflowClient, nil +} + +func (loader *FakeClientLoader) LoadNumaflowClientFromConfig(config *rest.Config) (numaflow.Interface, error) { + numaflowClient, ok := FakeNumaflowClientMap[config.Host] + if !ok { + numaflowClient = numaflowfake.NewSimpleClientset() + FakeNumaflowClientMap[config.Host] = numaflowClient + } + return numaflowClient, nil +} + func (loader *FakeClientLoader) LoadKubeClientFromPath(path string) (kubernetes.Interface, error) { return FakeKubeClient, nil } diff --git a/admiral/pkg/client/loader/kube_loader.go b/admiral/pkg/client/loader/kube_loader.go index 2be4e880..9a2f4aaa 100644 --- a/admiral/pkg/client/loader/kube_loader.go +++ b/admiral/pkg/client/loader/kube_loader.go @@ -5,6 +5,7 @@ import ( argo "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned" admiralapi "github.com/istio-ecosystem/admiral-api/pkg/client/clientset/versioned" admiral "github.com/istio-ecosystem/admiral/admiral/pkg/client/clientset/versioned" + numaflow "github.com/numaproj/numaflow/pkg/client/clientset/versioned" log "github.com/sirupsen/logrus" istio "istio.io/client-go/pkg/clientset/versioned" "k8s.io/client-go/kubernetes" @@ -85,6 +86,19 @@ func (loader *KubeClientLoader) LoadKubeClientFromConfig(config *rest.Config) (k return kubernetes.NewForConfig(config) } +func (loader *KubeClientLoader) LoadNumaflowClientFromPath(kubeConfigPath string) (numaflow.Interface, error) { + config, err := getConfig(kubeConfigPath) + if err != nil || config == nil { + return nil, err + } + + return loader.LoadNumaflowClientFromConfig(config) +} + +func (loader *KubeClientLoader) LoadNumaflowClientFromConfig(config *rest.Config) (numaflow.Interface, error) { + return numaflow.NewForConfig(config) +} + func getConfig(kubeConfigPath string) (*rest.Config, error) { log.Infof("getting kubeconfig from: %#v", kubeConfigPath) // create the config from the path diff --git a/admiral/pkg/clusters/clientdiscovery_handler.go b/admiral/pkg/clusters/clientdiscovery_handler.go new file mode 100644 index 00000000..ca443d22 --- /dev/null +++ b/admiral/pkg/clusters/clientdiscovery_handler.go @@ -0,0 +1,86 @@ +package clusters + +import ( + "context" + "fmt" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/admiral" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + commonUtil "github.com/istio-ecosystem/admiral/admiral/pkg/util" + log "github.com/sirupsen/logrus" +) + +type ClientDiscoveryHandler struct { + RemoteRegistry *RemoteRegistry + ClusterID string +} + +func (cdh *ClientDiscoveryHandler) Added(ctx context.Context, obj *common.K8sObject) error { + err := HandleEventForClientDiscovery(ctx, admiral.Add, obj, cdh.RemoteRegistry, cdh.ClusterID) + if err != nil { + return fmt.Errorf(LogErrFormat, common.Add, common.JobResourceType, obj.Name, cdh.ClusterID, err) + } + return err +} + +func HandleEventForClientDiscovery(ctx context.Context, event admiral.EventType, obj *common.K8sObject, + remoteRegistry *RemoteRegistry, clusterName string) error { + log.Infof(LogFormat, event, obj.Type, obj.Name, clusterName, common.ReceivedStatus) + globalIdentifier := common.GetGlobalIdentifier(obj.Annotations, obj.Labels) + originalIdentifier := common.GetOriginalIdentifier(obj.Annotations, obj.Labels) + if len(globalIdentifier) == 0 { + log.Infof(LogFormat, event, obj.Type, obj.Name, clusterName, "Skipped as '"+common.GetWorkloadIdentifier()+" was not found', namespace="+obj.Namespace) + return nil + } + ctxLogger := common.GetCtxLogger(ctx, globalIdentifier, "") + + ctx = context.WithValue(ctx, "clusterName", clusterName) + ctx = context.WithValue(ctx, "eventResourceType", obj.Type) + + if remoteRegistry.AdmiralCache != nil { + + UpdateIdentityClusterCache(remoteRegistry, globalIdentifier, clusterName) + + if common.EnableSWAwareNSCaches() { + if remoteRegistry.AdmiralCache.IdentityClusterNamespaceCache != nil { + remoteRegistry.AdmiralCache.IdentityClusterNamespaceCache.Put(globalIdentifier, clusterName, obj.Namespace, obj.Namespace) + } + if remoteRegistry.AdmiralCache.PartitionIdentityCache != nil && len(common.GetIdentityPartition(obj.Annotations, obj.Labels)) > 0 { + remoteRegistry.AdmiralCache.PartitionIdentityCache.Put(globalIdentifier, originalIdentifier) + } + } + } else { + log.Warnf(LogFormatAdv, "Process", obj.Type, obj.Name, obj.Namespace, clusterName, "Skipping client discovery as Admiral cache is not initialized for identity="+globalIdentifier) + return fmt.Errorf(common.CtxLogFormat, event, obj.Name, obj.Namespace, clusterName, "processing skipped as Admiral cache is not initialized for identity="+globalIdentifier) + } + + if commonUtil.IsAdmiralReadOnly() { + ctxLogger.Infof(common.CtxLogFormat, event, "", "", "", "processing skipped as Admiral is in Read-only mode") + return nil + } + + if IsCacheWarmupTime(remoteRegistry) { + ctxLogger.Infof(common.CtxLogFormat, event, "", "", "", "processing skipped during cache warm up state") + return fmt.Errorf(common.CtxLogFormat, event, obj.Name, obj.Namespace, clusterName, "processing skipped during cache warm up state for env="+" identity="+globalIdentifier) + } + + //if we have a deployment/rollout in this namespace skip processing to save some cycles + if DeploymentOrRolloutExistsInNamespace(remoteRegistry, globalIdentifier, clusterName, obj.Namespace) { + log.Infof(LogFormatAdv, "Process", obj.Type, obj.Name, obj.Namespace, clusterName, "Skipping client discovery as Deployment/Rollout already present in namespace for client="+globalIdentifier) + return nil + } + + //write SEs required for this client + depRecord := remoteRegistry.DependencyController.Cache.Get(globalIdentifier) + + if depRecord == nil { + log.Warnf(LogFormatAdv, "Process", obj.Type, obj.Name, obj.Namespace, clusterName, "Skipping client discovery as no dependency record found for client="+globalIdentifier) + return nil + } + err := remoteRegistry.DependencyController.DepHandler.Added(ctx, depRecord) + + if err != nil { + return fmt.Errorf(LogFormatAdv, "Process", obj.Type, obj.Name, obj.Namespace, clusterName, "Error processing client discovery") + } + + return nil +} diff --git a/admiral/pkg/clusters/clientdiscovery_handler_test.go b/admiral/pkg/clusters/clientdiscovery_handler_test.go new file mode 100644 index 00000000..55e047e1 --- /dev/null +++ b/admiral/pkg/clusters/clientdiscovery_handler_test.go @@ -0,0 +1,179 @@ +package clusters + +import ( + "context" + "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/model" + "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1alpha1" + "github.com/stretchr/testify/assert" + "testing" + "time" + + argo "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + "github.com/istio-ecosystem/admiral/admiral/pkg/client/loader" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/admiral" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + "github.com/istio-ecosystem/admiral/admiral/pkg/test" + coreV1 "k8s.io/api/core/v1" + metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/rest" +) + +func TestClientDiscoveryHandler_Added(t *testing.T) { + //Struct of test case info. Name is required. + const ( + namespace = "namespace" + namespace_no_deps = "namespace-no-deps" + namespaceForRollout = "rollout-namespace" + cluster1 = "cluster1" + cluster2 = "cluster2" + cluster3 = "cluster3" + identity1 = "identity1" + identity2 = "identity2" + identity3 = "identity3" + identity4 = "identity4" + ) + var ( + stop = make(chan struct{}) + config = rest.Config{ + Host: "localhost", + } + matchLabel = map[string]string{ + "app": "test", + } + labelSelector = metaV1.LabelSelector{ + MatchLabels: matchLabel, + } + dep = v1alpha1.Dependency{ + Spec: model.Dependency{ + Source: identity1, + Destinations: []string{identity2}, + }, + } + rollout = argo.Rollout{ + Spec: argo.RolloutSpec{ + Selector: &labelSelector, + Strategy: argo.RolloutStrategy{ + BlueGreen: &argo.BlueGreenStrategy{}, + }, + Template: coreV1.PodTemplateSpec{ + ObjectMeta: metaV1.ObjectMeta{Annotations: map[string]string{ + "sidecar.istio.io/inject": "true", + "identity": identity1, + }, Labels: map[string]string{ + "env": "stage", + }}, + }, + }, + ObjectMeta: metaV1.ObjectMeta{ + Namespace: namespaceForRollout, + }, + } + objRolloutNamespace = &common.K8sObject{ + Namespace: namespaceForRollout, + Name: "obj-rollout-namespace", + Annotations: map[string]string{"identity": identity1}, + } + objValid = &common.K8sObject{ + Namespace: namespace, + Name: "obj", + Annotations: map[string]string{"identity": identity1}, + } + objValidWithNoDeps = &common.K8sObject{ + Namespace: namespace_no_deps, + Name: "obj-no-deps", + Annotations: map[string]string{"identity": identity4}, + } + ) + + setupForHandlerTests(true) + + depController, err := admiral.NewDependencyController(stop, &test.MockDependencyHandler{}, loader.FakeKubeconfigPath, "", time.Second*time.Duration(300), loader.GetFakeClientLoader()) + if err != nil { + t.Fatalf("failed to initialize dependency controller, err: %v", err) + } + r, err := admiral.NewRolloutsController(stop, &test.MockRolloutHandler{}, &config, time.Second*time.Duration(300), loader.GetFakeClientLoader()) + if err != nil { + t.Fatalf("failed to initialize rollout controller, err: %v", err) + } + d, err := admiral.NewDeploymentController(stop, &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300), loader.GetFakeClientLoader()) + if err != nil { + t.Fatalf("failed to initialize rollout controller, err: %v", err) + } + + rcCluster1 := &RemoteController{ + RolloutController: r, + DeploymentController: d, + } + + rcCluster2 := &RemoteController{ + RolloutController: r, + DeploymentController: d, + } + + ctx := context.Background() + + remoteRegistry := NewRemoteRegistry(context.TODO(), common.AdmiralParams{}) + remoteRegistry.DependencyController = depController + remoteRegistry.remoteControllers[cluster1] = rcCluster1 + remoteRegistry.remoteControllers[cluster2] = rcCluster2 + + remoteRegistry.AdmiralCache = &AdmiralCache{ + IdentityClusterCache: common.NewMapOfMaps(), + IdentityClusterNamespaceCache: common.NewMapOfMapOfMaps(), + PartitionIdentityCache: common.NewMap(), + } + + err = r.Added(ctx, &rollout) + assert.NoError(t, err) + rolloutFromCache := r.Cache.Get(identity1, "stage") + assert.NotNil(t, rolloutFromCache) + err = depController.Added(ctx, &dep) + assert.NoError(t, err) + depFromCache := depController.Cache.Get(identity1) + assert.NotNil(t, depFromCache) + + testCases := []struct { + name string + obj *common.K8sObject + rr *RemoteRegistry + clusterName string + expectedError error + }{ + { + name: "No global identifier on obj results in no errors", + obj: &common.K8sObject{Annotations: map[string]string{}, Labels: map[string]string{}}, + rr: remoteRegistry, + clusterName: cluster1, + expectedError: nil, + }, { + name: "Rollout with same identifier present in the namespace should " + + "return without processing and no errors", + obj: objRolloutNamespace, + rr: remoteRegistry, + clusterName: cluster1, + expectedError: nil, + }, { + name: "Valid client with no dependency record returns without processing", + obj: objValidWithNoDeps, + rr: remoteRegistry, + clusterName: cluster1, + expectedError: nil, + }, { + name: "Valid client with valid dependency record results in writing all dependencies endpoints to this cluster", + obj: objValid, + rr: remoteRegistry, + clusterName: cluster1, + expectedError: nil, + }, + } + + //Run the test for every provided case + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + result := HandleEventForClientDiscovery(ctx, admiral.Add, c.obj, remoteRegistry, c.clusterName) + if c.expectedError == nil && result != nil { + t.Fatalf("Expected error %v got %v", c.expectedError, result) + } + }) + } +} diff --git a/admiral/pkg/clusters/dependency_handler.go b/admiral/pkg/clusters/dependency_handler.go index 6338e70f..b28fcf1c 100644 --- a/admiral/pkg/clusters/dependency_handler.go +++ b/admiral/pkg/clusters/dependency_handler.go @@ -87,15 +87,17 @@ func isIdentityMeshEnabled(identity string, remoteRegistry *RemoteRegistry) bool return false } -func getDestinationsToBeProcessed( +func getDestinationsToBeProcessed(eventType admiral.EventType, updatedDependency *v1.Dependency, remoteRegistry *RemoteRegistry) ([]string, bool) { updatedDestinations := make([]string, 0) existingDestination := remoteRegistry.AdmiralCache.SourceToDestinations.Get(updatedDependency.Spec.Source) - var nonMeshEnabledExists bool lookup := make(map[string]bool) - for _, dest := range existingDestination { - lookup[dest] = true + //if this is an update, build a look up table to process only the diff + if eventType == admiral.Update { + for _, dest := range existingDestination { + lookup[dest] = true + } } for _, destination := range updatedDependency.Spec.Destinations { @@ -122,7 +124,7 @@ func (d *ProcessDestinationService) Process(ctx context.Context, dependency *v1. return nil } - destinations, hasNonMeshDestination := getDestinationsToBeProcessed(dependency, remoteRegistry) + destinations, hasNonMeshDestination := getDestinationsToBeProcessed(eventType, dependency, remoteRegistry) log.Infof(LogFormat, string(eventType), common.DependencyResourceType, dependency.Name, "", fmt.Sprintf("found %d new destinations: %v", len(destinations), destinations)) var processingErrors error diff --git a/admiral/pkg/clusters/dependency_handler_test.go b/admiral/pkg/clusters/dependency_handler_test.go index 4679115b..bb748c42 100644 --- a/admiral/pkg/clusters/dependency_handler_test.go +++ b/admiral/pkg/clusters/dependency_handler_test.go @@ -566,7 +566,7 @@ func TestProcessDestinationService(t *testing.T) { processDestinationService := &ProcessDestinationService{} - actualErr := processDestinationService.Process(context.TODO(), tc.dependency, tc.remoteRegistry, admiral.Add, tc.modifySEFunc) + actualErr := processDestinationService.Process(context.TODO(), tc.dependency, tc.remoteRegistry, admiral.Update, tc.modifySEFunc) if tc.expectedError != nil { assert.NotNil(t, actualErr) @@ -589,6 +589,7 @@ func TestGetDestinationDiff(t *testing.T) { identityClusterCacheWithAllMeshEnabled.Put("bar", "cluster1", "cluster1") testCases := []struct { name string + eventType admiral.EventType remoteRegistry *RemoteRegistry dependency *v1.Dependency expectedDestinations []string @@ -598,6 +599,7 @@ func TestGetDestinationDiff(t *testing.T) { name: "Given valid params " + "When the cache is empty" + "Then the func should return all the destinations as is", + eventType: admiral.Update, remoteRegistry: &RemoteRegistry{ AdmiralCache: &AdmiralCache{ SourceToDestinations: &sourceToDestinations{ @@ -620,6 +622,7 @@ func TestGetDestinationDiff(t *testing.T) { name: "Given valid params" + "When all the destinations are already in the cache" + "Then the func should return an empty list", + eventType: admiral.Update, remoteRegistry: &RemoteRegistry{ AdmiralCache: &AdmiralCache{ SourceToDestinations: &sourceToDestinations{ @@ -642,6 +645,7 @@ func TestGetDestinationDiff(t *testing.T) { name: "Given valid params" + "When there is an additional destination that is not in the cache" + "Then the func should return only the one that is missing in the cache", + eventType: admiral.Update, remoteRegistry: &RemoteRegistry{ AdmiralCache: &AdmiralCache{ SourceToDestinations: &sourceToDestinations{ @@ -664,6 +668,7 @@ func TestGetDestinationDiff(t *testing.T) { name: "Given valid params" + "When there is a NON mesh enabled service" + "Then the function should return new services, and true", + eventType: admiral.Update, remoteRegistry: &RemoteRegistry{ AdmiralCache: &AdmiralCache{ SourceToDestinations: &sourceToDestinations{ @@ -687,7 +692,7 @@ func TestGetDestinationDiff(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - actualDestinations, nonMeshEnabledExists := getDestinationsToBeProcessed(tc.dependency, tc.remoteRegistry) + actualDestinations, nonMeshEnabledExists := getDestinationsToBeProcessed(tc.eventType, tc.dependency, tc.remoteRegistry) assert.Equal(t, tc.expectedDestinations, actualDestinations) assert.Equal(t, tc.expectedIsNonMeshEnabled, nonMeshEnabledExists) }) diff --git a/admiral/pkg/clusters/handler.go b/admiral/pkg/clusters/handler.go index c756c2c4..281f8d6c 100644 --- a/admiral/pkg/clusters/handler.go +++ b/admiral/pkg/clusters/handler.go @@ -323,3 +323,33 @@ func GetServiceWithSuffixMatch(suffix string, services []*coreV1.Service) string } return "" } + +func UpdateIdentityClusterCache(remoteRegistry *RemoteRegistry, identity string, clusterId string) { + if remoteRegistry.AdmiralCache != nil && remoteRegistry.AdmiralCache.IdentityClusterCache != nil { + remoteRegistry.AdmiralCache.IdentityClusterCache.Put(identity, clusterId, clusterId) + } +} + +func DeploymentOrRolloutExistsInNamespace(remoteRegistry *RemoteRegistry, globalIdentifier string, clusterName string, namespace string) bool { + + if remoteRegistry.remoteControllers[clusterName] == nil { + log.Warnf(LogFormatAdv, "Find", "deployment", "", namespace, clusterName, "Remote controller not initialized when trying to find "+globalIdentifier) + return false + } + + deployments := remoteRegistry.remoteControllers[clusterName].DeploymentController.Cache.GetByIdentity(globalIdentifier) + for _, deployment := range deployments { + if deployment.Deployment.Namespace == namespace { + return true + } + } + + rollouts := remoteRegistry.remoteControllers[clusterName].RolloutController.Cache.GetByIdentity(globalIdentifier) + for _, rollout := range rollouts { + if rollout.Rollout.Namespace == namespace { + return true + } + } + + return false +} diff --git a/admiral/pkg/clusters/handler_test.go b/admiral/pkg/clusters/handler_test.go index 475956f0..ab817f8e 100644 --- a/admiral/pkg/clusters/handler_test.go +++ b/admiral/pkg/clusters/handler_test.go @@ -24,7 +24,15 @@ import ( func admiralParamsForHandlerTests(argoEnabled bool) common.AdmiralParams { return common.AdmiralParams{ ArgoRolloutsEnabled: argoEnabled, - LabelSet: &common.LabelSet{}, + LabelSet: &common.LabelSet{ + WorkloadIdentityKey: "identity", + EnvKey: "admiral.io/env", + AdmiralCRDIdentityLabel: "identity", + DeploymentAnnotation: "sidecar.istio.io/inject", + AdmiralIgnoreLabel: "admiral-ignore", + }, + EnableSWAwareNSCaches: true, + EnableDependencyProcessing: true, } } @@ -927,25 +935,3 @@ func TestGetServiceForRolloutBlueGreen(t *testing.T) { }) } } - -func makeRemoteRegistry( - clusterNames []string, remoteController *RemoteController, cname string, dependentClusters []string) *RemoteRegistry { - var ( - cache = common.NewMapOfMaps() - rr = NewRemoteRegistry(context.TODO(), common.AdmiralParams{}) - ) - rr.AdmiralCache = &AdmiralCache{ - CnameDependentClusterCache: cache, - } - for _, dependentCluster := range dependentClusters { - rr.AdmiralCache.CnameDependentClusterCache.Put(cname, dependentCluster, dependentCluster) - } - for _, clusterName := range clusterNames { - rr.PutRemoteController( - clusterName, - remoteController, - ) - } - - return rr -} diff --git a/admiral/pkg/clusters/registry.go b/admiral/pkg/clusters/registry.go index ed0760db..ab460bd6 100644 --- a/admiral/pkg/clusters/registry.go +++ b/admiral/pkg/clusters/registry.go @@ -52,7 +52,7 @@ func InitAdmiral(ctx context.Context, params common.AdmiralParams) (*RemoteRegis if err != nil { return nil, fmt.Errorf("error with dependency controller init: %v", err) } - + rr.DependencyController = wd.DepController if !params.ArgoRolloutsEnabled { logrus.Info("argo rollouts disabled") } @@ -215,6 +215,36 @@ func (r *RemoteRegistry) createCacheController(clientConfig *rest.Config, cluste return fmt.Errorf("error with RolloutController initialization, err: %v", err) } } + + if common.IsClientDiscoveryEnabled() { + + clustersForJobs := common.GetClientDiscoveryClustersForJobs() + + if len(clustersForJobs) == 0 || common.IsPresent(clustersForJobs, clusterID) { + logrus.Infof("starting JobController clusterID: %v", clusterID) + rc.JobController, err = admiral.NewJobController(stop, &ClientDiscoveryHandler{RemoteRegistry: r, ClusterID: clusterID}, clientConfig, 0, r.ClientLoader) + if err != nil { + return fmt.Errorf("error with JobController initialization, err: %v", err) + } + } + + clustersForNumaflow := common.GetClientDiscoveryClustersForNumaflow() + + if len(clustersForNumaflow) == 0 || common.IsPresent(clustersForNumaflow, clusterID) { + logrus.Infof("starting VertexController clusterID: %v", clusterID) + rc.VertexController, err = admiral.NewVertexController(stop, &ClientDiscoveryHandler{RemoteRegistry: r, ClusterID: clusterID}, clientConfig, 0, r.ClientLoader) + if err != nil { + return fmt.Errorf("error with VertexController initialization, err: %v", err) + } + + logrus.Infof("starting MonoVertexController clusterID: %v", clusterID) + rc.MonoVertexController, err = admiral.NewMonoVertexController(stop, &ClientDiscoveryHandler{RemoteRegistry: r, ClusterID: clusterID}, clientConfig, 0, r.ClientLoader) + if err != nil { + return fmt.Errorf("error with MonoVertexController initialization, err: %v", err) + } + } + } + } logrus.Infof("starting ServiceEntryController for clusterID: %v", clusterID) rc.ServiceEntryController, err = istio.NewServiceEntryController(stop, &ServiceEntryHandler{RemoteRegistry: r, ClusterID: clusterID}, clusterID, clientConfig, resyncPeriod.SeAndDrReconcileInterval, r.ClientLoader) @@ -244,6 +274,11 @@ func (r *RemoteRegistry) createCacheController(clientConfig *rest.Config, cluste return nil } +func isInClusterList(enabledClusters []string, clusterName string) bool { + + return false +} + func (r *RemoteRegistry) updateCacheController(clientConfig *rest.Config, clusterID string, resyncPeriod util.ResyncIntervals) error { //We want to refresh the cache controllers. But the current approach is parking the goroutines used in the previous set of controllers, leading to a rather large memory leak. //This is a temporary fix to only do the controller refresh if the API Server of the remote cluster has changed diff --git a/admiral/pkg/clusters/registry_test.go b/admiral/pkg/clusters/registry_test.go index 6431ec00..b4be7cb9 100644 --- a/admiral/pkg/clusters/registry_test.go +++ b/admiral/pkg/clusters/registry_test.go @@ -50,6 +50,7 @@ func admiralParamsForRegistryTests() common.AdmiralParams { EnableRoutingPolicy: true, EnvoyFilterVersion: "1.13", Profile: common.AdmiralProfileDefault, + EnableClientDiscovery: true, } } diff --git a/admiral/pkg/clusters/serviceentry.go b/admiral/pkg/clusters/serviceentry.go index 9bde36b9..5ee3525b 100644 --- a/admiral/pkg/clusters/serviceentry.go +++ b/admiral/pkg/clusters/serviceentry.go @@ -247,7 +247,8 @@ func modifyServiceEntryForNewServiceOrPod( clusterDeployRolloutPresent[rc.ClusterID] = make(map[string]bool) } - remoteRegistry.AdmiralCache.IdentityClusterCache.Put(partitionedIdentity, rc.ClusterID, rc.ClusterID) + UpdateIdentityClusterCache(remoteRegistry, partitionedIdentity, rc.ClusterID) + util.LogElapsedTimeSinceTask(ctxLogger, "AdmiralCacheIdentityClusterCachePut", deploymentOrRolloutName, deploymentOrRolloutNS, rc.ClusterID, "", start) diff --git a/admiral/pkg/clusters/types.go b/admiral/pkg/clusters/types.go index 135cf82a..81cafd8d 100644 --- a/admiral/pkg/clusters/types.go +++ b/admiral/pkg/clusters/types.go @@ -48,6 +48,9 @@ type RemoteController struct { EnvoyFilterController *admiral.EnvoyFilterController OutlierDetectionController *admiral.OutlierDetectionController ClientConnectionConfigController *admiral.ClientConnectionConfigController + JobController *admiral.JobController + VertexController *admiral.VertexController + MonoVertexController *admiral.MonoVertexController stop chan struct{} //listener for normal types } diff --git a/admiral/pkg/controller/admiral/controller.go b/admiral/pkg/controller/admiral/controller.go index 81b24dad..0f8b9b24 100644 --- a/admiral/pkg/controller/admiral/controller.go +++ b/admiral/pkg/controller/admiral/controller.go @@ -81,6 +81,10 @@ type Controller struct { informer cache.SharedIndexInformer } +type ClientDiscoveryHandler interface { + Added(ctx context.Context, obj *common.K8sObject) error +} + func NewController(name, clusterEndpoint string, stopCh <-chan struct{}, delegator Delegator, informer cache.SharedIndexInformer) Controller { controller := Controller{ name: name, diff --git a/admiral/pkg/controller/admiral/dependency.go b/admiral/pkg/controller/admiral/dependency.go index c5ec0d6b..f0c89875 100644 --- a/admiral/pkg/controller/admiral/dependency.go +++ b/admiral/pkg/controller/admiral/dependency.go @@ -66,7 +66,7 @@ func (d *depCache) Put(dep *v1.Dependency) { } func (d *depCache) getKey(dep *v1.Dependency) string { - return dep.Name + return dep.Spec.Source } func (d *depCache) Get(identity string) *v1.Dependency { diff --git a/admiral/pkg/controller/admiral/dependency_test.go b/admiral/pkg/controller/admiral/dependency_test.go index 9c2d1adc..89b2e9cb 100644 --- a/admiral/pkg/controller/admiral/dependency_test.go +++ b/admiral/pkg/controller/admiral/dependency_test.go @@ -236,7 +236,7 @@ func TestDependencyAddUpdateAndDelete(t *testing.T) { depObj := makeK8sDependencyObj(depName, "namespace1", dep) dependencyController.Added(ctx, depObj) - newDepObj := dependencyController.Cache.Get(depName) + newDepObj := dependencyController.Cache.Get(depObj.Spec.Source) if !cmp.Equal(depObj.Spec, newDepObj.Spec) { t.Errorf("dep update failed, expected: %v got %v", depObj, newDepObj) @@ -247,7 +247,7 @@ func TestDependencyAddUpdateAndDelete(t *testing.T) { updatedObj := makeK8sDependencyObj(depName, "namespace1", updatedDep) dependencyController.Updated(ctx, makeK8sDependencyObj(depName, "namespace1", updatedDep), depObj) - updatedDepObj := dependencyController.Cache.Get(depName) + updatedDepObj := dependencyController.Cache.Get(updatedObj.Spec.Source) if !cmp.Equal(updatedObj.Spec, updatedDepObj.Spec) { t.Errorf("dep update failed, expected: %v got %v", updatedObj, updatedDepObj) @@ -256,7 +256,7 @@ func TestDependencyAddUpdateAndDelete(t *testing.T) { //test delete dependencyController.Deleted(ctx, updatedDepObj) - deletedDepObj := dependencyController.Cache.Get(depName) + deletedDepObj := dependencyController.Cache.Get(updatedDepObj.Spec.Source) if deletedDepObj != nil { t.Errorf("dep delete failed") @@ -272,12 +272,18 @@ func TestDependencyGetProcessItemStatus(t *testing.T) { Name: "dep-in-cache", Namespace: "ns-1", }, + Spec: model.Dependency{ + Source: "identity1", + }, } dependencyNotInCache = &v1.Dependency{ ObjectMeta: v12.ObjectMeta{ Name: "dep-not-in-cache", Namespace: "ns-2", }, + Spec: model.Dependency{ + Source: "identity2", + }, } ) @@ -342,12 +348,18 @@ func TestDependencyUpdateProcessItemStatus(t *testing.T) { Name: "dep-in-cache", Namespace: "ns-1", }, + Spec: model.Dependency{ + Source: "identity1", + }, } dependencyNotInCache = &v1.Dependency{ ObjectMeta: v12.ObjectMeta{ Name: "dep-not-in-cache", Namespace: "ns-2", }, + Spec: model.Dependency{ + Source: "identity2", + }, } ) diff --git a/admiral/pkg/controller/admiral/job.go b/admiral/pkg/controller/admiral/job.go new file mode 100644 index 00000000..7f3e907a --- /dev/null +++ b/admiral/pkg/controller/admiral/job.go @@ -0,0 +1,258 @@ +package admiral + +import ( + "context" + "fmt" + v12 "k8s.io/api/batch/v1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/client-go/informers/batch/v1" + "sync" + "time" + + "github.com/istio-ecosystem/admiral/admiral/pkg/client/loader" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + log "github.com/sirupsen/logrus" + "k8s.io/client-go/rest" + + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" +) + +//Job controller discovers jobs as mesh clients (its assumed that k8s Job doesn't have any ingress communication) + +type JobController struct { + K8sClient kubernetes.Interface + JobHandler ClientDiscoveryHandler + informer cache.SharedIndexInformer + Cache *jobCache +} + +type JobEntry struct { + Identity string + Jobs map[string]*common.K8sObject +} + +type jobCache struct { + //map of dependencies key=identity value array of onboarded identities + cache map[string]*JobEntry + mutex *sync.Mutex +} + +func NewJobCache() *jobCache { + return &jobCache{ + cache: make(map[string]*JobEntry), + mutex: &sync.Mutex{}, + } +} + +func getK8sObjectFromJob(job *v12.Job) *common.K8sObject { + return &common.K8sObject{ + Name: job.Name, + Namespace: job.Namespace, + Annotations: job.Spec.Template.Annotations, + Labels: job.Spec.Template.Labels, + Status: common.NotProcessed, + Type: common.Job, + } +} + +func (p *jobCache) Put(job *common.K8sObject) (*common.K8sObject, bool) { + defer p.mutex.Unlock() + p.mutex.Lock() + identity := common.GetGlobalIdentifier(job.Annotations, job.Labels) + existingJobs := p.cache[identity] + if existingJobs == nil { + existingJobs = &JobEntry{ + Identity: identity, + Jobs: map[string]*common.K8sObject{job.Namespace: job}, + } + p.cache[identity] = existingJobs + return job, true + } else { + jobInCache := existingJobs.Jobs[job.Namespace] + if jobInCache == nil { + existingJobs.Jobs[job.Namespace] = job + p.cache[identity] = existingJobs + return job, true + } + } + return nil, false +} + +func (p *jobCache) Get(key string, namespace string) *common.K8sObject { + defer p.mutex.Unlock() + p.mutex.Lock() + + jce, ok := p.cache[key] + if ok { + j, ok := jce.Jobs[namespace] + if ok { + return j + } + } + return nil +} + +func (p *jobCache) GetJobProcessStatus(job *v12.Job) (string, error) { + defer p.mutex.Unlock() + p.mutex.Lock() + jobObj := getK8sObjectFromJob(job) + identity := common.GetGlobalIdentifier(jobObj.Annotations, jobObj.Labels) + + jce, ok := p.cache[identity] + if ok { + jobFromNamespace, ok := jce.Jobs[job.Namespace] + if ok { + return jobFromNamespace.Status, nil + } + } + + return common.NotProcessed, nil +} + +func (p *jobCache) UpdateJobProcessStatus(job *v12.Job, status string) error { + defer p.mutex.Unlock() + p.mutex.Lock() + jobObj := getK8sObjectFromJob(job) + identity := common.GetGlobalIdentifier(jobObj.Annotations, jobObj.Labels) + + jce, ok := p.cache[identity] + if ok { + jobFromNamespace, ok := jce.Jobs[job.Namespace] + if ok { + jobFromNamespace.Status = status + p.cache[jce.Identity] = jce + return nil + } else { + newJob := getK8sObjectFromJob(job) + newJob.Status = status + jce.Jobs[job.Namespace] = newJob + p.cache[jce.Identity] = jce + return nil + } + } + + return fmt.Errorf(LogCacheFormat, "UpdateStatus", "Job", + job.Name, job.Namespace, "", "nothing to update, job not found in cache") +} + +func (p *JobController) DoesGenerationMatch(ctxLogger *log.Entry, obj interface{}, oldObj interface{}) (bool, error) { + if !common.DoGenerationCheck() { + ctxLogger.Debugf(ControllerLogFormat, "DoesGenerationMatch", "", + fmt.Sprintf("generation check is disabled")) + return false, nil + } + jobNew, ok := obj.(*v12.Job) + if !ok { + return false, fmt.Errorf("type assertion failed, %v is not of type *Job", obj) + } + jobOld, ok := oldObj.(*v12.Job) + if !ok { + return false, fmt.Errorf("type assertion failed, %v is not of type *Job", oldObj) + } + if jobNew.Generation == jobOld.Generation { + ctxLogger.Infof(ControllerLogFormat, "DoesGenerationMatch", "", + fmt.Sprintf("old and new generation matched for job %s", jobNew.Name)) + return true, nil + } + return false, nil +} + +func NewJobController(stopCh <-chan struct{}, handler ClientDiscoveryHandler, config *rest.Config, resyncPeriod time.Duration, clientLoader loader.ClientLoader) (*JobController, error) { + + jobController := JobController{} + jobController.JobHandler = handler + + var err error + + jobController.K8sClient, err = clientLoader.LoadKubeClientFromConfig(config) + if err != nil { + return nil, fmt.Errorf("failed to create dependency controller k8s client: %v", err) + } + + jobController.informer = v1.NewJobInformer( + jobController.K8sClient, + meta_v1.NamespaceAll, + resyncPeriod, + cache.Indexers{}, + ) + + jobController.Cache = NewJobCache() + + NewController("job-ctrl", config.Host, stopCh, &jobController, jobController.informer) + + return &jobController, nil +} + +func (d *JobController) Added(ctx context.Context, obj interface{}) error { + return addUpdateJob(d, ctx, obj) +} + +func (d *JobController) Updated(ctx context.Context, obj interface{}, oldObj interface{}) error { + //Not Required, this is a no-op as as Add event already handles registering this as a mesh client + return nil +} + +func addUpdateJob(j *JobController, ctx context.Context, obj interface{}) error { + job, ok := obj.(*v12.Job) + if !ok { + return fmt.Errorf("failed to covert informer object to Job") + } + if !common.ShouldIgnore(job.Spec.Template.Annotations, job.Spec.Template.Labels) { + k8sObj := getK8sObjectFromJob(job) + newK8sObj, isNew := j.Cache.Put(k8sObj) + if isNew { + newK8sObj.Status = common.ProcessingInProgress + j.JobHandler.Added(ctx, newK8sObj) + } else { + log.Infof("Ignoring job %v as it was already processed", job.Name) + } + } + return nil +} + +func (p *JobController) Deleted(ctx context.Context, obj interface{}) error { + //Not Required (to be handled via asset off boarding) + return nil +} + +func (d *JobController) GetProcessItemStatus(obj interface{}) (string, error) { + job, ok := obj.(*v12.Job) + if !ok { + return common.NotProcessed, fmt.Errorf("type assertion failed, %v is not of type *common.K8sObject", obj) + } + return d.Cache.GetJobProcessStatus(job) +} + +func (d *JobController) UpdateProcessItemStatus(obj interface{}, status string) error { + job, ok := obj.(*v12.Job) + if !ok { + return fmt.Errorf("type assertion failed, %v is not of type *Job", obj) + } + return d.Cache.UpdateJobProcessStatus(job, status) +} + +func (d *JobController) LogValueOfAdmiralIoIgnore(obj interface{}) { + job, ok := obj.(*v12.Job) + if !ok { + return + } + jobObj := getK8sObjectFromJob(job) + if jobObj.Annotations[common.AdmiralIgnoreAnnotation] == "true" { + log.Infof("op=%s type=%v name=%v namespace=%s cluster=%s message=%s", "admiralIoIgnoreAnnotationCheck", common.MonoVertex, + job.Name, job.Namespace, "", "Value=true") + } +} + +func (j *JobController) Get(ctx context.Context, isRetry bool, obj interface{}) (interface{}, error) { + job, ok := obj.(*v12.Job) + if ok && isRetry { + jobObj := getK8sObjectFromJob(job) + identity := common.GetGlobalIdentifier(jobObj.Annotations, jobObj.Labels) + return j.Cache.Get(identity, job.Namespace), nil + } + if ok && j.K8sClient != nil { + return j.K8sClient.BatchV1().Jobs(job.Namespace).Get(ctx, job.Name, meta_v1.GetOptions{}) + } + return nil, fmt.Errorf("kubernetes client is not initialized, txId=%s", ctx.Value("txId")) +} diff --git a/admiral/pkg/controller/admiral/job_test.go b/admiral/pkg/controller/admiral/job_test.go new file mode 100644 index 00000000..00797f7f --- /dev/null +++ b/admiral/pkg/controller/admiral/job_test.go @@ -0,0 +1,509 @@ +package admiral + +import ( + "context" + "fmt" + "github.com/istio-ecosystem/admiral/admiral/pkg/client/loader" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + "github.com/istio-ecosystem/admiral/admiral/pkg/test" + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + v2 "k8s.io/api/batch/v1" + coreV1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/clientcmd" + "reflect" + "sync" + "testing" +) + +func getJob(namespace string, annotations map[string]string, labels map[string]string) *v2.Job { + job := &v2.Job{} + job.Namespace = namespace + spec := v2.JobSpec{ + Template: coreV1.PodTemplateSpec{ + ObjectMeta: v1.ObjectMeta{ + Annotations: annotations, + Labels: labels, + }, + }, + } + job.Spec = spec + return job +} + +func TestJobController_Added(t *testing.T) { + common.ResetSync() + admiralParams := common.AdmiralParams{ + LabelSet: &common.LabelSet{ + WorkloadIdentityKey: "identity", + EnvKey: "admiral.io/env", + AdmiralCRDIdentityLabel: "identity", + DeploymentAnnotation: "sidecar.istio.io/inject", + AdmiralIgnoreLabel: "admiral-ignore", + }, + } + common.InitializeConfig(admiralParams) + ctx := context.Background() + ctx = context.WithValue(ctx, "clusterId", "test-cluster-k8s") + //Jobs with the correct label are added to the cache + mdh := test.MockClientDiscoveryHandler{} + cache := jobCache{ + cache: map[string]*JobEntry{}, + mutex: &sync.Mutex{}, + } + jobController := JobController{ + JobHandler: &mdh, + Cache: &cache, + } + job := getJob("job-ns", map[string]string{"sidecar.istio.io/inject": "true", "admiral.io/env": "dev"}, map[string]string{"identity": "job", "istio-injected": "true"}) + expectedJob := getK8sObjectFromJob(job) + expectedJob.Status = common.ProcessingInProgress + jobWithBadLabels := getJob("jobWithBadLabels-ns", map[string]string{"admiral.io/env": "dev"}, map[string]string{"identity": "jobWithBadLabels", "random-label": "true"}) + jobWithIgnoreLabels := getJob("jobWithIgnoreLabels-ns", map[string]string{"sidecar.istio.io/inject": "true", "admiral.io/env": "dev"}, map[string]string{"identity": "jobWithIgnoreLabels", "istio-injected": "true", "admiral-ignore": "true"}) + jobWithIgnoreAnnotations := getJob("jobWithIgnoreAnnotations-ns", map[string]string{"admiral.io/ignore": "true"}, map[string]string{"identity": "jobWithIgnoreAnnotations"}) + jobWithIgnoreAnnotations.Annotations = map[string]string{"admiral.io/ignore": "true"} + + testCases := []struct { + name string + job *v2.Job + expectedJob *common.K8sObject + id string + expectedCacheContains bool + }{ + { + name: "Expects job to be added to the cache when the correct label is present", + job: job, + expectedJob: expectedJob, + id: "job", + expectedCacheContains: true, + }, + { + name: "Expects job to not be added to the cache when the correct label is not present", + job: jobWithBadLabels, + expectedJob: nil, + id: "jobWithBadLabels", + expectedCacheContains: false, + }, + { + name: "Expects ignored job identified by label to not be added to the cache", + job: jobWithIgnoreLabels, + expectedJob: nil, + id: "jobWithIgnoreLabels", + expectedCacheContains: false, + }, + { + name: "Expects ignored job identified by job annotation to not be added to the cache", + job: jobWithIgnoreAnnotations, + expectedJob: nil, + id: "jobWithIgnoreAnnotations", + expectedCacheContains: false, + }, + } + ns := coreV1.Namespace{} + ns.Name = "test-ns" + ns.Annotations = map[string]string{"admiral.io/ignore": "true"} + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + if c.name == "Expects ignored job identified by label to be removed from the cache" { + job.Spec.Template.Labels["admiral-ignore"] = "true" + } + jobController.Added(ctx, c.job) + jobClusterEntry := jobController.Cache.cache[c.id] + var jobsMap map[string]*common.K8sObject = nil + if jobClusterEntry != nil { + jobsMap = jobClusterEntry.Jobs + } + var jobObj *common.K8sObject = nil + if jobsMap != nil && len(jobsMap) > 0 { + jobObj = jobsMap[c.job.Namespace] + } + if !reflect.DeepEqual(c.expectedJob, jobObj) { + t.Errorf("Expected rollout %+v but got %+v", c.expectedJob, jobObj) + } + }) + } +} + +func TestJobControlle_DoesGenerationMatch(t *testing.T) { + dc := JobController{} + + admiralParams := common.AdmiralParams{} + + testCases := []struct { + name string + jobNew interface{} + jobOld interface{} + enableGenerationCheck bool + expectedValue bool + expectedError error + }{ + { + name: "Given context, new job and old job object " + + "When new job is not of type *v1.Job " + + "Then func should return an error", + jobNew: struct{}{}, + jobOld: struct{}{}, + enableGenerationCheck: true, + expectedError: fmt.Errorf("type assertion failed, {} is not of type *Job"), + }, + { + name: "Given context, new job and old job object " + + "When old job is not of type *v1.Job " + + "Then func should return an error", + jobNew: &v2.Job{}, + jobOld: struct{}{}, + enableGenerationCheck: true, + expectedError: fmt.Errorf("type assertion failed, {} is not of type *Job"), + }, + { + name: "Given context, new job and old job object " + + "When job generation check is enabled but the generation does not match " + + "Then func should return false ", + jobNew: &v2.Job{ + ObjectMeta: v1.ObjectMeta{ + Generation: 2, + }, + }, + jobOld: &v2.Job{ + ObjectMeta: v1.ObjectMeta{ + Generation: 1, + }, + }, + enableGenerationCheck: true, + expectedError: nil, + }, + { + name: "Given context, new job and old job object " + + "When job generation check is disabled " + + "Then func should return false ", + jobNew: &v2.Job{ + ObjectMeta: v1.ObjectMeta{ + Generation: 2, + }, + }, + jobOld: &v2.Job{ + ObjectMeta: v1.ObjectMeta{ + Generation: 1, + }, + }, + expectedError: nil, + }, + { + name: "Given context, new job and old job object " + + "When job generation check is enabled and the old and new job generation is equal " + + "Then func should just return true", + jobNew: &v2.Job{ + ObjectMeta: v1.ObjectMeta{ + Generation: 2, + }, + }, + jobOld: &v2.Job{ + ObjectMeta: v1.ObjectMeta{ + Generation: 2, + }, + }, + enableGenerationCheck: true, + expectedError: nil, + expectedValue: true, + }, + } + + ctxLogger := log.WithFields(log.Fields{ + "txId": "abc", + }) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + admiralParams.EnableGenerationCheck = tc.enableGenerationCheck + common.ResetSync() + common.InitializeConfig(admiralParams) + actual, err := dc.DoesGenerationMatch(ctxLogger, tc.jobNew, tc.jobOld) + if !ErrorEqualOrSimilar(err, tc.expectedError) { + t.Errorf("expected: %v, got: %v", tc.expectedError, err) + } + if err == nil { + if tc.expectedValue != actual { + t.Errorf("expected: %v, got: %v", tc.expectedValue, actual) + } + } + }) + } + +} + +func TestNewJobController(t *testing.T) { + config, err := clientcmd.BuildConfigFromFlags("", "../../test/resources/admins@fake-cluster.k8s.local") + if err != nil { + t.Errorf("%v", err) + } + stop := make(chan struct{}) + jobHandler := test.MockClientDiscoveryHandler{} + + jobCon, _ := NewJobController(stop, &jobHandler, config, 0, loader.GetFakeClientLoader()) + + if jobCon == nil { + t.Errorf("Job controller should not be nil") + } +} + +func TestJobUpdateProcessItemStatus(t *testing.T) { + common.ResetSync() + admiralParams := common.AdmiralParams{ + LabelSet: &common.LabelSet{ + WorkloadIdentityKey: "identity", + EnvKey: "admiral.io/env", + AdmiralCRDIdentityLabel: "identity", + }, + } + common.InitializeConfig(admiralParams) + jobInCache := getJob("namespace-1", map[string]string{}, map[string]string{"identity": "job1", "env": "prd"}) + jobInCache.Name = "job1" + jobInCache2 := getJob("namespace-2", map[string]string{}, map[string]string{"identity": "job2", "env": "prd"}) + jobInCache2.Name = "job2" + jobNotInCache := getJob("namespace-3", map[string]string{}, map[string]string{"identity": "job3", "env": "prd"}) + jobNotInCache.Name = "job3" + var ( + serviceAccount = &coreV1.ServiceAccount{} + ) + + // Populating the job Cache + jobCache := &jobCache{ + cache: make(map[string]*JobEntry), + mutex: &sync.Mutex{}, + } + + jobController := &JobController{ + Cache: jobCache, + } + + jobCache.Put(getK8sObjectFromJob(jobInCache)) + jobController.UpdateProcessItemStatus(jobInCache, common.Processed) + jobCache.Put(getK8sObjectFromJob(jobInCache2)) + + testCases := []struct { + name string + obj interface{} + statusToSet string + expectedErr error + expectedStatus string + }{ + { + name: "Given job cache has a valid job in its cache, " + + "And is processed" + + "Then, the status for the valid job should be updated to processed", + obj: jobInCache, + statusToSet: common.Processed, + expectedErr: nil, + expectedStatus: common.Processed, + }, + { + name: "Given job cache has a valid job in its cache, " + + "And is processed" + + "Then, the status for the valid job should be updated to not processed", + obj: jobInCache2, + statusToSet: common.NotProcessed, + expectedErr: nil, + expectedStatus: common.NotProcessed, + }, + { + name: "Given job cache does not has a valid job in its cache, " + + "Then, the status for the valid job should be not processed, " + + "And an error should be returned with the job not found message", + obj: jobNotInCache, + statusToSet: common.NotProcessed, + expectedErr: fmt.Errorf(LogCacheFormat, "UpdateStatus", "Job", jobNotInCache.Name, jobNotInCache.Namespace, "", "nothing to update, job not found in cache"), + expectedStatus: common.NotProcessed, + }, + { + name: "Given ServiceAccount is passed to the function, " + + "Then, the function should not panic, " + + "And return an error", + obj: serviceAccount, + expectedErr: fmt.Errorf("type assertion failed"), + expectedStatus: common.NotProcessed, + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + err := jobController.UpdateProcessItemStatus(c.obj, c.statusToSet) + if !ErrorEqualOrSimilar(err, c.expectedErr) { + t.Errorf("expected: %v, got: %v", c.expectedErr, err) + } + status, _ := jobController.GetProcessItemStatus(c.obj) + assert.Equal(t, c.expectedStatus, status) + }) + } +} + +func TestJobGetProcessItemStatus(t *testing.T) { + common.ResetSync() + admiralParams := common.AdmiralParams{ + LabelSet: &common.LabelSet{ + WorkloadIdentityKey: "identity", + EnvKey: "admiral.io/env", + AdmiralCRDIdentityLabel: "identity", + }, + } + common.InitializeConfig(admiralParams) + var ( + serviceAccount = &coreV1.ServiceAccount{} + ) + jobInCache := getJob("namespace-1", map[string]string{}, map[string]string{"identity": "job1"}) + jobInCache.Name = "debug-1" + jobNotInCache := getJob("namespace-2", map[string]string{}, map[string]string{"identity": "job2"}) + jobNotInCache.Name = "debug-2" + + // Populating the job Cache + jobCache := &jobCache{ + cache: make(map[string]*JobEntry), + mutex: &sync.Mutex{}, + } + + jobController := &JobController{ + Cache: jobCache, + } + + jobCache.Put(getK8sObjectFromJob(jobInCache)) + jobCache.UpdateJobProcessStatus(jobInCache, common.Processed) + + testCases := []struct { + name string + obj interface{} + expectedErr error + expectedResult string + }{ + { + name: "Given job cache has a valid job in its cache, " + + "And is processed" + + "Then, we should be able to get the status as processed", + obj: jobInCache, + expectedResult: common.Processed, + }, + { + name: "Given job cache does not has a valid job in its cache, " + + "Then, the status for the valid job should not be updated", + obj: jobNotInCache, + expectedResult: common.NotProcessed, + }, + { + name: "Given ServiceAccount is passed to the function, " + + "Then, the function should not panic, " + + "And return an error", + obj: serviceAccount, + expectedErr: fmt.Errorf("type assertion failed"), + expectedResult: common.NotProcessed, + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + res, err := jobController.GetProcessItemStatus(c.obj) + if !ErrorEqualOrSimilar(err, c.expectedErr) { + t.Errorf("expected: %v, got: %v", c.expectedErr, err) + } + assert.Equal(t, c.expectedResult, res) + }) + } +} + +func TestJobLogValueOfAdmiralIoIgnore(t *testing.T) { + // Test case 1: obj is not a Job object + d := &JobController{} + d.LogValueOfAdmiralIoIgnore("not a job") + // No error should occur + + // Test case 2: K8sClient is nil + d = &JobController{} + d.LogValueOfAdmiralIoIgnore(&v2.Job{}) + // No error should occur + + d.LogValueOfAdmiralIoIgnore(&v2.Job{ObjectMeta: metav1.ObjectMeta{Namespace: "test-ns"}}) + // No error should occur + + // Test case 3: AdmiralIgnoreAnnotation is set in Job object + d = &JobController{} + job := &v2.Job{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test-ns", + Annotations: map[string]string{ + common.AdmiralIgnoreAnnotation: "true", + }, + }, + } + d.LogValueOfAdmiralIoIgnore(job) + // No error should occur +} + +func TestJobController_CacheGet(t *testing.T) { + common.ResetSync() + admiralParams := common.AdmiralParams{ + LabelSet: &common.LabelSet{ + WorkloadIdentityKey: "identity", + EnvKey: "admiral.io/env", + AdmiralCRDIdentityLabel: "identity", + DeploymentAnnotation: "sidecar.istio.io/inject", + AdmiralIgnoreLabel: "admiral-ignore", + }, + } + common.InitializeConfig(admiralParams) + ctx := context.Background() + ctx = context.WithValue(ctx, "clusterId", "test-cluster-k8s") + + cache := jobCache{ + cache: map[string]*JobEntry{}, + mutex: &sync.Mutex{}, + } + + job := getJob("job-ns", map[string]string{"sidecar.istio.io/inject": "true", "admiral.io/env": "dev"}, map[string]string{"identity": "job1", "istio-injected": "true"}) + cache.Put(getK8sObjectFromJob(job)) + jobSameIdentityInDiffNamespace := getJob("job-ns-2", map[string]string{"sidecar.istio.io/inject": "true", "admiral.io/env": "dev"}, map[string]string{"identity": "job1", "istio-injected": "true"}) + cache.Put(getK8sObjectFromJob(jobSameIdentityInDiffNamespace)) + job2 := getJob("job-ns-3", map[string]string{"sidecar.istio.io/inject": "true", "admiral.io/env": "dev"}, map[string]string{"identity": "job2", "istio-injected": "true"}) + cache.Put(getK8sObjectFromJob(job2)) + + testCases := []struct { + name string + expectedVertex *common.K8sObject + identity string + namespace string + }{ + { + name: "Expects job to be in the cache when right identity and namespace are passed", + expectedVertex: getK8sObjectFromJob(job), + identity: "job1", + namespace: "job-ns", + }, + { + name: "Expects job to be in the cache when same identity and diff namespace are passed", + expectedVertex: getK8sObjectFromJob(jobSameIdentityInDiffNamespace), + identity: "job1", + namespace: "job-ns-2", + }, + { + name: "Expects job to be in the cache when diff identity and diff namespace are passed", + expectedVertex: getK8sObjectFromJob(job2), + identity: "job2", + namespace: "job-ns-3", + }, + { + name: "Expects nil job in random namespace", + expectedVertex: nil, + identity: "job2", + namespace: "random", + }, + } + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + if c.name == "Expects ignored job identified by label to be removed from the cache" { + job.Spec.Template.Labels["admiral-ignore"] = "true" + } + jobObj := cache.Get(c.identity, c.namespace) + if !reflect.DeepEqual(c.expectedVertex, jobObj) { + t.Errorf("Expected rollout %+v but got %+v", c.expectedVertex, jobObj) + } + }) + } +} diff --git a/admiral/pkg/controller/admiral/monovertex.go b/admiral/pkg/controller/admiral/monovertex.go new file mode 100644 index 00000000..6db23c19 --- /dev/null +++ b/admiral/pkg/controller/admiral/monovertex.go @@ -0,0 +1,266 @@ +package admiral + +import ( + "context" + "fmt" + "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1" + numaflow "github.com/numaproj/numaflow/pkg/client/clientset/versioned" + v1alpha12 "github.com/numaproj/numaflow/pkg/client/informers/externalversions/numaflow/v1alpha1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sync" + "time" + + "github.com/istio-ecosystem/admiral/admiral/pkg/client/loader" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + log "github.com/sirupsen/logrus" + "k8s.io/client-go/rest" + + "k8s.io/client-go/tools/cache" +) + +//MonoVertex controller discovers monoVertexs as mesh clients (its assumed that k8s MonoVertex doesn't have any ingress communication) + +type MonoVertexController struct { + NumaflowClient numaflow.Interface + MonoVertexHandler ClientDiscoveryHandler + informer cache.SharedIndexInformer + Cache *monoVertexCache +} + +type MonoVertexEntry struct { + Identity string + MonoVertices map[string]*common.K8sObject +} + +type monoVertexCache struct { + //map of dependencies key=identity value array of onboarded identities + cache map[string]*MonoVertexEntry + mutex *sync.Mutex +} + +func newMonoVertexCache() *monoVertexCache { + return &monoVertexCache{ + cache: make(map[string]*MonoVertexEntry), + mutex: &sync.Mutex{}, + } +} + +func getK8sObjectFromMonoVertex(monoVertex *v1alpha1.MonoVertex) *common.K8sObject { + labels := make(map[string]string) + annotations := make(map[string]string) + if monoVertex.Spec.Metadata != nil { + labels = monoVertex.Spec.Metadata.Labels + annotations = monoVertex.Spec.Metadata.Annotations + } + return &common.K8sObject{ + Name: monoVertex.Name, + Namespace: monoVertex.Namespace, + Annotations: annotations, + Labels: labels, + Status: common.NotProcessed, + Type: common.MonoVertex, + } +} + +func (p *monoVertexCache) Put(monoVertex *common.K8sObject) (*common.K8sObject, bool) { + defer p.mutex.Unlock() + p.mutex.Lock() + identity := common.GetGlobalIdentifier(monoVertex.Annotations, monoVertex.Labels) + existingMonoVertices := p.cache[identity] + if existingMonoVertices == nil { + existingMonoVertices = &MonoVertexEntry{ + Identity: identity, + MonoVertices: map[string]*common.K8sObject{monoVertex.Namespace: monoVertex}, + } + p.cache[identity] = existingMonoVertices + return monoVertex, true + } else { + monoVertexInCache := existingMonoVertices.MonoVertices[monoVertex.Namespace] + if monoVertexInCache == nil { + existingMonoVertices.MonoVertices[monoVertex.Namespace] = monoVertex + p.cache[identity] = existingMonoVertices + return monoVertex, true + } + } + return nil, false +} + +func (p *monoVertexCache) Get(key string, namespace string) *common.K8sObject { + defer p.mutex.Unlock() + p.mutex.Lock() + + jce, ok := p.cache[key] + if ok { + j, ok := jce.MonoVertices[namespace] + if ok { + return j + } + } + return nil +} + +func (p *monoVertexCache) GetMonoVertexProcessStatus(monoVertex *v1alpha1.MonoVertex) (string, error) { + defer p.mutex.Unlock() + p.mutex.Lock() + + monoVertexObj := getK8sObjectFromMonoVertex(monoVertex) + identity := common.GetGlobalIdentifier(monoVertexObj.Annotations, monoVertexObj.Labels) + + jce, ok := p.cache[identity] + if ok { + monoVertexFromNamespace, ok := jce.MonoVertices[monoVertexObj.Namespace] + if ok { + return monoVertexFromNamespace.Status, nil + } + } + + return common.NotProcessed, nil +} + +func (p *monoVertexCache) UpdateMonoVertexProcessStatus(monoVertex *v1alpha1.MonoVertex, status string) error { + defer p.mutex.Unlock() + p.mutex.Lock() + + monoVertexObj := getK8sObjectFromMonoVertex(monoVertex) + identity := common.GetGlobalIdentifier(monoVertexObj.Annotations, monoVertexObj.Labels) + + jce, ok := p.cache[identity] + if ok { + monoVertexFromNamespace, ok := jce.MonoVertices[monoVertex.Namespace] + if ok { + monoVertexFromNamespace.Status = status + p.cache[jce.Identity] = jce + return nil + } else { + newMonoVertex := getK8sObjectFromMonoVertex(monoVertex) + newMonoVertex.Status = status + jce.MonoVertices[monoVertex.Namespace] = newMonoVertex + p.cache[jce.Identity] = jce + return nil + } + } + + return fmt.Errorf(LogCacheFormat, "UpdateStatus", "MonoVertex", + monoVertex.Name, monoVertex.Namespace, "", "nothing to update, monoVertex not found in cache") +} + +func (p *MonoVertexController) DoesGenerationMatch(ctxLogger *log.Entry, obj interface{}, oldObj interface{}) (bool, error) { + if !common.DoGenerationCheck() { + ctxLogger.Debugf(ControllerLogFormat, "DoesGenerationMatch", "", + fmt.Sprintf("generation check is disabled")) + return false, nil + } + monoVertexNew, ok := obj.(*v1alpha1.MonoVertex) + if !ok { + return false, fmt.Errorf("type assertion failed, %v is not of type *MonoVertex", obj) + } + monoVertexOld, ok := oldObj.(*v1alpha1.MonoVertex) + if !ok { + return false, fmt.Errorf("type assertion failed, %v is not of type *MonoVertex", oldObj) + } + if monoVertexNew.Generation == monoVertexOld.Generation { + ctxLogger.Infof(ControllerLogFormat, "DoesGenerationMatch", "", + fmt.Sprintf("old and new generation matched for monoVertex %s", monoVertexNew.Name)) + return true, nil + } + return false, nil +} + +func NewMonoVertexController(stopCh <-chan struct{}, handler ClientDiscoveryHandler, config *rest.Config, resyncPeriod time.Duration, clientLoader loader.ClientLoader) (*MonoVertexController, error) { + + monoVertexController := MonoVertexController{} + monoVertexController.MonoVertexHandler = handler + + var err error + + monoVertexController.NumaflowClient, err = clientLoader.LoadNumaflowClientFromConfig(config) + if err != nil { + return nil, fmt.Errorf("failed to create dependency controller k8s client: %v", err) + } + + monoVertexController.informer = v1alpha12.NewMonoVertexInformer( + monoVertexController.NumaflowClient, + meta_v1.NamespaceAll, + resyncPeriod, + cache.Indexers{}, + ) + + monoVertexController.Cache = newMonoVertexCache() + + NewController("monoVertex-ctrl", config.Host, stopCh, &monoVertexController, monoVertexController.informer) + + return &monoVertexController, nil +} + +func (d *MonoVertexController) Added(ctx context.Context, obj interface{}) error { + return addUpdateMonoVertex(d, ctx, obj) +} + +func (d *MonoVertexController) Updated(ctx context.Context, obj interface{}, oldObj interface{}) error { + //Not Required, this is a no-op as as Add event already handles registering this as a mesh client + return nil +} + +func addUpdateMonoVertex(j *MonoVertexController, ctx context.Context, obj interface{}) error { + monoVertex, ok := obj.(*v1alpha1.MonoVertex) + if !ok { + return fmt.Errorf("failed to covert informer object to MonoVertex") + } + if !common.ShouldIgnore(monoVertex.Spec.Metadata.Annotations, monoVertex.Spec.Metadata.Labels) { + k8sObj := getK8sObjectFromMonoVertex(monoVertex) + newK8sObj, isNew := j.Cache.Put(k8sObj) + if isNew { + newK8sObj.Status = common.ProcessingInProgress + j.MonoVertexHandler.Added(ctx, newK8sObj) + } else { + log.Infof("Ignoring monoVertex %v as it was already processed", monoVertex.Name) + } + } + return nil +} + +func (p *MonoVertexController) Deleted(ctx context.Context, obj interface{}) error { + //Not Required (to be handled via asset off boarding) + return nil +} + +func (d *MonoVertexController) GetProcessItemStatus(obj interface{}) (string, error) { + monoVertex, ok := obj.(*v1alpha1.MonoVertex) + if !ok { + return common.NotProcessed, fmt.Errorf("type assertion failed, %v is not of type *common.K8sObject", obj) + } + return d.Cache.GetMonoVertexProcessStatus(monoVertex) +} + +func (d *MonoVertexController) UpdateProcessItemStatus(obj interface{}, status string) error { + monoVertex, ok := obj.(*v1alpha1.MonoVertex) + if !ok { + return fmt.Errorf("type assertion failed, %v is not of type *MonoVertex", obj) + } + return d.Cache.UpdateMonoVertexProcessStatus(monoVertex, status) +} + +func (d *MonoVertexController) LogValueOfAdmiralIoIgnore(obj interface{}) { + monoVertex, ok := obj.(*v1alpha1.MonoVertex) + if !ok { + return + } + monoVertexObj := getK8sObjectFromMonoVertex(monoVertex) + if monoVertexObj.Annotations[common.AdmiralIgnoreAnnotation] == "true" { + log.Infof("op=%s type=%v name=%v namespace=%s cluster=%s message=%s", "admiralIoIgnoreAnnotationCheck", common.MonoVertex, + monoVertex.Name, monoVertex.Namespace, "", "Value=true") + } +} + +func (j *MonoVertexController) Get(ctx context.Context, isRetry bool, obj interface{}) (interface{}, error) { + monoVertex, ok := obj.(*v1alpha1.MonoVertex) + monoVertexObj := getK8sObjectFromMonoVertex(monoVertex) + identity := common.GetGlobalIdentifier(monoVertexObj.Annotations, monoVertexObj.Labels) + if ok && isRetry { + return j.Cache.Get(identity, monoVertex.Namespace), nil + } + if ok && j.NumaflowClient != nil { + return j.NumaflowClient.NumaflowV1alpha1().MonoVertices(monoVertex.Namespace).Get(ctx, monoVertex.Name, meta_v1.GetOptions{}) + } + return nil, fmt.Errorf("kubernetes client is not initialized, txId=%s", ctx.Value("txId")) +} diff --git a/admiral/pkg/controller/admiral/monovertex_test.go b/admiral/pkg/controller/admiral/monovertex_test.go new file mode 100644 index 00000000..7d097f6c --- /dev/null +++ b/admiral/pkg/controller/admiral/monovertex_test.go @@ -0,0 +1,508 @@ +package admiral + +import ( + "context" + "fmt" + "github.com/istio-ecosystem/admiral/admiral/pkg/client/loader" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + "github.com/istio-ecosystem/admiral/admiral/pkg/test" + "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1" + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + coreV1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/clientcmd" + "reflect" + "sync" + "testing" +) + +func getMonoVertex(namespace string, annotations map[string]string, labels map[string]string) *v1alpha1.MonoVertex { + monomonoVertex := &v1alpha1.MonoVertex{} + monomonoVertex.Namespace = namespace + spec := v1alpha1.MonoVertexSpec{} + spec.Metadata = &v1alpha1.Metadata{ + Annotations: annotations, + Labels: labels, + } + monomonoVertex.Spec = spec + return monomonoVertex +} + +func TestMonoVertexController_Added(t *testing.T) { + common.ResetSync() + admiralParams := common.AdmiralParams{ + LabelSet: &common.LabelSet{ + WorkloadIdentityKey: "identity", + EnvKey: "admiral.io/env", + AdmiralCRDIdentityLabel: "identity", + DeploymentAnnotation: "sidecar.istio.io/inject", + AdmiralIgnoreLabel: "admiral-ignore", + }, + } + common.InitializeConfig(admiralParams) + ctx := context.Background() + ctx = context.WithValue(ctx, "clusterId", "test-cluster-k8s") + //MonoVertexs with the correct label are added to the cache + mdh := test.MockClientDiscoveryHandler{} + cache := monoVertexCache{ + cache: map[string]*MonoVertexEntry{}, + mutex: &sync.Mutex{}, + } + monomonoVertexController := MonoVertexController{ + MonoVertexHandler: &mdh, + Cache: &cache, + } + monomonoVertex := getMonoVertex("monomonoVertex-ns", map[string]string{"sidecar.istio.io/inject": "true", "admiral.io/env": "dev"}, map[string]string{"identity": "monomonoVertex", "istio-injected": "true"}) + expectedMonoVertex := getK8sObjectFromMonoVertex(monomonoVertex) + expectedMonoVertex.Status = common.ProcessingInProgress + monomonoVertexWithBadLabels := getMonoVertex("monomonoVertexWithBadLabels-ns", map[string]string{"admiral.io/env": "dev"}, map[string]string{"identity": "monomonoVertexWithBadLabels", "random-label": "true"}) + monomonoVertexWithIgnoreLabels := getMonoVertex("monomonoVertexWithIgnoreLabels-ns", map[string]string{"sidecar.istio.io/inject": "true", "admiral.io/env": "dev"}, map[string]string{"identity": "monomonoVertexWithIgnoreLabels", "istio-injected": "true", "admiral-ignore": "true"}) + monomonoVertexWithIgnoreAnnotations := getMonoVertex("monomonoVertexWithIgnoreAnnotations-ns", map[string]string{"admiral.io/ignore": "true"}, map[string]string{"identity": "monomonoVertexWithIgnoreAnnotations"}) + monomonoVertexWithIgnoreAnnotations.Annotations = map[string]string{"admiral.io/ignore": "true"} + + testCases := []struct { + name string + monomonoVertex *v1alpha1.MonoVertex + expectedMonoVertex *common.K8sObject + id string + expectedCacheContains bool + }{ + { + name: "Expects monomonoVertex to be added to the cache when the correct label is present", + monomonoVertex: monomonoVertex, + expectedMonoVertex: expectedMonoVertex, + id: "monomonoVertex", + expectedCacheContains: true, + }, + { + name: "Expects monomonoVertex to not be added to the cache when the correct label is not present", + monomonoVertex: monomonoVertexWithBadLabels, + expectedMonoVertex: nil, + id: "monomonoVertexWithBadLabels", + expectedCacheContains: false, + }, + { + name: "Expects ignored monomonoVertex identified by label to not be added to the cache", + monomonoVertex: monomonoVertexWithIgnoreLabels, + expectedMonoVertex: nil, + id: "monomonoVertexWithIgnoreLabels", + expectedCacheContains: false, + }, + { + name: "Expects ignored monomonoVertex identified by monomonoVertex annotation to not be added to the cache", + monomonoVertex: monomonoVertexWithIgnoreAnnotations, + expectedMonoVertex: nil, + id: "monomonoVertexWithIgnoreAnnotations", + expectedCacheContains: false, + }, + } + ns := coreV1.Namespace{} + ns.Name = "test-ns" + ns.Annotations = map[string]string{"admiral.io/ignore": "true"} + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + if c.name == "Expects ignored monomonoVertex identified by label to be removed from the cache" { + monomonoVertex.Spec.Metadata.Labels["admiral-ignore"] = "true" + } + monomonoVertexController.Added(ctx, c.monomonoVertex) + monomonoVertexClusterEntry := monomonoVertexController.Cache.cache[c.id] + var monomonoVertexsMap map[string]*common.K8sObject = nil + if monomonoVertexClusterEntry != nil { + monomonoVertexsMap = monomonoVertexClusterEntry.MonoVertices + } + var monomonoVertexObj *common.K8sObject = nil + if monomonoVertexsMap != nil && len(monomonoVertexsMap) > 0 { + monomonoVertexObj = monomonoVertexsMap[c.monomonoVertex.Namespace] + } + if !reflect.DeepEqual(c.expectedMonoVertex, monomonoVertexObj) { + t.Errorf("Expected rollout %+v but got %+v", c.expectedMonoVertex, monomonoVertexObj) + } + }) + } +} + +func TestMonoVertexControlle_DoesGenerationMatch(t *testing.T) { + dc := MonoVertexController{} + + admiralParams := common.AdmiralParams{} + + testCases := []struct { + name string + monomonoVertexNew interface{} + monomonoVertexOld interface{} + enableGenerationCheck bool + expectedValue bool + expectedError error + }{ + { + name: "Given context, new monomonoVertex and old monomonoVertex object " + + "When new monomonoVertex is not of type *v1.MonoVertex " + + "Then func should return an error", + monomonoVertexNew: struct{}{}, + monomonoVertexOld: struct{}{}, + enableGenerationCheck: true, + expectedError: fmt.Errorf("type assertion failed, {} is not of type *MonoVertex"), + }, + { + name: "Given context, new monomonoVertex and old monomonoVertex object " + + "When old monomonoVertex is not of type *v1.MonoVertex " + + "Then func should return an error", + monomonoVertexNew: &v1alpha1.MonoVertex{}, + monomonoVertexOld: struct{}{}, + enableGenerationCheck: true, + expectedError: fmt.Errorf("type assertion failed, {} is not of type *MonoVertex"), + }, + { + name: "Given context, new monomonoVertex and old monomonoVertex object " + + "When monomonoVertex generation check is enabled but the generation does not match " + + "Then func should return false ", + monomonoVertexNew: &v1alpha1.MonoVertex{ + ObjectMeta: v1.ObjectMeta{ + Generation: 2, + }, + }, + monomonoVertexOld: &v1alpha1.MonoVertex{ + ObjectMeta: v1.ObjectMeta{ + Generation: 1, + }, + }, + enableGenerationCheck: true, + expectedError: nil, + }, + { + name: "Given context, new monomonoVertex and old monomonoVertex object " + + "When monomonoVertex generation check is disabled " + + "Then func should return false ", + monomonoVertexNew: &v1alpha1.MonoVertex{ + ObjectMeta: v1.ObjectMeta{ + Generation: 2, + }, + }, + monomonoVertexOld: &v1alpha1.MonoVertex{ + ObjectMeta: v1.ObjectMeta{ + Generation: 1, + }, + }, + expectedError: nil, + }, + { + name: "Given context, new monomonoVertex and old monomonoVertex object " + + "When monomonoVertex generation check is enabled and the old and new monomonoVertex generation is equal " + + "Then func should just return true", + monomonoVertexNew: &v1alpha1.MonoVertex{ + ObjectMeta: v1.ObjectMeta{ + Generation: 2, + }, + }, + monomonoVertexOld: &v1alpha1.MonoVertex{ + ObjectMeta: v1.ObjectMeta{ + Generation: 2, + }, + }, + enableGenerationCheck: true, + expectedError: nil, + expectedValue: true, + }, + } + + ctxLogger := log.WithFields(log.Fields{ + "txId": "abc", + }) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + admiralParams.EnableGenerationCheck = tc.enableGenerationCheck + common.ResetSync() + common.InitializeConfig(admiralParams) + actual, err := dc.DoesGenerationMatch(ctxLogger, tc.monomonoVertexNew, tc.monomonoVertexOld) + if !ErrorEqualOrSimilar(err, tc.expectedError) { + t.Errorf("expected: %v, got: %v", tc.expectedError, err) + } + if err == nil { + if tc.expectedValue != actual { + t.Errorf("expected: %v, got: %v", tc.expectedValue, actual) + } + } + }) + } + +} + +func TestNewMonoVertexController(t *testing.T) { + config, err := clientcmd.BuildConfigFromFlags("", "../../test/resources/admins@fake-cluster.k8s.local") + if err != nil { + t.Errorf("%v", err) + } + stop := make(chan struct{}) + monomonoVertexHandler := test.MockClientDiscoveryHandler{} + + monomonoVertexCon, _ := NewMonoVertexController(stop, &monomonoVertexHandler, config, 0, loader.GetFakeClientLoader()) + + if monomonoVertexCon == nil { + t.Errorf("MonoVertex controller should not be nil") + } +} + +func TestMonoVertexUpdateProcessItemStatus(t *testing.T) { + common.ResetSync() + admiralParams := common.AdmiralParams{ + LabelSet: &common.LabelSet{ + WorkloadIdentityKey: "identity", + EnvKey: "admiral.io/env", + AdmiralCRDIdentityLabel: "identity", + }, + } + common.InitializeConfig(admiralParams) + monomonoVertexInCache := getMonoVertex("namespace-1", map[string]string{}, map[string]string{"identity": "monomonoVertex1", "env": "prd"}) + monomonoVertexInCache.Name = "monomonoVertex1" + monomonoVertexInCache2 := getMonoVertex("namespace-2", map[string]string{}, map[string]string{"identity": "monomonoVertex2", "env": "prd"}) + monomonoVertexInCache2.Name = "monomonoVertex2" + monomonoVertexNotInCache := getMonoVertex("namespace-3", map[string]string{}, map[string]string{"identity": "monomonoVertex3", "env": "prd"}) + monomonoVertexNotInCache.Name = "monomonoVertex3" + var ( + serviceAccount = &coreV1.ServiceAccount{} + ) + + // Populating the monomonoVertex Cache + monomonoVertexCache := &monoVertexCache{ + cache: make(map[string]*MonoVertexEntry), + mutex: &sync.Mutex{}, + } + + monomonoVertexController := &MonoVertexController{ + Cache: monomonoVertexCache, + } + + monomonoVertexCache.Put(getK8sObjectFromMonoVertex(monomonoVertexInCache)) + monomonoVertexCache.Put(getK8sObjectFromMonoVertex(monomonoVertexInCache2)) + + testCases := []struct { + name string + obj interface{} + statusToSet string + expectedErr error + expectedStatus string + }{ + { + name: "Given monomonoVertex cache has a valid monomonoVertex in its cache, " + + "And is processed" + + "Then, the status for the valid monomonoVertex should be updated to processed", + obj: monomonoVertexInCache, + statusToSet: common.Processed, + expectedErr: nil, + expectedStatus: common.Processed, + }, + { + name: "Given monomonoVertex cache has a valid monomonoVertex in its cache, " + + "And is processed" + + "Then, the status for the valid monomonoVertex should be updated to not processed", + obj: monomonoVertexInCache2, + statusToSet: common.NotProcessed, + expectedErr: nil, + expectedStatus: common.NotProcessed, + }, + { + name: "Given monomonoVertex cache does not has a valid monomonoVertex in its cache, " + + "Then, the status for the valid monomonoVertex should be not processed, " + + "And an error should be returned with the monomonoVertex not found message", + obj: monomonoVertexNotInCache, + statusToSet: common.NotProcessed, + expectedErr: fmt.Errorf(LogCacheFormat, "UpdateStatus", "MonoVertex", monomonoVertexNotInCache.Name, monomonoVertexNotInCache.Namespace, "", "nothing to update, monoVertex not found in cache"), + expectedStatus: common.NotProcessed, + }, + { + name: "Given ServiceAccount is passed to the function, " + + "Then, the function should not panic, " + + "And return an error", + obj: serviceAccount, + expectedErr: fmt.Errorf("type assertion failed"), + expectedStatus: common.NotProcessed, + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + err := monomonoVertexController.UpdateProcessItemStatus(c.obj, c.statusToSet) + if !ErrorEqualOrSimilar(err, c.expectedErr) { + t.Errorf("expected: %v, got: %v", c.expectedErr, err) + } + status, _ := monomonoVertexController.GetProcessItemStatus(c.obj) + assert.Equal(t, c.expectedStatus, status) + }) + } +} + +func TestMonoVertexGetProcessItemStatus(t *testing.T) { + common.ResetSync() + admiralParams := common.AdmiralParams{ + LabelSet: &common.LabelSet{ + WorkloadIdentityKey: "identity", + EnvKey: "admiral.io/env", + AdmiralCRDIdentityLabel: "identity", + }, + } + common.InitializeConfig(admiralParams) + var ( + serviceAccount = &coreV1.ServiceAccount{} + ) + monomonoVertexInCache := getMonoVertex("namespace-1", map[string]string{}, map[string]string{"identity": "monomonoVertex1"}) + monomonoVertexInCache.Name = "debug-1" + monomonoVertexNotInCache := getMonoVertex("namespace-2", map[string]string{}, map[string]string{"identity": "monomonoVertex2"}) + monomonoVertexNotInCache.Name = "debug-2" + + // Populating the monomonoVertex Cache + monomonoVertexCache := &monoVertexCache{ + cache: make(map[string]*MonoVertexEntry), + mutex: &sync.Mutex{}, + } + + monomonoVertexController := &MonoVertexController{ + Cache: monomonoVertexCache, + } + + monomonoVertexCache.Put(getK8sObjectFromMonoVertex(monomonoVertexInCache)) + monomonoVertexCache.UpdateMonoVertexProcessStatus(monomonoVertexInCache, common.Processed) + + testCases := []struct { + name string + obj interface{} + expectedErr error + expectedResult string + }{ + { + name: "Given monomonoVertex cache has a valid monomonoVertex in its cache, " + + "And is processed" + + "Then, we should be able to get the status as processed", + obj: monomonoVertexInCache, + expectedResult: common.Processed, + }, + { + name: "Given monomonoVertex cache does not has a valid monomonoVertex in its cache, " + + "Then, the status for the valid monomonoVertex should not be updated", + obj: monomonoVertexNotInCache, + expectedResult: common.NotProcessed, + }, + { + name: "Given ServiceAccount is passed to the function, " + + "Then, the function should not panic, " + + "And return an error", + obj: serviceAccount, + expectedErr: fmt.Errorf("type assertion failed"), + expectedResult: common.NotProcessed, + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + res, err := monomonoVertexController.GetProcessItemStatus(c.obj) + if !ErrorEqualOrSimilar(err, c.expectedErr) { + t.Errorf("expected: %v, got: %v", c.expectedErr, err) + } + assert.Equal(t, c.expectedResult, res) + }) + } +} + +func TestMonoVertexLogValueOfAdmiralIoIgnore(t *testing.T) { + // Test case 1: obj is not a MonoVertex object + d := &MonoVertexController{} + d.LogValueOfAdmiralIoIgnore("not a monomonoVertex") + // No error should occur + + // Test case 2: K8sClient is nil + d = &MonoVertexController{} + d.LogValueOfAdmiralIoIgnore(&v1alpha1.MonoVertex{}) + // No error should occur + + d.LogValueOfAdmiralIoIgnore(&v1alpha1.MonoVertex{ObjectMeta: metav1.ObjectMeta{Namespace: "test-ns"}}) + // No error should occur + + // Test case 3: AdmiralIgnoreAnnotation is set in MonoVertex object + d = &MonoVertexController{} + monomonoVertex := &v1alpha1.MonoVertex{ + Spec: v1alpha1.MonoVertexSpec{ + AbstractPodTemplate: v1alpha1.AbstractPodTemplate{ + Metadata: &v1alpha1.Metadata{ + Annotations: map[string]string{ + common.AdmiralIgnoreAnnotation: "true", + }, + }, + }, + }, + } + d.LogValueOfAdmiralIoIgnore(monomonoVertex) + // No error should occur +} + +func TestMonoVertexController_CacheGet(t *testing.T) { + common.ResetSync() + admiralParams := common.AdmiralParams{ + LabelSet: &common.LabelSet{ + WorkloadIdentityKey: "identity", + EnvKey: "admiral.io/env", + AdmiralCRDIdentityLabel: "identity", + DeploymentAnnotation: "sidecar.istio.io/inject", + AdmiralIgnoreLabel: "admiral-ignore", + }, + } + common.InitializeConfig(admiralParams) + ctx := context.Background() + ctx = context.WithValue(ctx, "clusterId", "test-cluster-k8s") + + cache := monoVertexCache{ + cache: map[string]*MonoVertexEntry{}, + mutex: &sync.Mutex{}, + } + + monoVertex := getMonoVertex("monoVertex-ns", map[string]string{"sidecar.istio.io/inject": "true", "admiral.io/env": "dev"}, map[string]string{"identity": "monoVertex1", "istio-injected": "true"}) + cache.Put(getK8sObjectFromMonoVertex(monoVertex)) + monoVertexSameIdentityInDiffNamespace := getMonoVertex("monoVertex-ns-2", map[string]string{"sidecar.istio.io/inject": "true", "admiral.io/env": "dev"}, map[string]string{"identity": "monoVertex1", "istio-injected": "true"}) + cache.Put(getK8sObjectFromMonoVertex(monoVertexSameIdentityInDiffNamespace)) + monoVertex2 := getMonoVertex("monoVertex-ns-3", map[string]string{"sidecar.istio.io/inject": "true", "admiral.io/env": "dev"}, map[string]string{"identity": "monoVertex2", "istio-injected": "true"}) + cache.Put(getK8sObjectFromMonoVertex(monoVertex2)) + + testCases := []struct { + name string + expectedVertex *common.K8sObject + identity string + namespace string + }{ + { + name: "Expects monoVertex to be in the cache when right identity and namespace are passed", + expectedVertex: getK8sObjectFromMonoVertex(monoVertex), + identity: "monoVertex1", + namespace: "monoVertex-ns", + }, + { + name: "Expects monoVertex to be in the cache when same identity and diff namespace are passed", + expectedVertex: getK8sObjectFromMonoVertex(monoVertexSameIdentityInDiffNamespace), + identity: "monoVertex1", + namespace: "monoVertex-ns-2", + }, + { + name: "Expects monoVertex to be in the cache when diff identity and diff namespace are passed", + expectedVertex: getK8sObjectFromMonoVertex(monoVertex2), + identity: "monoVertex2", + namespace: "monoVertex-ns-3", + }, + { + name: "Expects nil monoVertex in random namespace", + expectedVertex: nil, + identity: "monoVertex2", + namespace: "random", + }, + } + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + if c.name == "Expects ignored monoVertex identified by label to be removed from the cache" { + monoVertex.Spec.Metadata.Labels["admiral-ignore"] = "true" + } + monoVertexObj := cache.Get(c.identity, c.namespace) + if !reflect.DeepEqual(c.expectedVertex, monoVertexObj) { + t.Errorf("Expected rollout %+v but got %+v", c.expectedVertex, monoVertexObj) + } + }) + } +} diff --git a/admiral/pkg/controller/admiral/vertex.go b/admiral/pkg/controller/admiral/vertex.go new file mode 100644 index 00000000..49c0f82d --- /dev/null +++ b/admiral/pkg/controller/admiral/vertex.go @@ -0,0 +1,267 @@ +package admiral + +import ( + "context" + "fmt" + "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1" + numaflow "github.com/numaproj/numaflow/pkg/client/clientset/versioned" + v1alpha12 "github.com/numaproj/numaflow/pkg/client/informers/externalversions/numaflow/v1alpha1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sync" + "time" + + "github.com/istio-ecosystem/admiral/admiral/pkg/client/loader" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + log "github.com/sirupsen/logrus" + "k8s.io/client-go/rest" + + "k8s.io/client-go/tools/cache" +) + +//Vertex controller discovers vertexs as mesh clients (its assumed that k8s Vertex doesn't have any ingress communication) + +type VertexController struct { + NumaflowClient numaflow.Interface + VertexHandler ClientDiscoveryHandler + informer cache.SharedIndexInformer + Cache *vertexCache +} + +type VertexEntry struct { + Identity string + Vertices map[string]*common.K8sObject +} + +type vertexCache struct { + //map of dependencies key=identity value array of onboarded identities + cache map[string]*VertexEntry + mutex *sync.Mutex +} + +func newVertexCache() *vertexCache { + return &vertexCache{ + cache: make(map[string]*VertexEntry), + mutex: &sync.Mutex{}, + } +} + +func getK8sObjectFromVertex(vertex *v1alpha1.Vertex) *common.K8sObject { + labels := make(map[string]string) + annotations := make(map[string]string) + if vertex.Spec.Metadata != nil { + labels = vertex.Spec.Metadata.Labels + annotations = vertex.Spec.Metadata.Annotations + } + return &common.K8sObject{ + Name: vertex.Name, + Namespace: vertex.Namespace, + Annotations: annotations, + Labels: labels, + Status: common.NotProcessed, + Type: common.Vertex, + } +} + +func (p *vertexCache) Put(vertex *common.K8sObject) (*common.K8sObject, bool) { + defer p.mutex.Unlock() + p.mutex.Lock() + + identity := common.GetGlobalIdentifier(vertex.Annotations, vertex.Labels) + existingVertices := p.cache[identity] + if existingVertices == nil { + existingVertices = &VertexEntry{ + Identity: identity, + Vertices: map[string]*common.K8sObject{vertex.Namespace: vertex}, + } + p.cache[identity] = existingVertices + return vertex, true + } else { + vertexInCache := existingVertices.Vertices[vertex.Namespace] + if vertexInCache == nil { + existingVertices.Vertices[vertex.Namespace] = vertex + p.cache[identity] = existingVertices + return vertex, true + } + } + return nil, false +} + +func (p *vertexCache) Get(key string, namespace string) *common.K8sObject { + defer p.mutex.Unlock() + p.mutex.Lock() + + jce, ok := p.cache[key] + if ok { + j, ok := jce.Vertices[namespace] + if ok { + return j + } + } + return nil +} + +func (p *vertexCache) GetVertexProcessStatus(vertex *v1alpha1.Vertex) (string, error) { + defer p.mutex.Unlock() + p.mutex.Lock() + + vertexObj := getK8sObjectFromVertex(vertex) + identity := common.GetGlobalIdentifier(vertexObj.Annotations, vertexObj.Labels) + + jce, ok := p.cache[identity] + if ok { + vertexFromNamespace, ok := jce.Vertices[vertexObj.Namespace] + if ok { + return vertexFromNamespace.Status, nil + } + } + + return common.NotProcessed, nil +} + +func (p *vertexCache) UpdateVertexProcessStatus(vertex *v1alpha1.Vertex, status string) error { + defer p.mutex.Unlock() + p.mutex.Lock() + + vertexObj := getK8sObjectFromVertex(vertex) + + identity := common.GetGlobalIdentifier(vertexObj.Annotations, vertexObj.Labels) + + jce, ok := p.cache[identity] + if ok { + vertexFromNamespace, ok := jce.Vertices[vertexObj.Namespace] + if ok { + vertexFromNamespace.Status = status + p.cache[jce.Identity] = jce + return nil + } else { + vertexObj.Status = status + jce.Vertices[vertex.Namespace] = vertexObj + p.cache[jce.Identity] = jce + return nil + } + } + + return fmt.Errorf(LogCacheFormat, "UpdateStatus", "Vertex", + vertex.Name, vertex.Namespace, "", "nothing to update, vertex not found in cache") +} + +func (p *VertexController) DoesGenerationMatch(ctxLogger *log.Entry, obj interface{}, oldObj interface{}) (bool, error) { + if !common.DoGenerationCheck() { + ctxLogger.Debugf(ControllerLogFormat, "DoesGenerationMatch", "", + fmt.Sprintf("generation check is disabled")) + return false, nil + } + vertexNew, ok := obj.(*v1alpha1.Vertex) + if !ok { + return false, fmt.Errorf("type assertion failed, %v is not of type *Vertex", obj) + } + vertexOld, ok := oldObj.(*v1alpha1.Vertex) + if !ok { + return false, fmt.Errorf("type assertion failed, %v is not of type *Vertex", oldObj) + } + if vertexNew.Generation == vertexOld.Generation { + ctxLogger.Infof(ControllerLogFormat, "DoesGenerationMatch", "", + fmt.Sprintf("old and new generation matched for vertex %s", vertexNew.Name)) + return true, nil + } + return false, nil +} + +func NewVertexController(stopCh <-chan struct{}, handler ClientDiscoveryHandler, config *rest.Config, resyncPeriod time.Duration, clientLoader loader.ClientLoader) (*VertexController, error) { + + vertexController := VertexController{} + vertexController.VertexHandler = handler + + var err error + + vertexController.NumaflowClient, err = clientLoader.LoadNumaflowClientFromConfig(config) + if err != nil { + return nil, fmt.Errorf("failed to create dependency controller k8s client: %v", err) + } + + vertexController.informer = v1alpha12.NewVertexInformer( + vertexController.NumaflowClient, + meta_v1.NamespaceAll, + resyncPeriod, + cache.Indexers{}, + ) + + vertexController.Cache = newVertexCache() + + NewController("vertex-ctrl", config.Host, stopCh, &vertexController, vertexController.informer) + + return &vertexController, nil +} + +func (d *VertexController) Added(ctx context.Context, obj interface{}) error { + return addUpdateVertex(d, ctx, obj) +} + +func (d *VertexController) Updated(ctx context.Context, obj interface{}, oldObj interface{}) error { + //Not Required, this is a no-op as Add event already handles registering this as a mesh client + return nil +} + +func addUpdateVertex(j *VertexController, ctx context.Context, obj interface{}) error { + vertex, ok := obj.(*v1alpha1.Vertex) + if !ok { + return fmt.Errorf("failed to covert informer object to Vertex") + } + k8sObj := getK8sObjectFromVertex(vertex) + if !common.ShouldIgnore(vertex.Spec.Metadata.Annotations, vertex.Spec.Metadata.Labels) { + newK8sObj, isNew := j.Cache.Put(k8sObj) + if isNew { + newK8sObj.Status = common.ProcessingInProgress + j.VertexHandler.Added(ctx, newK8sObj) + } else { + log.Infof("Ignoring vertex %v as it was already processed", vertex.Name) + } + } + return nil +} + +func (p *VertexController) Deleted(ctx context.Context, obj interface{}) error { + //Not Required (to be handled via asset off boarding) + return nil +} + +func (d *VertexController) GetProcessItemStatus(obj interface{}) (string, error) { + vertex, ok := obj.(*v1alpha1.Vertex) + if !ok { + return common.NotProcessed, fmt.Errorf("type assertion failed, %v is not of type *common.K8sObject", obj) + } + return d.Cache.GetVertexProcessStatus(vertex) +} + +func (d *VertexController) UpdateProcessItemStatus(obj interface{}, status string) error { + vertex, ok := obj.(*v1alpha1.Vertex) + if !ok { + return fmt.Errorf("type assertion failed, %v is not of type *Vertex", obj) + } + return d.Cache.UpdateVertexProcessStatus(vertex, status) +} + +func (d *VertexController) LogValueOfAdmiralIoIgnore(obj interface{}) { + vertex, ok := obj.(*v1alpha1.Vertex) + if !ok { + return + } + vetexObj := getK8sObjectFromVertex(vertex) + if vetexObj.Annotations[common.AdmiralIgnoreAnnotation] == "true" { + log.Infof("op=%s type=%v name=%v namespace=%s cluster=%s message=%s", "admiralIoIgnoreAnnotationCheck", common.Vertex, + vertex.Name, vertex.Namespace, "", "Value=true") + } +} + +func (j *VertexController) Get(ctx context.Context, isRetry bool, obj interface{}) (interface{}, error) { + vertex, ok := obj.(*v1alpha1.Vertex) + vertexObj := getK8sObjectFromVertex(vertex) + identity := common.GetGlobalIdentifier(vertexObj.Annotations, vertexObj.Labels) + if ok && isRetry { + return j.Cache.Get(identity, vertex.Namespace), nil + } + if ok && j.NumaflowClient != nil { + return j.NumaflowClient.NumaflowV1alpha1().Vertices(vertex.Namespace).Get(ctx, vertex.Name, meta_v1.GetOptions{}) + } + return nil, fmt.Errorf("kubernetes client is not initialized, txId=%s", ctx.Value("txId")) +} diff --git a/admiral/pkg/controller/admiral/vertex_test.go b/admiral/pkg/controller/admiral/vertex_test.go new file mode 100644 index 00000000..1d49b864 --- /dev/null +++ b/admiral/pkg/controller/admiral/vertex_test.go @@ -0,0 +1,510 @@ +package admiral + +import ( + "context" + "fmt" + "github.com/istio-ecosystem/admiral/admiral/pkg/client/loader" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + "github.com/istio-ecosystem/admiral/admiral/pkg/test" + "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1" + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + coreV1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/clientcmd" + "reflect" + "sync" + "testing" +) + +func getVertex(namespace string, annotations map[string]string, labels map[string]string) *v1alpha1.Vertex { + vertex := &v1alpha1.Vertex{} + vertex.Namespace = namespace + spec := v1alpha1.VertexSpec{} + spec.Metadata = &v1alpha1.Metadata{ + Annotations: annotations, + Labels: labels, + } + vertex.Spec = spec + return vertex +} + +func TestVertexController_Added(t *testing.T) { + common.ResetSync() + admiralParams := common.AdmiralParams{ + LabelSet: &common.LabelSet{ + WorkloadIdentityKey: "identity", + EnvKey: "admiral.io/env", + AdmiralCRDIdentityLabel: "identity", + DeploymentAnnotation: "sidecar.istio.io/inject", + AdmiralIgnoreLabel: "admiral-ignore", + }, + } + common.InitializeConfig(admiralParams) + ctx := context.Background() + ctx = context.WithValue(ctx, "clusterId", "test-cluster-k8s") + //Vertexs with the correct label are added to the cache + mdh := test.MockClientDiscoveryHandler{} + cache := vertexCache{ + cache: map[string]*VertexEntry{}, + mutex: &sync.Mutex{}, + } + vertexController := VertexController{ + VertexHandler: &mdh, + Cache: &cache, + } + vertex := getVertex("vertex-ns", map[string]string{"sidecar.istio.io/inject": "true", "admiral.io/env": "dev"}, map[string]string{"identity": "vertex", "istio-injected": "true"}) + expectedVertex := getK8sObjectFromVertex(vertex) + expectedVertex.Status = common.ProcessingInProgress + vertexWithBadLabels := getVertex("vertexWithBadLabels-ns", map[string]string{"admiral.io/env": "dev"}, map[string]string{"identity": "vertexWithBadLabels", "random-label": "true"}) + vertexWithIgnoreLabels := getVertex("vertexWithIgnoreLabels-ns", map[string]string{"sidecar.istio.io/inject": "true", "admiral.io/env": "dev"}, map[string]string{"identity": "vertexWithIgnoreLabels", "istio-injected": "true", "admiral-ignore": "true"}) + vertexWithIgnoreAnnotations := getVertex("vertexWithIgnoreAnnotations-ns", map[string]string{"admiral.io/ignore": "true"}, map[string]string{"identity": "vertexWithIgnoreAnnotations"}) + vertexWithIgnoreAnnotations.Annotations = map[string]string{"admiral.io/ignore": "true"} + + testCases := []struct { + name string + vertex *v1alpha1.Vertex + expectedVertex *common.K8sObject + id string + expectedCacheContains bool + }{ + { + name: "Expects vertex to be added to the cache when the correct label is present", + vertex: vertex, + expectedVertex: expectedVertex, + id: "vertex", + expectedCacheContains: true, + }, + { + name: "Expects vertex to not be added to the cache when the correct label is not present", + vertex: vertexWithBadLabels, + expectedVertex: nil, + id: "vertexWithBadLabels", + expectedCacheContains: false, + }, + { + name: "Expects ignored vertex identified by label to not be added to the cache", + vertex: vertexWithIgnoreLabels, + expectedVertex: nil, + id: "vertexWithIgnoreLabels", + expectedCacheContains: false, + }, + { + name: "Expects ignored vertex identified by vertex annotation to not be added to the cache", + vertex: vertexWithIgnoreAnnotations, + expectedVertex: nil, + id: "vertexWithIgnoreAnnotations", + expectedCacheContains: false, + }, + } + ns := coreV1.Namespace{} + ns.Name = "test-ns" + ns.Annotations = map[string]string{"admiral.io/ignore": "true"} + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + if c.name == "Expects ignored vertex identified by label to be removed from the cache" { + vertex.Spec.Metadata.Labels["admiral-ignore"] = "true" + } + vertexController.Added(ctx, c.vertex) + vertexClusterEntry := vertexController.Cache.cache[c.id] + var vertexsMap map[string]*common.K8sObject = nil + if vertexClusterEntry != nil { + vertexsMap = vertexClusterEntry.Vertices + } + var vertexObj *common.K8sObject = nil + if vertexsMap != nil && len(vertexsMap) > 0 { + vertexObj = vertexsMap[c.vertex.Namespace] + } + if !reflect.DeepEqual(c.expectedVertex, vertexObj) { + t.Errorf("Expected rollout %+v but got %+v", c.expectedVertex, vertexObj) + } + }) + } +} + +func TestVertexController_CacheGet(t *testing.T) { + common.ResetSync() + admiralParams := common.AdmiralParams{ + LabelSet: &common.LabelSet{ + WorkloadIdentityKey: "identity", + EnvKey: "admiral.io/env", + AdmiralCRDIdentityLabel: "identity", + DeploymentAnnotation: "sidecar.istio.io/inject", + AdmiralIgnoreLabel: "admiral-ignore", + }, + } + common.InitializeConfig(admiralParams) + ctx := context.Background() + ctx = context.WithValue(ctx, "clusterId", "test-cluster-k8s") + + cache := vertexCache{ + cache: map[string]*VertexEntry{}, + mutex: &sync.Mutex{}, + } + + vertex := getVertex("vertex-ns", map[string]string{"sidecar.istio.io/inject": "true", "admiral.io/env": "dev"}, map[string]string{"identity": "vertex1", "istio-injected": "true"}) + cache.Put(getK8sObjectFromVertex(vertex)) + vertexSameIdentityInDiffNamespace := getVertex("vertex-ns-2", map[string]string{"sidecar.istio.io/inject": "true", "admiral.io/env": "dev"}, map[string]string{"identity": "vertex1", "istio-injected": "true"}) + cache.Put(getK8sObjectFromVertex(vertexSameIdentityInDiffNamespace)) + vertex2 := getVertex("vertex-ns-3", map[string]string{"sidecar.istio.io/inject": "true", "admiral.io/env": "dev"}, map[string]string{"identity": "vertex2", "istio-injected": "true"}) + cache.Put(getK8sObjectFromVertex(vertex2)) + + testCases := []struct { + name string + expectedVertex *common.K8sObject + identity string + namespace string + }{ + { + name: "Expects vertex to be in the cache when right identity and namespace are passed", + expectedVertex: getK8sObjectFromVertex(vertex), + identity: "vertex1", + namespace: "vertex-ns", + }, + { + name: "Expects vertex to be in the cache when same identity and diff namespace are passed", + expectedVertex: getK8sObjectFromVertex(vertexSameIdentityInDiffNamespace), + identity: "vertex1", + namespace: "vertex-ns-2", + }, + { + name: "Expects vertex to be in the cache when diff identity and diff namespace are passed", + expectedVertex: getK8sObjectFromVertex(vertex2), + identity: "vertex2", + namespace: "vertex-ns-3", + }, + { + name: "Expects nil vertex in random namespace", + expectedVertex: nil, + identity: "vertex2", + namespace: "random", + }, + } + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + if c.name == "Expects ignored vertex identified by label to be removed from the cache" { + vertex.Spec.Metadata.Labels["admiral-ignore"] = "true" + } + vertexObj := cache.Get(c.identity, c.namespace) + if !reflect.DeepEqual(c.expectedVertex, vertexObj) { + t.Errorf("Expected rollout %+v but got %+v", c.expectedVertex, vertexObj) + } + }) + } +} + +func TestVertexControlle_DoesGenerationMatch(t *testing.T) { + dc := VertexController{} + + admiralParams := common.AdmiralParams{} + + testCases := []struct { + name string + vertexNew interface{} + vertexOld interface{} + enableGenerationCheck bool + expectedValue bool + expectedError error + }{ + { + name: "Given context, new vertex and old vertex object " + + "When new vertex is not of type *v1.Vertex " + + "Then func should return an error", + vertexNew: struct{}{}, + vertexOld: struct{}{}, + enableGenerationCheck: true, + expectedError: fmt.Errorf("type assertion failed, {} is not of type *Vertex"), + }, + { + name: "Given context, new vertex and old vertex object " + + "When old vertex is not of type *v1.Vertex " + + "Then func should return an error", + vertexNew: &v1alpha1.Vertex{}, + vertexOld: struct{}{}, + enableGenerationCheck: true, + expectedError: fmt.Errorf("type assertion failed, {} is not of type *Vertex"), + }, + { + name: "Given context, new vertex and old vertex object " + + "When vertex generation check is enabled but the generation does not match " + + "Then func should return false ", + vertexNew: &v1alpha1.Vertex{ + ObjectMeta: v1.ObjectMeta{ + Generation: 2, + }, + }, + vertexOld: &v1alpha1.Vertex{ + ObjectMeta: v1.ObjectMeta{ + Generation: 1, + }, + }, + enableGenerationCheck: true, + expectedError: nil, + }, + { + name: "Given context, new vertex and old vertex object " + + "When vertex generation check is disabled " + + "Then func should return false ", + vertexNew: &v1alpha1.Vertex{ + ObjectMeta: v1.ObjectMeta{ + Generation: 2, + }, + }, + vertexOld: &v1alpha1.Vertex{ + ObjectMeta: v1.ObjectMeta{ + Generation: 1, + }, + }, + expectedError: nil, + }, + { + name: "Given context, new vertex and old vertex object " + + "When vertex generation check is enabled and the old and new vertex generation is equal " + + "Then func should just return true", + vertexNew: &v1alpha1.Vertex{ + ObjectMeta: v1.ObjectMeta{ + Generation: 2, + }, + }, + vertexOld: &v1alpha1.Vertex{ + ObjectMeta: v1.ObjectMeta{ + Generation: 2, + }, + }, + enableGenerationCheck: true, + expectedError: nil, + expectedValue: true, + }, + } + + ctxLogger := log.WithFields(log.Fields{ + "txId": "abc", + }) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + admiralParams.EnableGenerationCheck = tc.enableGenerationCheck + common.ResetSync() + common.InitializeConfig(admiralParams) + actual, err := dc.DoesGenerationMatch(ctxLogger, tc.vertexNew, tc.vertexOld) + if !ErrorEqualOrSimilar(err, tc.expectedError) { + t.Errorf("expected: %v, got: %v", tc.expectedError, err) + } + if err == nil { + if tc.expectedValue != actual { + t.Errorf("expected: %v, got: %v", tc.expectedValue, actual) + } + } + }) + } + +} + +func TestNewVertexController(t *testing.T) { + config, err := clientcmd.BuildConfigFromFlags("", "../../test/resources/admins@fake-cluster.k8s.local") + if err != nil { + t.Errorf("%v", err) + } + stop := make(chan struct{}) + vertexHandler := test.MockClientDiscoveryHandler{} + + vertexCon, _ := NewVertexController(stop, &vertexHandler, config, 0, loader.GetFakeClientLoader()) + + if vertexCon == nil { + t.Errorf("Vertex controller should not be nil") + } +} + +func TestVertexUpdateProcessItemStatus(t *testing.T) { + common.ResetSync() + admiralParams := common.AdmiralParams{ + LabelSet: &common.LabelSet{ + WorkloadIdentityKey: "identity", + EnvKey: "admiral.io/env", + AdmiralCRDIdentityLabel: "identity", + }, + } + common.InitializeConfig(admiralParams) + vertexInCache := getVertex("namespace-1", map[string]string{}, map[string]string{"identity": "vertex1", "env": "prd"}) + vertexInCache.Name = "vertex1" + vertexInCache2 := getVertex("namespace-2", map[string]string{}, map[string]string{"identity": "vertex2", "env": "prd"}) + vertexInCache.Name = "vertex2" + vertexNotInCache := getVertex("namespace-3", map[string]string{}, map[string]string{"identity": "vertex3", "env": "prd"}) + vertexInCache.Name = "vertex2" + var ( + serviceAccount = &coreV1.ServiceAccount{} + ) + + // Populating the vertex Cache + vertexCache := &vertexCache{ + cache: make(map[string]*VertexEntry), + mutex: &sync.Mutex{}, + } + + vertexController := &VertexController{ + Cache: vertexCache, + } + + vertexCache.Put(getK8sObjectFromVertex(vertexInCache)) + vertexCache.Put(getK8sObjectFromVertex(vertexInCache2)) + + testCases := []struct { + name string + obj interface{} + statusToSet string + expectedErr error + expectedStatus string + }{ + { + name: "Given vertex cache has a valid vertex in its cache, " + + "And is processed" + + "Then, the status for the valid vertex should be updated to processed", + obj: vertexInCache, + statusToSet: common.Processed, + expectedErr: nil, + expectedStatus: common.Processed, + }, + { + name: "Given vertex cache has a valid vertex in its cache, " + + "And is processed" + + "Then, the status for the valid vertex should be updated to not processed", + obj: vertexInCache2, + statusToSet: common.NotProcessed, + expectedErr: nil, + expectedStatus: common.NotProcessed, + }, + { + name: "Given vertex cache does not has a valid vertex in its cache, " + + "Then, the status for the valid vertex should be not processed, " + + "And an error should be returned with the vertex not found message", + obj: vertexNotInCache, + statusToSet: common.NotProcessed, + expectedErr: fmt.Errorf(LogCacheFormat, "UpdateStatus", "Vertex", vertexNotInCache.Name, vertexNotInCache.Namespace, "", "nothing to update, vertex not found in cache"), + expectedStatus: common.NotProcessed, + }, + { + name: "Given ServiceAccount is passed to the function, " + + "Then, the function should not panic, " + + "And return an error", + obj: serviceAccount, + expectedErr: fmt.Errorf("type assertion failed"), + expectedStatus: common.NotProcessed, + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + err := vertexController.UpdateProcessItemStatus(c.obj, c.statusToSet) + if !ErrorEqualOrSimilar(err, c.expectedErr) { + t.Errorf("expected: %v, got: %v", c.expectedErr, err) + } + status, _ := vertexController.GetProcessItemStatus(c.obj) + assert.Equal(t, c.expectedStatus, status) + }) + } +} + +func TestVertexGetProcessItemStatus(t *testing.T) { + common.ResetSync() + admiralParams := common.AdmiralParams{ + LabelSet: &common.LabelSet{ + WorkloadIdentityKey: "identity", + EnvKey: "admiral.io/env", + AdmiralCRDIdentityLabel: "identity", + }, + } + common.InitializeConfig(admiralParams) + var ( + serviceAccount = &coreV1.ServiceAccount{} + ) + vertexInCache := getVertex("namespace-1", map[string]string{}, map[string]string{"identity": "vertex1"}) + vertexInCache.Name = "debug-1" + vertexNotInCache := getVertex("namespace-2", map[string]string{}, map[string]string{"identity": "vertex2"}) + vertexNotInCache.Name = "debug-2" + + // Populating the vertex Cache + vertexCache := &vertexCache{ + cache: make(map[string]*VertexEntry), + mutex: &sync.Mutex{}, + } + + vertexController := &VertexController{ + Cache: vertexCache, + } + + vertexCache.Put(getK8sObjectFromVertex(vertexInCache)) + vertexCache.UpdateVertexProcessStatus(vertexInCache, common.Processed) + + testCases := []struct { + name string + obj interface{} + expectedErr error + expectedResult string + }{ + { + name: "Given vertex cache has a valid vertex in its cache, " + + "And is processed" + + "Then, we should be able to get the status as processed", + obj: vertexInCache, + expectedResult: common.Processed, + }, + { + name: "Given vertex cache does not has a valid vertex in its cache, " + + "Then, the status for the valid vertex should not be updated", + obj: vertexNotInCache, + expectedResult: common.NotProcessed, + }, + { + name: "Given ServiceAccount is passed to the function, " + + "Then, the function should not panic, " + + "And return an error", + obj: serviceAccount, + expectedErr: fmt.Errorf("type assertion failed"), + expectedResult: common.NotProcessed, + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + res, err := vertexController.GetProcessItemStatus(c.obj) + if !ErrorEqualOrSimilar(err, c.expectedErr) { + t.Errorf("expected: %v, got: %v", c.expectedErr, err) + } + assert.Equal(t, c.expectedResult, res) + }) + } +} + +func TestVertexLogValueOfAdmiralIoIgnore(t *testing.T) { + // Test case 1: obj is not a Vertex object + d := &VertexController{} + d.LogValueOfAdmiralIoIgnore("not a vertex") + // No error should occur + + // Test case 2: K8sClient is nil + d = &VertexController{} + d.LogValueOfAdmiralIoIgnore(&v1alpha1.Vertex{}) + // No error should occur + + d.LogValueOfAdmiralIoIgnore(&v1alpha1.Vertex{ObjectMeta: metav1.ObjectMeta{Namespace: "test-ns"}}) + // No error should occur + + // Test case 3: AdmiralIgnoreAnnotation is set in Vertex object + d = &VertexController{} + vertex := &v1alpha1.Vertex{ + Spec: v1alpha1.VertexSpec{ + AbstractVertex: v1alpha1.AbstractVertex{ + AbstractPodTemplate: v1alpha1.AbstractPodTemplate{ + Metadata: &v1alpha1.Metadata{ + Annotations: map[string]string{ + common.AdmiralIgnoreAnnotation: "true", + }, + }, + }, + }, + }, + } + d.LogValueOfAdmiralIoIgnore(vertex) + // No error should occur +} diff --git a/admiral/pkg/controller/common/common.go b/admiral/pkg/controller/common/common.go index 758a83ad..0cafe6ef 100644 --- a/admiral/pkg/controller/common/common.go +++ b/admiral/pkg/controller/common/common.go @@ -92,6 +92,9 @@ const ( MeshService = "MESH_SERVICE" Deployment = "deployment" Rollout = "rollout" + Job = "job" + Vertex = "vertex" + MonoVertex = "monovertex" GTP = "gtp" EventType = "eventType" ProcessingInProgress = "ProcessingInProgress" @@ -135,6 +138,7 @@ const ( ConfigMapResourceType ResourceType = "ConfigMap" SecretResourceType ResourceType = "Secret" NodeResourceType ResourceType = "Node" + JobResourceType ResourceType = "Job" // Admiral Resource Types DependencyResourceType ResourceType = "Dependency" @@ -703,6 +707,56 @@ func GetMeshPortsHelper(meshPorts string, destService *k8sV1.Service, clusterNam return ports } +func ShouldIgnore(annotations map[string]string, labels map[string]string) bool { + labelSet := GetLabelSet() + + //if admiral ignore label set + if labels[labelSet.AdmiralIgnoreLabel] == "true" { //if we should ignore, do that and who cares what else is there + return true + } + + //if sidecar not injected + if annotations[labelSet.DeploymentAnnotation] != "true" { //Not sidecar injected, we don't want to inject + return true + } + + if annotations[AdmiralIgnoreAnnotation] == "true" { + return true + } + + return false //labels are fine, we should not ignore +} + +func GetIdentityPartition(annotations map[string]string, labels map[string]string) string { + identityPartition := annotations[GetPartitionIdentifier()] + if len(identityPartition) == 0 { + //In case partition is accidentally applied as Label + identityPartition = labels[GetPartitionIdentifier()] + } + return identityPartition +} + +func GetGlobalIdentifier(annotations map[string]string, labels map[string]string) string { + identity := labels[GetWorkloadIdentifier()] + if len(identity) == 0 { + //TODO can this be removed now? This was for backward compatibility + identity = annotations[GetWorkloadIdentifier()] + } + if EnableSWAwareNSCaches() && len(identity) > 0 && len(GetIdentityPartition(annotations, labels)) > 0 { + identity = GetIdentityPartition(annotations, labels) + Sep + strings.ToLower(identity) + } + return identity +} + +func GetOriginalIdentifier(annotations map[string]string, labels map[string]string) string { + identity := labels[GetWorkloadIdentifier()] + if len(identity) == 0 { + //TODO can this be removed now? This was for backward compatibility + identity = annotations[GetWorkloadIdentifier()] + } + return identity +} + func GenerateUniqueNameForVS(originNamespace string, vsName string) string { if originNamespace == "" && vsName == "" { diff --git a/admiral/pkg/controller/common/common_test.go b/admiral/pkg/controller/common/common_test.go index c292edb6..e6002e83 100644 --- a/admiral/pkg/controller/common/common_test.go +++ b/admiral/pkg/controller/common/common_test.go @@ -58,6 +58,8 @@ func initConfig(fqdn bool, fqdnLocal bool) { p.LabelSet.AdmiralCRDIdentityLabel = "identity" p.LabelSet.EnvKey = "admiral.io/env" p.LabelSet.IdentityPartitionKey = "admiral.io/identityPartition" + p.LabelSet.DeploymentAnnotation = "sidecar.istio.io/inject" + p.LabelSet.AdmiralIgnoreLabel = "admiral.io/ignore" InitializeConfig(p) } @@ -1183,6 +1185,188 @@ func TestGetGtpIdentityPartition(t *testing.T) { } } +func TestShouldIgnore(t *testing.T) { + initConfig(true, true) + + testCases := []struct { + name string + annotations map[string]string + labels map[string]string + expected bool + }{ + { + name: "Given valid admiral ignore label " + + "Should ignore the object ", + annotations: map[string]string{"sidecar.istio.io/inject": "true"}, + labels: map[string]string{"admiral.io/ignore": "true", "app": "app"}, + expected: true, + }, + { + name: "Given istio injection is not enabled " + + "Should ignore the object ", + annotations: map[string]string{}, + labels: map[string]string{"app": "app"}, + expected: true, + }, + { + name: "Given valid admiral ignore annotation " + + "Should ignore the object ", + annotations: map[string]string{"admiral.io/ignore": "true", "sidecar.istio.io/inject": "true"}, + labels: map[string]string{"app": "app"}, + expected: true, + }, + { + name: "Given no admiral ignore set " + + "Should not ignore the object ", + annotations: map[string]string{"sidecar.istio.io/inject": "true"}, + labels: map[string]string{"app": "app"}, + expected: false, + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + iVal := ShouldIgnore(c.annotations, c.labels) + if !(iVal == c.expected) { + t.Errorf("Wanted value: %v, got: %v", c.expected, iVal) + } + }) + } +} + +func TestGetIdentityPartition(t *testing.T) { + initConfig(true, true) + + testCases := []struct { + name string + annotations map[string]string + labels map[string]string + expected string + }{ + { + name: "Given valid identity partition on annotations " + + "Should return identity partition ", + annotations: map[string]string{"admiral.io/identityPartition": "partition1"}, + labels: map[string]string{"app": "app"}, + expected: "partition1", + }, + { + name: "Given valid identity partition on labels " + + "Should return identity partition ", + annotations: map[string]string{}, + labels: map[string]string{"app": "app", "admiral.io/identityPartition": "partition2"}, + expected: "partition2", + }, + { + name: "Given no valid identity partition present on labels or annotations " + + "Should return empty string ", + annotations: map[string]string{}, + labels: map[string]string{"app": "app"}, + expected: "", + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + iVal := GetIdentityPartition(c.annotations, c.labels) + if !(iVal == c.expected) { + t.Errorf("Wanted value: %v, got: %v", c.expected, iVal) + } + }) + } +} + +func TestGetGlobalIdentifier(t *testing.T) { + initConfig(true, true) + + testCases := []struct { + name string + annotations map[string]string + labels map[string]string + expected string + }{ + { + name: "Given valid identity partition on annotations and valid identity" + + "Should return global identifier with identity partition ", + annotations: map[string]string{"admiral.io/identityPartition": "partition1"}, + labels: map[string]string{"app": "app", "identity": "identity1"}, + expected: "partition1.identity1", + }, + { + name: "Given valid identity partition on labels and valid identity " + + "Should return global identifier with identity partition ", + annotations: map[string]string{}, + labels: map[string]string{"app": "app", "admiral.io/identityPartition": "partition2", "identity": "identity2"}, + expected: "partition2.identity2", + }, + { + name: "Given no valid identity partition present on labels or annotations and valid identity " + + "Should return identity string ", + annotations: map[string]string{}, + labels: map[string]string{"app": "app", "identity": "identity3"}, + expected: "identity3", + }, + { + name: "Given no valid identity partition and no valid identity " + + "Should empty string ", + annotations: map[string]string{}, + labels: map[string]string{"app": "app"}, + expected: "", + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + iVal := GetGlobalIdentifier(c.annotations, c.labels) + if !(iVal == c.expected) { + t.Errorf("Wanted value: %v, got: %v", c.expected, iVal) + } + }) + } +} + +func TestGetOriginalIdentifier(t *testing.T) { + initConfig(true, true) + + testCases := []struct { + name string + annotations map[string]string + labels map[string]string + expected string + }{ + { + name: "Given valid identity on annotations " + + "Should return identity ", + annotations: map[string]string{"identity": "identity1"}, + labels: map[string]string{"app": "app"}, + expected: "identity1", + }, + { + name: "Given valid identity on labels " + + "Should return identity ", + annotations: map[string]string{}, + labels: map[string]string{"app": "app", "identity": "identity2"}, + expected: "identity2", + }, + { + name: "Given no valid identity on labels or annotations " + + "Should return empty identity ", + annotations: map[string]string{}, + labels: map[string]string{"app": "app"}, + expected: "", + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + iVal := GetOriginalIdentifier(c.annotations, c.labels) + if !(iVal == c.expected) { + t.Errorf("Wanted value: %v, got: %v", c.expected, iVal) + } + }) + } +} + func TestGenerateUniqueNameForVS(t *testing.T) { initConfig(true, true) diff --git a/admiral/pkg/controller/common/config.go b/admiral/pkg/controller/common/config.go index a5544d1d..ddabcef7 100644 --- a/admiral/pkg/controller/common/config.go +++ b/admiral/pkg/controller/common/config.go @@ -482,6 +482,24 @@ func GetExportToMaxNamespaces() int { return wrapper.params.ExportToMaxNamespaces } +func IsClientDiscoveryEnabled() bool { + wrapper.RLock() + defer wrapper.RUnlock() + return wrapper.params.EnableClientDiscovery +} + +func GetClientDiscoveryClustersForJobs() []string { + wrapper.RLock() + defer wrapper.RUnlock() + return wrapper.params.ClientDiscoveryClustersForJobs +} + +func GetClientDiscoveryClustersForNumaflow() []string { + wrapper.RLock() + defer wrapper.RUnlock() + return wrapper.params.DiscoveryClustersForNumaflow +} + func IsAdmiralStateSyncerMode() bool { wrapper.RLock() defer wrapper.RUnlock() diff --git a/admiral/pkg/controller/common/config_test.go b/admiral/pkg/controller/common/config_test.go index 554b3590..423c5139 100644 --- a/admiral/pkg/controller/common/config_test.go +++ b/admiral/pkg/controller/common/config_test.go @@ -22,28 +22,31 @@ func setupForConfigTests() { AdmiralCRDIdentityLabel: "identity", IdentityPartitionKey: "admiral.io/identityPartition", }, - EnableSAN: true, - SANPrefix: "prefix", - HostnameSuffix: "mesh", - SyncNamespace: "admiral-sync", - SecretFilterTags: "admiral/sync", - CacheReconcileDuration: time.Minute, - ClusterRegistriesNamespace: "default", - DependenciesNamespace: "default", - Profile: "default", - WorkloadSidecarName: "default", - WorkloadSidecarUpdate: "disabled", - MetricsEnabled: true, - DeprecatedEnvoyFilterVersion: "1.10,1.17", - EnvoyFilterVersion: "1.10,1.13,1.17", - CartographerFeatures: map[string]string{"throttle_filter_gen": "disabled"}, - DisableIPGeneration: false, - EnableSWAwareNSCaches: true, - ExportToIdentityList: []string{"*"}, - ExportToMaxNamespaces: 35, - AdmiralOperatorMode: false, - OperatorSyncNamespace: "admiral-sync", - OperatorSecretFilterTags: "admiral/syncoperator", + EnableSAN: true, + SANPrefix: "prefix", + HostnameSuffix: "mesh", + SyncNamespace: "admiral-sync", + SecretFilterTags: "admiral/sync", + CacheReconcileDuration: time.Minute, + ClusterRegistriesNamespace: "default", + DependenciesNamespace: "default", + Profile: "default", + WorkloadSidecarName: "default", + WorkloadSidecarUpdate: "disabled", + MetricsEnabled: true, + DeprecatedEnvoyFilterVersion: "1.10,1.17", + EnvoyFilterVersion: "1.10,1.13,1.17", + CartographerFeatures: map[string]string{"throttle_filter_gen": "disabled"}, + DisableIPGeneration: false, + EnableSWAwareNSCaches: true, + ExportToIdentityList: []string{"*"}, + ExportToMaxNamespaces: 35, + AdmiralOperatorMode: false, + OperatorSyncNamespace: "admiral-sync", + OperatorSecretFilterTags: "admiral/syncoperator", + DiscoveryClustersForNumaflow: make([]string, 0), + ClientDiscoveryClustersForJobs: make([]string, 0), + EnableClientDiscovery: true, } ResetSync() initHappened = true @@ -237,6 +240,18 @@ func TestConfigManagement(t *testing.T) { if GetOperatorSecretFilterTags() != "admiral/syncoperator" { t.Errorf("operator secret filter tags mismatch, expected admiral/syncoperator, got %s", GetOperatorSecretFilterTags()) } + + if IsClientDiscoveryEnabled() != true { + t.Errorf("client discovery enabled mismatch, expected true, got %v", IsClientDiscoveryEnabled()) + } + + if len(GetClientDiscoveryClustersForJobs()) != 0 { + t.Errorf("clusters for jobs client discovery mismatch, expected 0, got %v", GetClientDiscoveryClustersForJobs()) + } + + if len(GetClientDiscoveryClustersForNumaflow()) != 0 { + t.Errorf("clusters for numaflow client discovery mismatch, expected 0, got %v", GetClientDiscoveryClustersForNumaflow()) + } } func TestGetCRDIdentityLabelWithCRDIdentity(t *testing.T) { diff --git a/admiral/pkg/controller/common/types.go b/admiral/pkg/controller/common/types.go index 56cbe599..d784f185 100644 --- a/admiral/pkg/controller/common/types.go +++ b/admiral/pkg/controller/common/types.go @@ -126,6 +126,11 @@ type AdmiralParams struct { IngressVSExportToNamespaces []string IngressLBPolicy string VSRoutingEnabledClusters []string + + //Client discovery (types requiring mesh egress only) + EnableClientDiscovery bool + ClientDiscoveryClustersForJobs []string + DiscoveryClustersForNumaflow []string } func (b AdmiralParams) String() string { @@ -458,3 +463,12 @@ func (s *ProxiedServiceInfo) String() string { func (s *ProxiedServiceEnvironment) String() string { return fmt.Sprintf("{Environment:%s, DnsName: %s, CNames: %s}", s.Environment, s.DnsName, s.CNames) } + +type K8sObject struct { + Name string + Namespace string + Type string + Annotations map[string]string + Labels map[string]string + Status string +} diff --git a/admiral/pkg/test/mock.go b/admiral/pkg/test/mock.go index 0d77b5db..95ee2b51 100644 --- a/admiral/pkg/test/mock.go +++ b/admiral/pkg/test/mock.go @@ -5,6 +5,7 @@ import ( "errors" admiralapiv1 "github.com/istio-ecosystem/admiral-api/pkg/apis/admiral/v1" admiralapi "github.com/istio-ecosystem/admiral-api/pkg/client/clientset/versioned" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" argoprojv1alpha1 "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned/typed/rollouts/v1alpha1" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -425,3 +426,10 @@ func (m *MockShardHandler) Added(ctx context.Context, obj *admiralapiv1.Shard, c func (m *MockShardHandler) Deleted(ctx context.Context, obj *admiralapiv1.Shard) error { return nil } + +type MockClientDiscoveryHandler struct { +} + +func (m *MockClientDiscoveryHandler) Added(ctx context.Context, obj *common.K8sObject) error { + return nil +} diff --git a/go.mod b/go.mod index 2225d6f2..19e8bed3 100644 --- a/go.mod +++ b/go.mod @@ -1,25 +1,26 @@ module github.com/istio-ecosystem/admiral -go 1.21.7 +go 1.22 -toolchain go1.21.11 +toolchain go1.23.0 require ( github.com/argoproj/argo-rollouts v1.2.1 github.com/cenkalti/backoff v2.2.1+incompatible - github.com/go-openapi/swag v0.22.9 // indirect - github.com/golang/protobuf v1.5.3 + github.com/go-openapi/swag v0.23.0 // indirect + github.com/golang/protobuf v1.5.4 github.com/google/go-cmp v0.6.0 github.com/gorilla/mux v1.8.0 - github.com/imdario/mergo v0.3.12 // indirect + github.com/imdario/mergo v0.3.16 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/numaproj/numaflow v1.3.1 github.com/onsi/gomega v1.30.0 github.com/prometheus/client_golang v1.19.1 github.com/prometheus/client_model v0.6.1 // indirect github.com/sirupsen/logrus v1.8.1 - github.com/spf13/cobra v1.5.0 + github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.9.0 - golang.org/x/net v0.21.0 // indirect + golang.org/x/net v0.25.0 // indirect golang.org/x/time v0.5.0 // indirect gopkg.in/yaml.v2 v2.4.0 istio.io/api v1.19.6 @@ -38,20 +39,20 @@ require ( go.opentelemetry.io/otel/exporters/prometheus v0.49.0 go.opentelemetry.io/otel/metric v1.27.0 go.opentelemetry.io/otel/sdk/metric v1.27.0 - google.golang.org/protobuf v1.34.1 + google.golang.org/protobuf v1.34.2 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) require ( github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/gnostic-models v0.6.8 // indirect github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect go.opentelemetry.io/otel/sdk v1.27.0 // indirect go.opentelemetry.io/otel/trace v1.27.0 // indirect - google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13 // indirect + google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect ) require ( @@ -59,17 +60,16 @@ require ( github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.2 // indirect github.com/evanphx/json-patch v5.9.0+incompatible // indirect github.com/go-logr/logr v1.4.1 // indirect - github.com/go-openapi/jsonpointer v0.20.2 // indirect - github.com/go-openapi/jsonreference v0.20.4 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/gnostic v0.6.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.6.0 - github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/matryer/resync v0.0.0-20161211202428-d39c09a11215 @@ -77,16 +77,15 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pkg/errors v0.9.1 + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/procfs v0.15.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.2 // indirect - golang.org/x/oauth2 v0.17.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/term v0.17.0 // indirect - golang.org/x/text v0.14.0 // indirect - google.golang.org/appengine v1.6.8 // indirect + golang.org/x/oauth2 v0.20.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/term v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect @@ -101,12 +100,9 @@ replace ( github.com/fsnotify/fsnotify => github.com/fsnotify/fsnotify v1.5.1 github.com/go-check/check => github.com/go-check/check v0.0.0-20180628173108-788fd7840127 github.com/prometheus/common => github.com/prometheus/common v0.26.0 - k8s.io/api => k8s.io/api v0.24.2 k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.24.2 - k8s.io/apimachinery => k8s.io/apimachinery v0.24.2 k8s.io/apiserver => k8s.io/apiserver v0.24.2 k8s.io/cli-runtime => k8s.io/cli-runtime v0.24.2 - k8s.io/client-go => k8s.io/client-go v0.24.2 k8s.io/cloud-provider => k8s.io/cloud-provider v0.24.2 k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.24.2 k8s.io/code-generator => k8s.io/code-generator v0.24.2 @@ -117,7 +113,6 @@ replace ( k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.24.2 k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.24.2 k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.24.2 - k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20230525220651-2546d827e515 k8s.io/kube-proxy => k8s.io/kube-proxy v0.24.2 k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.24.2 k8s.io/kubectl => k8s.io/kubectl v0.24.2 diff --git a/go.sum b/go.sum index 1cf8d5b8..ee890373 100644 --- a/go.sum +++ b/go.sum @@ -1,72 +1,16 @@ -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.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.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/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= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -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/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/argoproj/argo-rollouts v1.2.1 h1:4hSgKEqpQsZreZBv+XcLsB+oBaRGMVW19nMScx5ikIQ= github.com/argoproj/argo-rollouts v1.2.1/go.mod h1:ETmWr9Lysxr9SgbqalMMBdytBcDHUt9qulFoKJ9b9ZU= -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.55.2 h1:/2OFM8uFfK9e+cqHTw9YPrvTzIXT2XkFGXRM7WbJb7E= github.com/aws/aws-sdk-go v1.55.2/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= 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/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -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.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -75,163 +19,64 @@ github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgk github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +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/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+BtnqkLAU= github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logr/logr 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 v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= -github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= -github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= -github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-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/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/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/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/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/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= -github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= -github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.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.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/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-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -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/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -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/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/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/istio-ecosystem/admiral-api v1.1.0 h1:SLRgKRdZP31G0Q2uaYcVb3JxkjAbTxbSsze2N5ncapE= github.com/istio-ecosystem/admiral-api v1.1.0/go.mod h1:xB+G1v2H/cOxuR6koi/3kLHgF+oc3y905Lt12NCyMCI= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= @@ -241,28 +86,20 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC 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.10/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/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 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/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/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.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/matryer/resync v0.0.0-20161211202428-d39c09a11215 h1:hDa3vAq/Zo5gjfJ46XMsGFbH+hTizpR4fUzQCk2nxgk= @@ -270,46 +107,29 @@ github.com/matryer/resync v0.0.0-20161211202428-d39c09a11215/go.mod h1:LH+NgPY9A 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 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= 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/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-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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/numaproj/numaflow v1.3.1 h1:aVd0bOocLmdk24JHuot8yUXTIj5qzml3XCC2GYef0mM= +github.com/numaproj/numaflow v1.3.1/go.mod h1:dWTidSrhA9bGGt4ExVIGst1mM3xL2ZMwfPXQrCnbRaQ= github.com/onsi/ginkgo/v2 v2.14.0 h1:vSmGj2Z5YPb9JwCWT6z6ihcUvDhuXLc3sJiqd3jMKAY= github.com/onsi/ginkgo/v2 v2.14.0/go.mod h1:JkUdW7JkN0V6rFvsHcJ478egV3XH9NxpD27Hal/PhZw= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= -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.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= @@ -318,53 +138,26 @@ github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9 github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.15.0 h1:A82kmvXJq2jTu5YUhSGNlYoxh85zLnKgPz4bMZgI5Ek= github.com/prometheus/procfs v0.15.0/go.mod h1:Y0RJ/Y5g5wJpkTisOtqwDSo4HwhGmLB4VQSw2sQJLHk= -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.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/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.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= go.opentelemetry.io/otel/exporters/prometheus v0.49.0 h1:Er5I1g/YhfYv9Affk9nJLfH/+qCCVVg1f2R9AbJfqDQ= @@ -377,443 +170,106 @@ go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2N go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -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/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/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/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-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-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 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-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= -golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 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/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/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-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-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/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-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-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-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-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-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-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.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= -golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -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/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/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-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0= -google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= -google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13 h1:U7+wNaVuSTaUqNvK2+osJ9ejEZxbjHHk8F2b6Hpx0AE= -google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:RdyHbowztCGQySiCvQPgWQWgWhGnouTdCflKoDBt32U= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= 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.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= 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-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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 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-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= istio.io/api v1.19.6 h1:xG5EKIy66WvlOg+UvfjK9nRiQTeuAm38avzUkvrGep0= istio.io/api v1.19.6/go.mod h1:KstZe4bKbXouALUJ5PqpjNEhu5nj90HrDFitZfpNhlU= istio.io/client-go v1.14.0 h1:KKXMnxXx3U2866OP8FBYlJhjKdI3yIUQnt8L6hSzDHE= istio.io/client-go v1.14.0/go.mod h1:C7K0CKQlvY84yQKkZhxQbD1riqvnsgXJm3jF5GOmzNg= -k8s.io/api v0.24.2 h1:g518dPU/L7VRLxWfcadQn2OnsiGWVOadTLpdnqgY2OI= -k8s.io/api v0.24.2/go.mod h1:AHqbSkTm6YrQ0ObxjO3Pmp/ubFF/KuM7jU+3khoBsOg= -k8s.io/apimachinery v0.24.2 h1:5QlH9SL2C8KMcrNJPor+LbXVTaZRReml7svPEh4OKDM= -k8s.io/apimachinery v0.24.2/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= -k8s.io/client-go v0.24.2 h1:CoXFSf8if+bLEbinDqN9ePIDGzcLtqhfd6jpfnwGOFA= -k8s.io/client-go v0.24.2/go.mod h1:zg4Xaoo+umDsfCWr4fCnmLEtQXyCNXCvJuSsglNcV30= -k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -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.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A= +k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0= +k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8= +k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= +k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg= +k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA= k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20230525220651-2546d827e515 h1:OmK1d0WrkD3IPfkskvroRykOulHVHf0s0ZIFRjyt+UI= -k8s.io/kube-openapi v0.0.0-20230525220651-2546d827e515/go.mod h1:kzo02I3kQ4BTtEfVLaPbjvCkX97YqGve33wzlb3fofQ= -k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/kube-openapi v0.0.0-20240209001042-7a0d5b415232 h1:MMq4iF9pHuAz/9dLnHwBQKEoeigXClzs3MFh/seyqtA= +k8s.io/kube-openapi v0.0.0-20240209001042-7a0d5b415232/go.mod h1:Pa1PvrP7ACSkuX6I7KYomY6cmMA0Tx86waBhDUgoKPw= k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ= k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/install/admiral/base/deployments.yaml b/install/admiral/base/deployments.yaml index ce4d9abe..83777b48 100644 --- a/install/admiral/base/deployments.yaml +++ b/install/admiral/base/deployments.yaml @@ -36,6 +36,10 @@ spec: - --envoy_filter_version - "1.13" - --enable_routing_policy=true + - --exportto_identity_list + - "*" + - --enable_sw_aware_ns_caches=true + - --enable_dependency_processing=true image: docker.io/admiralproj/admiral:latest imagePullPolicy: Always livenessProbe: diff --git a/install/admiral/overlays/demosinglecluster/envconfig_values.yaml b/install/admiral/overlays/demosinglecluster/envconfig_values.yaml index f531d133..facd9ea8 100644 --- a/install/admiral/overlays/demosinglecluster/envconfig_values.yaml +++ b/install/admiral/overlays/demosinglecluster/envconfig_values.yaml @@ -23,4 +23,8 @@ spec: - --envoy_filter_version - "1.13" - --enable_routing_policy=true + - --exportto_identity_list + - "*" + - --enable_sw_aware_ns_caches=true + - --enable_dependency_processing=true name: admiral diff --git a/install/admiralremote/base/remote.yaml b/install/admiralremote/base/remote.yaml index 2eb356b6..ffff0d1a 100644 --- a/install/admiralremote/base/remote.yaml +++ b/install/admiralremote/base/remote.yaml @@ -27,8 +27,14 @@ rules: resources: ['globaltrafficpolicies', 'routingpolicies',"trafficonfigs", "outlierdetections"] verbs: [ "get", "list", "watch"] - apiGroups: ["argoproj.io"] - resources: ['rollouts'] + resources: ['rollouts', 'workflows'] verbs: [ "get", "list", "watch"] + - apiGroups: ['batch'] + resources: [ 'jobs', 'cronjobs'] + verbs: ['get', 'watch', 'list'] + - apiGroups: ['numaflow.numaproj.io'] + resources: [ 'vertices', 'monovertices'] + verbs: ['get', 'watch', 'list'] --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 diff --git a/install/sample/overlays/job/job.yaml b/install/sample/overlays/job/job.yaml new file mode 100644 index 00000000..ccba2095 --- /dev/null +++ b/install/sample/overlays/job/job.yaml @@ -0,0 +1,20 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: pi + labels: + identity: job +spec: + template: + metadata: + annotations: + sidecar.istio.io/inject: "true" + labels: + identity: job + spec: + containers: + - name: pi + image: perl:5.34.0 + command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] + restartPolicy: Never + backoffLimit: 4 \ No newline at end of file diff --git a/install/sample/overlays/job/kustomization.yaml b/install/sample/overlays/job/kustomization.yaml new file mode 100644 index 00000000..8b9e1d3e --- /dev/null +++ b/install/sample/overlays/job/kustomization.yaml @@ -0,0 +1,13 @@ +apiversion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: sample-job + +bases: + - ../../base + +patchesStrategicMerge: + - webapp.yaml + +resources: + - job.yaml \ No newline at end of file diff --git a/install/sample/overlays/job/webapp.yaml b/install/sample/overlays/job/webapp.yaml new file mode 100644 index 00000000..132e9ec0 --- /dev/null +++ b/install/sample/overlays/job/webapp.yaml @@ -0,0 +1,35 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: webapp + annotations: + #admiral.io/ignore: "true" #Uncommenting this line will cause admiral to ignore this deployment despite the fact that it's in the mesh +spec: + replicas: 1 + selector: + matchLabels: + app: webapp + template: + metadata: + annotations: + admiral.io/env: stage + sidecar.istio.io/inject: "true" + labels: + app: webapp + identity: jobwebapp + #admiral-ignore: "true" #Uncommenting this line will cause admiral to ignore this deployment despite the fact that it's in the mesh + spec: + serviceAccountName: webapp + containers: + - name: webapp + image: pstauffer/curl + command: ["/bin/sleep", "3650d"] + imagePullPolicy: IfNotPresent + resources: + requests: + cpu: "10m" + memory: "50Mi" + limits: + cpu: "20m" + memory: "75Mi" \ No newline at end of file diff --git a/install/sample/overlays/numaflow/kustomization.yaml b/install/sample/overlays/numaflow/kustomization.yaml new file mode 100644 index 00000000..fad6c590 --- /dev/null +++ b/install/sample/overlays/numaflow/kustomization.yaml @@ -0,0 +1,13 @@ +apiversion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: sample-monovertex + +bases: + - ../../base + +patchesStrategicMerge: + - webapp.yaml + +resources: + - monovertex.yaml \ No newline at end of file diff --git a/install/sample/overlays/numaflow/monovertex.yaml b/install/sample/overlays/numaflow/monovertex.yaml new file mode 100644 index 00000000..fdf43bf9 --- /dev/null +++ b/install/sample/overlays/numaflow/monovertex.yaml @@ -0,0 +1,22 @@ +apiVersion: numaflow.numaproj.io/v1alpha1 +kind: MonoVertex +metadata: + name: simple-mono-vertex +spec: + metadata: + annotations: + sidecar.istio.io/inject: "true" + labels: + identity: monovertex + source: + udsource: + container: + image: quay.io/numaio/numaflow-java/source-simple-source:stable + # transformer is an optional container to do any transformation to the incoming data before passing to the sink + transformer: + container: + image: quay.io/numaio/numaflow-rs/source-transformer-now:stable + sink: + udsink: + container: + image: quay.io/numaio/numaflow-java/simple-sink:stable \ No newline at end of file diff --git a/install/sample/overlays/numaflow/webapp.yaml b/install/sample/overlays/numaflow/webapp.yaml new file mode 100644 index 00000000..9591f21b --- /dev/null +++ b/install/sample/overlays/numaflow/webapp.yaml @@ -0,0 +1,35 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: webapp + annotations: + #admiral.io/ignore: "true" #Uncommenting this line will cause admiral to ignore this deployment despite the fact that it's in the mesh +spec: + replicas: 1 + selector: + matchLabels: + app: webapp + template: + metadata: + annotations: + admiral.io/env: stage + sidecar.istio.io/inject: "true" + labels: + app: webapp + identity: monovertexwebapp + #admiral-ignore: "true" #Uncommenting this line will cause admiral to ignore this deployment despite the fact that it's in the mesh + spec: + serviceAccountName: webapp + containers: + - name: webapp + image: pstauffer/curl + command: ["/bin/sleep", "3650d"] + imagePullPolicy: IfNotPresent + resources: + requests: + cpu: "10m" + memory: "50Mi" + limits: + cpu: "20m" + memory: "75Mi" \ No newline at end of file diff --git a/install/sample/sample_dep.yaml b/install/sample/sample_dep.yaml index 648b6619..2e4cd811 100644 --- a/install/sample/sample_dep.yaml +++ b/install/sample/sample_dep.yaml @@ -2,7 +2,7 @@ apiVersion: admiral.io/v1alpha1 kind: Dependency metadata: - name: dependency + name: dependency-webapp namespace: admiral spec: source: webapp @@ -29,11 +29,34 @@ spec: apiVersion: admiral.io/v1alpha1 kind: Dependency metadata: - name: dependency + name: dependency-greeting namespace: admiral spec: source: greeting #TODO this value is hard coded to identity identityLabel: identity destinations: - - httpbin \ No newline at end of file + - httpbin +--- +apiVersion: admiral.io/v1alpha1 +kind: Dependency +metadata: + name: dependency-job + namespace: admiral +spec: + source: job + identityLabel: identity + destinations: + - greeting + +--- +apiVersion: admiral.io/v1alpha1 +kind: Dependency +metadata: + name: dependency-monovertex + namespace: admiral +spec: + source: monovertex + identityLabel: identity + destinations: + - greeting \ No newline at end of file diff --git a/install/scripts/install_numaflow.sh b/install/scripts/install_numaflow.sh new file mode 100755 index 00000000..75e8373e --- /dev/null +++ b/install/scripts/install_numaflow.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +if [ -z "$KUBECONFIG" ] +then + echo "\$KUBECONFIG is not set" + exit 1 +elif [[ $KUBECONFIG == *"ppd"* || $KUBECONFIG == *"prd"* || $KUBECONFIG == *"prod"* ]] +then + echo "\$KUBECONFIG is not for the dev cluster" + exit 1 +fi + +echo "**********Installing numaflow *********" +kubectl create ns numaflow-system +kubectl apply -n numaflow-system -f https://raw.githubusercontent.com/numaproj/numaflow/stable/config/install.yaml +kubectl apply -f https://raw.githubusercontent.com/numaproj/numaflow/stable/examples/0-isbsvc-jetstream.yaml +echo "****Numaflow installed*******" diff --git a/install/scripts/install_sample_services.sh b/install/scripts/install_sample_services.sh index 807dbbb9..a97a12a1 100755 --- a/install/scripts/install_sample_services.sh +++ b/install/scripts/install_sample_services.sh @@ -15,6 +15,10 @@ then exit 1 fi +#Install the dependency CR + +kubectl apply -f $install_dir/yaml/sample_dep.yaml + #Install test services kubectl apply -f $install_dir/yaml/sample.yaml @@ -23,10 +27,8 @@ kubectl apply -f $install_dir/yaml/gtp_failover.yaml kubectl apply -f $install_dir/yaml/sample-greeting-rollout-bluegreen.yaml kubectl apply -f $install_dir/yaml/sample-greeting-rollout-canary.yaml kubectl apply -f $install_dir/yaml/grpc.yaml - -#Install the dependency CR - -kubectl apply -f $install_dir/yaml/sample_dep.yaml +kubectl apply -f $install_dir/yaml/job_sample.yaml +kubectl apply -f $install_dir/yaml/numaflow_sample.yaml #Install the dependencyProxy resource kubectl apply -f $install_dir/yaml/depProxyExample.yaml @@ -85,26 +87,3 @@ for identity in webapp greeting greeting.canary greeting.bluegreen grpc-server s exit 1 fi done - -#Verify that admiral created proxy virtualservice -checkproxyvs() { - identity=$1 - num_ses=$(kubectl get vs -n admiral-sync | grep $1 -c) - - if [ -z "$num_ses" ] || [ $num_ses -lt 1 ] - then - echo "No proxy virtualservice created for $identity workload" - return 1; - else - echo "Admiral created proxy virtualservice for $identity workload" - return 0 - fi -} -export -f checkproxyvs -for identity in stage.httpbin.foo-vs qal.httpbin.foo-vs; do - timeout 180s bash -c "until checkproxyvs $identity; do sleep 10; done" - if [[ $? -eq 124 ]] - then - exit 1 - fi -done diff --git a/tests/client_discovery_test.sh b/tests/client_discovery_test.sh new file mode 100755 index 00000000..33f54094 --- /dev/null +++ b/tests/client_discovery_test.sh @@ -0,0 +1,27 @@ +#!/bin/bash -e + +## This test verifies if the Client Discovery (Mesh egress works as expected) works +## Validates the mesh endpoints for greeting are exported to job and numaflow types as mesh clients + +#Test +output=$(kubectl get se stage.greeting.global-se -n admiral-sync -o yaml | grep sample-job) + +if [[ "$output" == *"sample-job"* ]]; then + echo "PASS" +else + echo "FAIL" + echo $output + exit 1 +fi + +output=$(kubectl get se stage.greeting.global-se -n admiral-sync -o yaml | grep sample-monovertex) + +if [[ "$output" == *"sample-monovertex"* ]]; then + echo "PASS" +else + echo "FAIL" + echo $output + exit 1 +fi + +exit 0 diff --git a/tests/install_istio.sh b/tests/install_istio.sh index c1d3e870..ceedca85 100755 --- a/tests/install_istio.sh +++ b/tests/install_istio.sh @@ -84,9 +84,6 @@ then echo "Istio version $istio_version is no longer officially supported by this version of Admiral" exit 1 #install istio core with DNS proxying enabled and multicluster enabled -elif [ "$os" == "osx-arm64" ]; then - # defalt image does not support iptable manipulation on apple silicon - https://stackoverflow.com/questions/72073613/istio-installation-failed-apple-silicon-m1 - "./istio-$istio_version/bin/istioctl" install -f cluster1.yaml --set hub=ghcr.io/resf/istio -y else "./istio-$istio_version/bin/istioctl" install -f cluster1.yaml -y fi diff --git a/tests/run.sh b/tests/run.sh index 3d8cee96..3de4e796 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -23,6 +23,7 @@ fi $install_dir/scripts/install_admiral.sh $install_dir $install_dir/scripts/install_rollouts.sh +$install_dir/scripts/install_numaflow.sh $install_dir/scripts/cluster-secret.sh $KUBECONFIG $KUBECONFIG admiral $install_dir/scripts/install_sample_services.sh $install_dir @@ -33,9 +34,6 @@ sleep 10 ./test5.sh "webapp" "sample-rollout-bluegreen" "greeting.bluegreen" ./test2.sh "webapp" "sample-rollout-canary" "greeting.canary" -# Test Proxy VirtualService Call -./test7.sh "webapp" "sample" "stage.httpbin.foo" - #cleanup to fee up the pipeline minkube resources if [[ $IS_LOCAL == "false" ]]; then kubectl scale --replicas=0 deploy webapp -n sample @@ -49,4 +47,6 @@ fi # Testing routing policy ./test6.sh "sample" "1.13" $install_dir +./client_discovery_test.sh + ./cleanup.sh $istio_version \ No newline at end of file diff --git a/tests/test7.sh b/tests/test7.sh deleted file mode 100755 index fe0b78d2..00000000 --- a/tests/test7.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -e - -## This test verifies if the DependencyProxy created for the httpbin service -## to proxy the request through a "proxy" mesh service. -## The curl should validate httpbin external service is reachable -## through the proxy's virtualservice host - -## Request Flow: -## GET http://stage.httpbin.foo/api/uuid => -## Istio intercepts and routes to VS host stage.httpbin.foo => -## VS host stage.httpbin.foo has a destination of SE host stage.httpbin.global:80 => -## SE host stage.httpbin.global:80 forwards to proxy.svc.cluster.local:80 => -## proxy.svc.cluster.local:80 which is an nginx gateway proxy, forwards the request to https://httpbin.org/uuid - -[ $# -lt 3 ] && { echo "Usage: $0 " ; exit 1; } - -source=$1 -source_ns=$2 -dest=$3 - -#Test -output=$(kubectl exec --namespace=$source_ns -it $(kubectl get pod -l "app=$source" --namespace=$source_ns -o jsonpath='{.items[0].metadata.name}') -c $source -- curl -v --resolve $dest:80:240.0.0.1 "http://$dest/api/uuid" && echo "") - -if [[ "$output" == *"uuid"* ]]; then - echo "PASS" - exit 0 -else - echo "FAIL" - echo $output - exit 1 -fi