diff --git a/admiral/pkg/apis/admiral/routes/handlers.go b/admiral/pkg/apis/admiral/routes/handlers.go index 29e4ce4d..8fb9a135 100644 --- a/admiral/pkg/apis/admiral/routes/handlers.go +++ b/admiral/pkg/apis/admiral/routes/handlers.go @@ -3,12 +3,14 @@ package routes import ( "encoding/json" "fmt" - "github.com/gorilla/mux" - "github.com/istio-ecosystem/admiral/admiral/pkg/clusters" - "istio.io/client-go/pkg/apis/networking/v1alpha3" "log" "net/http" "strings" + + "github.com/gorilla/mux" + "github.com/istio-ecosystem/admiral/admiral/pkg/clusters" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + "istio.io/client-go/pkg/apis/networking/v1alpha3" ) type RouteOpts struct { @@ -126,16 +128,18 @@ func (opts *RouteOpts) GetServiceEntriesByIdentity(w http.ResponseWriter, r *htt if identity != "" { - for cname, serviceCluster := range opts.RemoteRegistry.AdmiralCache.SeClusterCache.Map() { + m := opts.RemoteRegistry.AdmiralCache.SeClusterCache + + m.Range(func(cname string, serviceCluster *common.Map) { if strings.Contains(cname, identity) { var identityServiceEntry IdentityServiceEntry identityServiceEntry.Cname = cname - for _, clusterId := range serviceCluster.Map() { - identityServiceEntry.ClusterNames = append(identityServiceEntry.ClusterNames, clusterId) - } + serviceCluster.Range(func(k string, clusterID string) { + identityServiceEntry.ClusterNames = append(identityServiceEntry.ClusterNames, clusterID) + }) response = append(response, identityServiceEntry) } - } + }) out, err := json.Marshal(response) if err != nil { log.Printf("Failed to marshall response GetServiceEntriesByIdentity call") diff --git a/admiral/pkg/clusters/handler.go b/admiral/pkg/clusters/handler.go index 158eff1f..f6b608f3 100644 --- a/admiral/pkg/clusters/handler.go +++ b/admiral/pkg/clusters/handler.go @@ -3,10 +3,15 @@ package clusters import ( "bytes" "fmt" + "reflect" + "sort" + "strings" + "time" + argo "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" "github.com/gogo/protobuf/types" "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/model" - "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" + v1 "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" "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/controller/util" @@ -16,10 +21,6 @@ import ( k8sAppsV1 "k8s.io/api/apps/v1" k8sV1 "k8s.io/api/core/v1" v12 "k8s.io/apimachinery/pkg/apis/meta/v1" - "reflect" - "sort" - "strings" - "time" ) const ROLLOUT_POD_HASH_LABEL string = "rollouts-pod-template-hash" @@ -45,8 +46,8 @@ type SidecarHandler struct { } type WeightedService struct { - Weight int32 - Service *k8sV1.Service + Weight int32 + Service *k8sV1.Service } func updateIdentityDependencyCache(sourceIdentity string, identityDependencyCache *common.MapOfMaps, dr *v1.Dependency) { @@ -70,9 +71,9 @@ func getDestinationRule(host string, locality string, gtpTrafficPolicy *model.Tr processGtp = false } outlierDetection := &v1alpha32.OutlierDetection{ - BaseEjectionTime: &types.Duration{Seconds: 300}, + BaseEjectionTime: &types.Duration{Seconds: 300}, Consecutive_5XxErrors: &types.UInt32Value{Value: uint32(10)}, - Interval: &types.Duration{Seconds: 60}, + Interval: &types.Duration{Seconds: 60}, } if gtpTrafficPolicy != nil && processGtp { var loadBalancerSettings = &v1alpha32.LoadBalancerSettings{ @@ -108,28 +109,28 @@ func getDestinationRule(host string, locality string, gtpTrafficPolicy *model.Tr func (se *ServiceEntryHandler) Added(obj *v1alpha3.ServiceEntry) { if IgnoreIstioResource(obj.Spec.ExportTo, obj.Annotations, obj.Namespace) { - log.Infof(LogFormat, "Add", "ServiceEntry", obj.Name, se.ClusterID, "Skipping resource from namespace=" + obj.Namespace) + log.Infof(LogFormat, "Add", "ServiceEntry", obj.Name, se.ClusterID, "Skipping resource from namespace="+obj.Namespace) return } } func (se *ServiceEntryHandler) Updated(obj *v1alpha3.ServiceEntry) { if IgnoreIstioResource(obj.Spec.ExportTo, obj.Annotations, obj.Namespace) { - log.Infof(LogFormat, "Update", "ServiceEntry", obj.Name, se.ClusterID, "Skipping resource from namespace=" + obj.Namespace) + log.Infof(LogFormat, "Update", "ServiceEntry", obj.Name, se.ClusterID, "Skipping resource from namespace="+obj.Namespace) return } } func (se *ServiceEntryHandler) Deleted(obj *v1alpha3.ServiceEntry) { if IgnoreIstioResource(obj.Spec.ExportTo, obj.Annotations, obj.Namespace) { - log.Infof(LogFormat, "Delete", "ServiceEntry", obj.Name, se.ClusterID, "Skipping resource from namespace=" + obj.Namespace) + log.Infof(LogFormat, "Delete", "ServiceEntry", obj.Name, se.ClusterID, "Skipping resource from namespace="+obj.Namespace) return } } func (dh *DestinationRuleHandler) Added(obj *v1alpha3.DestinationRule) { if IgnoreIstioResource(obj.Spec.ExportTo, obj.Annotations, obj.Namespace) { - log.Infof(LogFormat, "Add", "DestinationRule", obj.Name, dh.ClusterID, "Skipping resource from namespace=" + obj.Namespace) + log.Infof(LogFormat, "Add", "DestinationRule", obj.Name, dh.ClusterID, "Skipping resource from namespace="+obj.Namespace) return } handleDestinationRuleEvent(obj, dh, common.Add, common.DestinationRule) @@ -137,7 +138,7 @@ func (dh *DestinationRuleHandler) Added(obj *v1alpha3.DestinationRule) { func (dh *DestinationRuleHandler) Updated(obj *v1alpha3.DestinationRule) { if IgnoreIstioResource(obj.Spec.ExportTo, obj.Annotations, obj.Namespace) { - log.Infof(LogFormat, "Update", "DestinationRule", obj.Name, dh.ClusterID, "Skipping resource from namespace=" + obj.Namespace) + log.Infof(LogFormat, "Update", "DestinationRule", obj.Name, dh.ClusterID, "Skipping resource from namespace="+obj.Namespace) return } handleDestinationRuleEvent(obj, dh, common.Update, common.DestinationRule) @@ -145,7 +146,7 @@ func (dh *DestinationRuleHandler) Updated(obj *v1alpha3.DestinationRule) { func (dh *DestinationRuleHandler) Deleted(obj *v1alpha3.DestinationRule) { if IgnoreIstioResource(obj.Spec.ExportTo, obj.Annotations, obj.Namespace) { - log.Infof(LogFormat, "Delete", "DestinationRule", obj.Name, dh.ClusterID, "Skipping resource from namespace=" + obj.Namespace) + log.Infof(LogFormat, "Delete", "DestinationRule", obj.Name, dh.ClusterID, "Skipping resource from namespace="+obj.Namespace) return } handleDestinationRuleEvent(obj, dh, common.Delete, common.DestinationRule) @@ -153,7 +154,7 @@ func (dh *DestinationRuleHandler) Deleted(obj *v1alpha3.DestinationRule) { func (vh *VirtualServiceHandler) Added(obj *v1alpha3.VirtualService) { if IgnoreIstioResource(obj.Spec.ExportTo, obj.Annotations, obj.Namespace) { - log.Infof(LogFormat, "Add", "VirtualService", obj.Name, vh.ClusterID, "Skipping resource from namespace=" + obj.Namespace) + log.Infof(LogFormat, "Add", "VirtualService", obj.Name, vh.ClusterID, "Skipping resource from namespace="+obj.Namespace) return } err := handleVirtualServiceEvent(obj, vh, common.Add, common.VirtualService) @@ -164,7 +165,7 @@ func (vh *VirtualServiceHandler) Added(obj *v1alpha3.VirtualService) { func (vh *VirtualServiceHandler) Updated(obj *v1alpha3.VirtualService) { if IgnoreIstioResource(obj.Spec.ExportTo, obj.Annotations, obj.Namespace) { - log.Infof(LogFormat, "Update", "VirtualService", obj.Name, vh.ClusterID, "Skipping resource from namespace=" + obj.Namespace) + log.Infof(LogFormat, "Update", "VirtualService", obj.Name, vh.ClusterID, "Skipping resource from namespace="+obj.Namespace) return } err := handleVirtualServiceEvent(obj, vh, common.Update, common.VirtualService) @@ -175,7 +176,7 @@ func (vh *VirtualServiceHandler) Updated(obj *v1alpha3.VirtualService) { func (vh *VirtualServiceHandler) Deleted(obj *v1alpha3.VirtualService) { if IgnoreIstioResource(obj.Spec.ExportTo, obj.Annotations, obj.Namespace) { - log.Infof(LogFormat, "Delete", "VirtualService", obj.Name, vh.ClusterID, "Skipping resource from namespace=" + obj.Namespace) + log.Infof(LogFormat, "Delete", "VirtualService", obj.Name, vh.ClusterID, "Skipping resource from namespace="+obj.Namespace) return } err := handleVirtualServiceEvent(obj, vh, common.Delete, common.VirtualService) @@ -192,7 +193,7 @@ func (dh *SidecarHandler) Deleted(obj *v1alpha3.Sidecar) {} func IgnoreIstioResource(exportTo []string, annotations map[string]string, namespace string) bool { - if len(annotations) > 0 && annotations[common.AdmiralIgnoreAnnotation] == "true" { + if len(annotations) > 0 && annotations[common.AdmiralIgnoreAnnotation] == "true" { return true } @@ -225,9 +226,9 @@ func handleDestinationRuleEvent(obj *v1alpha3.DestinationRule, dh *DestinationRu r := dh.RemoteRegistry - dependentClusters := r.AdmiralCache.CnameDependentClusterCache.Get(destinationRule.Host) + dependentClusters := r.AdmiralCache.CnameDependentClusterCache.Get(destinationRule.Host).Copy() - if dependentClusters != nil && len(dependentClusters.Map()) > 0 { + if len(dependentClusters) > 0 { log.Infof(LogFormat, "Event", "DestinationRule", obj.Name, clusterId, "Processing") @@ -240,7 +241,7 @@ func handleDestinationRuleEvent(obj *v1alpha3.DestinationRule, dh *DestinationRu allDependentClusters := make(map[string]string) - util.MapCopy(allDependentClusters, dependentClusters.Map()) + util.MapCopy(allDependentClusters, dependentClusters) allDependentClusters[clusterId] = clusterId @@ -416,7 +417,7 @@ func handleVirtualServiceEvent(obj *v1alpha3.VirtualService, vh *VirtualServiceH } //check if this virtual service is used by Argo rollouts for canary strategy, if so, update the corresponding SE with appropriate weights - if common.GetAdmiralParams().ArgoRolloutsEnabled { + if common.GetAdmiralParams().ArgoRolloutsEnabled { rollouts, err := vh.RemoteRegistry.RemoteControllers[clusterId].RolloutController.RolloutClient.Rollouts(obj.Namespace).List(v12.ListOptions{}) if err != nil { @@ -432,11 +433,11 @@ func handleVirtualServiceEvent(obj *v1alpha3.VirtualService, vh *VirtualServiceH } } - dependentClusters := r.AdmiralCache.CnameDependentClusterCache.Get(virtualService.Hosts[0]) + dependentClusters := r.AdmiralCache.CnameDependentClusterCache.Get(virtualService.Hosts[0]).Copy() - if dependentClusters != nil && len(dependentClusters.Map()) > 0 { + if len(dependentClusters) > 0 { - for _, dependentCluster := range dependentClusters.Map() { + for _, dependentCluster := range dependentClusters { rc := r.RemoteControllers[dependentCluster] @@ -480,7 +481,7 @@ func handleVirtualServiceEvent(obj *v1alpha3.VirtualService, vh *VirtualServiceH } return nil } else { - log.Infof(LogFormat,"Event", "VirtualService", obj.Name, clusterId, "No dependent clusters found") + log.Infof(LogFormat, "Event", "VirtualService", obj.Name, clusterId, "No dependent clusters found") } //copy the VirtualService `as is` if they are not generated by Admiral (not in CnameDependentClusterCache) @@ -510,7 +511,7 @@ func addUpdateVirtualService(obj *v1alpha3.VirtualService, exist *v1alpha3.Virtu if obj.Annotations == nil { obj.Annotations = map[string]string{} } - obj.Annotations["app.kubernetes.io/created-by"] = "admiral" + obj.Annotations["app.kubernetes.io/created-by"] = "admiral" if exist == nil || len(exist.Spec.Hosts) == 0 { obj.Namespace = namespace obj.ResourceVersion = "" @@ -537,20 +538,20 @@ func addUpdateServiceEntry(obj *v1alpha3.ServiceEntry, exist *v1alpha3.ServiceEn if obj.Annotations == nil { obj.Annotations = map[string]string{} } - obj.Annotations["app.kubernetes.io/created-by"] = "admiral" + obj.Annotations["app.kubernetes.io/created-by"] = "admiral" if exist == nil || exist.Spec.Hosts == nil { obj.Namespace = namespace obj.ResourceVersion = "" _, err = rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(namespace).Create(obj) op = "Add" - log.Infof(LogFormat + " SE=%s", op, "ServiceEntry", obj.Name, rc.ClusterID, "New SE", obj.Spec.String()) + log.Infof(LogFormat+" SE=%s", op, "ServiceEntry", obj.Name, rc.ClusterID, "New SE", obj.Spec.String()) } else { exist.Labels = obj.Labels exist.Annotations = obj.Annotations op = "Update" skipUpdate, diff := skipDestructiveUpdate(rc, obj, exist) if diff != "" { - log.Infof(LogFormat + " diff=%s", op, "ServiceEntry", obj.Name, rc.ClusterID, "Diff in update", diff) + log.Infof(LogFormat+" diff=%s", op, "ServiceEntry", obj.Name, rc.ClusterID, "Diff in update", diff) } if skipUpdate { log.Infof(LogFormat, op, "ServiceEntry", obj.Name, rc.ClusterID, "Update skipped as it was destructive during Admiral's bootup phase") @@ -573,7 +574,7 @@ func skipDestructiveUpdate(rc *RemoteController, new *v1alpha3.ServiceEntry, old skipDestructive = false destructive, diff := getServiceEntryDiff(new, old) //do not update SEs during bootup phase if they are destructive - if time.Since(rc.StartTime) < (2 * common.GetAdmiralParams().CacheRefreshDuration) && destructive { + if time.Since(rc.StartTime) < (2*common.GetAdmiralParams().CacheRefreshDuration) && destructive { skipDestructive = true } @@ -603,17 +604,17 @@ func getServiceEntryDiff(new *v1alpha3.ServiceEntry, old *v1alpha3.ServiceEntry) found[nEndpoint.Address] = "1" if !reflect.DeepEqual(val, nEndpoint) { destructive = true - buffer.WriteString(fmt.Sprintf(format, "endpoint", "Update", val.String(), nEndpoint.String())) + buffer.WriteString(fmt.Sprintf(format, "endpoint", "Update", val.String(), nEndpoint.String())) } } else { - buffer.WriteString(fmt.Sprintf(format, "endpoint", "Add", "", nEndpoint.String())) + buffer.WriteString(fmt.Sprintf(format, "endpoint", "Add", "", nEndpoint.String())) } } for key := range oldEndpointMap { if _, ok := found[key]; !ok { destructive = true - buffer.WriteString(fmt.Sprintf(format, "endpoint", "Delete", oldEndpointMap[key].String(), "")) + buffer.WriteString(fmt.Sprintf(format, "endpoint", "Delete", oldEndpointMap[key].String(), "")) } } @@ -638,7 +639,7 @@ func addUpdateDestinationRule(obj *v1alpha3.DestinationRule, exist *v1alpha3.Des if obj.Annotations == nil { obj.Annotations = map[string]string{} } - obj.Annotations["app.kubernetes.io/created-by"] = "admiral" + obj.Annotations["app.kubernetes.io/created-by"] = "admiral" if exist == nil || exist.Name == "" || exist.Spec.Host == "" { obj.Namespace = namespace obj.ResourceVersion = "" @@ -714,22 +715,24 @@ func getServiceForDeployment(rc *RemoteController, deployment *k8sAppsV1.Deploym return matchedService } -func getDependentClusters(dependents *common.Map, identityClusterCache *common.MapOfMaps, sourceServices map[string]*k8sV1.Service) map[string]string { +func getDependentClusters(dependents map[string]string, identityClusterCache *common.MapOfMaps, sourceServices map[string]*k8sV1.Service) map[string]string { var dependentClusters = make(map[string]string) - //TODO optimize this map construction - if dependents != nil { - for identity, clusters := range identityClusterCache.Map() { - for depIdentity := range dependents.Map() { - if identity == depIdentity { - for _, clusterId := range clusters.Map() { - _, ok := sourceServices[clusterId] - if !ok { - dependentClusters[clusterId] = clusterId - } - } - } - } + + if dependents == nil { + return dependentClusters + } + + for depIdentity := range dependents { + clusters := identityClusterCache.Get(depIdentity) + if clusters == nil { + continue } + clusters.Range(func(k string, clusterID string) { + _, ok := sourceServices[clusterID] + if !ok { + dependentClusters[clusterID] = clusterID + } + }) } return dependentClusters } @@ -761,7 +764,7 @@ func getServiceForRollout(rc *RemoteController, rollout *argo.Rollout) map[strin return nil } - var canaryService, stableService, virtualServiceRouteName string + var canaryService, stableService, virtualServiceRouteName string var istioCanaryWeights = make(map[string]int32) @@ -827,7 +830,6 @@ func getServiceForRollout(rc *RemoteController, rollout *argo.Rollout) map[strin var matchedServices = make(map[string]*WeightedService) - //if we have more than one matching service we will pick the first one, for this to be deterministic we sort services var servicesInNamespace = cachedService.Service[rollout.Namespace] diff --git a/admiral/pkg/clusters/handler_test.go b/admiral/pkg/clusters/handler_test.go index 8c97e573..0089be30 100644 --- a/admiral/pkg/clusters/handler_test.go +++ b/admiral/pkg/clusters/handler_test.go @@ -1,6 +1,11 @@ package clusters import ( + "reflect" + "strings" + "testing" + "time" + argo "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" "github.com/gogo/protobuf/types" "github.com/google/go-cmp/cmp" @@ -9,75 +14,147 @@ import ( "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" "github.com/istio-ecosystem/admiral/admiral/pkg/controller/istio" "github.com/istio-ecosystem/admiral/admiral/pkg/test" + "github.com/stretchr/testify/assert" "istio.io/api/networking/v1alpha3" v1alpha32 "istio.io/client-go/pkg/apis/networking/v1alpha3" istiofake "istio.io/client-go/pkg/clientset/versioned/fake" coreV1 "k8s.io/api/core/v1" + k8sV1 "k8s.io/api/core/v1" k8sv1 "k8s.io/apimachinery/pkg/apis/meta/v1" v12 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/rest" - "strings" - "testing" - "time" ) +func TestGetDependentClusters(t *testing.T) { + + identityClusterCache := common.NewMapOfMaps() + identityClusterCache.Put("id1", "dep1", "cl1") + identityClusterCache.Put("id2", "dep2", "cl2") + identityClusterCache.Put("id3", "dep3", "cl3") + + testCases := []struct { + name string + dependents map[string]string + identityClusterCache *common.MapOfMaps + sourceServices map[string]*k8sV1.Service + expectedResult map[string]string + }{ + { + name: "nil dependents map", + dependents: nil, + expectedResult: make(map[string]string), + }, + { + name: "empty dependents map", + dependents: map[string]string{}, + identityClusterCache: identityClusterCache, + expectedResult: map[string]string{}, + }, + { + name: "no dependent match", + dependents: map[string]string{ + "id99": "val1", + }, + identityClusterCache: identityClusterCache, + expectedResult: map[string]string{}, + }, + { + name: "no service for matched dep cluster", + dependents: map[string]string{ + "id1": "val1", + }, + identityClusterCache: identityClusterCache, + sourceServices: map[string]*k8sV1.Service{ + "cl1": &k8sV1.Service{}, + }, + expectedResult: map[string]string{}, + }, + { + name: "found service for matched dep cluster", + dependents: map[string]string{ + "id1": "val1", + }, + identityClusterCache: identityClusterCache, + sourceServices: map[string]*k8sV1.Service{ + "cl99": &k8sV1.Service{ + ObjectMeta: v12.ObjectMeta{ + Name: "testservice", + }, + }, + }, + expectedResult: map[string]string{ + "cl1": "cl1", + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actualResult := getDependentClusters(tc.dependents, tc.identityClusterCache, tc.sourceServices) + assert.Equal(t, len(tc.expectedResult), len(actualResult)) + assert.True(t, reflect.DeepEqual(actualResult, tc.expectedResult)) + }) + } + +} + func TestIgnoreIstioResource(t *testing.T) { //Struct of test case info. Name is required. testCases := []struct { name string exportTo []string - annotations map[string]string - namespace string + annotations map[string]string + namespace string expectedResult bool }{ { name: "Should return false when exportTo is not present", exportTo: nil, - annotations: nil, - namespace: "random-ns", + annotations: nil, + namespace: "random-ns", expectedResult: false, }, { name: "Should return false when its exported to *", exportTo: []string{"*"}, - annotations: nil, - namespace: "random-ns", + annotations: nil, + namespace: "random-ns", expectedResult: false, }, { name: "Should return true when its exported to .", exportTo: []string{"."}, - annotations: nil, - namespace: "random-ns", + annotations: nil, + namespace: "random-ns", expectedResult: true, }, { name: "Should return true when its exported to a handful of namespaces", exportTo: []string{"namespace1", "namespace2"}, - annotations: nil, - namespace: "random-ns", + annotations: nil, + namespace: "random-ns", expectedResult: true, }, { name: "Should return true when admiral ignore annotation is set", exportTo: []string{"*"}, - annotations: map[string]string{common.AdmiralIgnoreAnnotation: "true"}, - namespace: "random-ns", + annotations: map[string]string{common.AdmiralIgnoreAnnotation: "true"}, + namespace: "random-ns", expectedResult: true, }, { name: "Should return true when its from istio-system namespace", exportTo: []string{"*"}, - annotations: nil, - namespace: "istio-system", + annotations: nil, + namespace: "istio-system", expectedResult: true, }, { name: "Should return true when its from admiral sync namespace", exportTo: []string{"*"}, - annotations: nil, - namespace: "ns", + annotations: nil, + namespace: "ns", expectedResult: true, }, } @@ -98,9 +175,9 @@ func TestIgnoreIstioResource(t *testing.T) { func TestGetDestinationRule(t *testing.T) { //Do setup here outlierDetection := &v1alpha3.OutlierDetection{ - BaseEjectionTime: &types.Duration{Seconds: 300}, + BaseEjectionTime: &types.Duration{Seconds: 300}, Consecutive_5XxErrors: &types.UInt32Value{Value: 10}, - Interval: &types.Duration{Seconds: 60}} + Interval: &types.Duration{Seconds: 60}} mTLS := &v1alpha3.TrafficPolicy{Tls: &v1alpha3.TLSSettings{Mode: v1alpha3.TLSSettings_ISTIO_MUTUAL}, OutlierDetection: outlierDetection} noGtpDr := v1alpha3.DestinationRule{ @@ -426,18 +503,18 @@ func TestGetServiceForRolloutCanary(t *testing.T) { service.Spec.Ports = ports stableService := &coreV1.Service{ - ObjectMeta: v12.ObjectMeta{Name:STABLESERVICENAME, Namespace: NAMESPACE}, + ObjectMeta: v12.ObjectMeta{Name: STABLESERVICENAME, Namespace: NAMESPACE}, Spec: coreV1.ServiceSpec{ Selector: selectorMap, - Ports: ports, + Ports: ports, }, } canaryService := &coreV1.Service{ - ObjectMeta: v12.ObjectMeta{Name:CANARYSERVICENAME, Namespace: NAMESPACE}, + ObjectMeta: v12.ObjectMeta{Name: CANARYSERVICENAME, Namespace: NAMESPACE}, Spec: coreV1.ServiceSpec{ Selector: selectorMap, - Ports: ports, + Ports: ports, }, } @@ -493,30 +570,30 @@ func TestGetServiceForRolloutCanary(t *testing.T) { virtualService := &v1alpha32.VirtualService{ ObjectMeta: v12.ObjectMeta{Name: VS_NAME_1, Namespace: NAMESPACE}, - Spec:v1alpha3.VirtualService{ - Http: []*v1alpha3.HTTPRoute{{Route:[]*v1alpha3.HTTPRouteDestination{ - {Destination: &v1alpha3.Destination{Host: STABLESERVICENAME}, Weight:80}, - {Destination: &v1alpha3.Destination{Host: CANARYSERVICENAME}, Weight:20}, + Spec: v1alpha3.VirtualService{ + Http: []*v1alpha3.HTTPRoute{{Route: []*v1alpha3.HTTPRouteDestination{ + {Destination: &v1alpha3.Destination{Host: STABLESERVICENAME}, Weight: 80}, + {Destination: &v1alpha3.Destination{Host: CANARYSERVICENAME}, Weight: 20}, }}}, }, } vsMutipleRoutesWithMatch := &v1alpha32.VirtualService{ ObjectMeta: v12.ObjectMeta{Name: VS_NAME_2, Namespace: NAMESPACE}, - Spec:v1alpha3.VirtualService{ - Http: []*v1alpha3.HTTPRoute{{Name:VS_ROUTE_PRIMARY, Route:[]*v1alpha3.HTTPRouteDestination{ - {Destination: &v1alpha3.Destination{Host: STABLESERVICENAME}, Weight:80}, - {Destination: &v1alpha3.Destination{Host: CANARYSERVICENAME}, Weight:20}, + Spec: v1alpha3.VirtualService{ + Http: []*v1alpha3.HTTPRoute{{Name: VS_ROUTE_PRIMARY, Route: []*v1alpha3.HTTPRouteDestination{ + {Destination: &v1alpha3.Destination{Host: STABLESERVICENAME}, Weight: 80}, + {Destination: &v1alpha3.Destination{Host: CANARYSERVICENAME}, Weight: 20}, }}}, }, } vsMutipleRoutesWithZeroWeight := &v1alpha32.VirtualService{ ObjectMeta: v12.ObjectMeta{Name: VS_NAME_4, Namespace: NAMESPACE}, - Spec:v1alpha3.VirtualService{ - Http: []*v1alpha3.HTTPRoute{{Name:"random", Route:[]*v1alpha3.HTTPRouteDestination{ - {Destination: &v1alpha3.Destination{Host: STABLESERVICENAME}, Weight:100}, - {Destination: &v1alpha3.Destination{Host: CANARYSERVICENAME}, Weight:0}, + Spec: v1alpha3.VirtualService{ + Http: []*v1alpha3.HTTPRoute{{Name: "random", Route: []*v1alpha3.HTTPRouteDestination{ + {Destination: &v1alpha3.Destination{Host: STABLESERVICENAME}, Weight: 100}, + {Destination: &v1alpha3.Destination{Host: CANARYSERVICENAME}, Weight: 0}, }}}, }, } @@ -613,7 +690,7 @@ func TestGetServiceForRolloutCanary(t *testing.T) { CanaryService: CANARYSERVICENAME, TrafficRouting: &argo.RolloutTrafficRouting{ Istio: &argo.IstioTrafficRouting{ - VirtualService: argo.IstioVirtualService{Name: VS_NAME_2, Routes: []string {VS_ROUTE_PRIMARY}}, + VirtualService: argo.IstioVirtualService{Name: VS_NAME_2, Routes: []string{VS_ROUTE_PRIMARY}}, }, }, }, @@ -632,7 +709,7 @@ func TestGetServiceForRolloutCanary(t *testing.T) { CanaryService: CANARYSERVICENAME, TrafficRouting: &argo.RolloutTrafficRouting{ Istio: &argo.IstioTrafficRouting{ - VirtualService: argo.IstioVirtualService{Name: VS_NAME_3, Routes: []string {"random"}}, + VirtualService: argo.IstioVirtualService{Name: VS_NAME_3, Routes: []string{"random"}}, }, }, }, @@ -676,16 +753,16 @@ func TestGetServiceForRolloutCanary(t *testing.T) { }, } - resultForDummy := map[string]*WeightedService {"dummy": {Weight:1, Service:service1},} + resultForDummy := map[string]*WeightedService{"dummy": {Weight: 1, Service: service1}} - resultForRandomMatch := map[string]*WeightedService {CANARYSERVICENAME: {Weight:1, Service:canaryService},} + resultForRandomMatch := map[string]*WeightedService{CANARYSERVICENAME: {Weight: 1, Service: canaryService}} - resultForStableServiceOnly := map[string]*WeightedService {STABLESERVICENAME: {Weight:1, Service:stableService},} + resultForStableServiceOnly := map[string]*WeightedService{STABLESERVICENAME: {Weight: 1, Service: stableService}} - resultForCanaryWithIstio := map[string]*WeightedService {STABLESERVICENAME: {Weight:80, Service:stableService}, - CANARYSERVICENAME: {Weight:20, Service:canaryService},} + resultForCanaryWithIstio := map[string]*WeightedService{STABLESERVICENAME: {Weight: 80, Service: stableService}, + CANARYSERVICENAME: {Weight: 20, Service: canaryService}} - resultForCanaryWithStableService := map[string]*WeightedService {STABLESERVICENAME: {Weight:100, Service:stableService},} + resultForCanaryWithStableService := map[string]*WeightedService{STABLESERVICENAME: {Weight: 100, Service: stableService}} testCases := []struct { name string @@ -933,7 +1010,7 @@ func TestGetServiceForRolloutBlueGreen(t *testing.T) { }, } - resultForBlueGreen := map[string]*WeightedService {SERVICENAME: {Weight:1, Service:activeService},} + resultForBlueGreen := map[string]*WeightedService{SERVICENAME: {Weight: 1, Service: activeService}} testCases := []struct { name string @@ -955,7 +1032,7 @@ func TestGetServiceForRolloutBlueGreen(t *testing.T) { name: "canaryRolloutHappyCase", rollout: &bgRollout, rc: rc, - result: resultForBlueGreen , + result: resultForBlueGreen, }, { name: "canaryRolloutNilRollout", @@ -1039,7 +1116,7 @@ func TestSkipDestructiveUpdate(t *testing.T) { }, } - newSeTwoEndpoints := &v1alpha32.ServiceEntry{ + newSeTwoEndpoints := &v1alpha32.ServiceEntry{ ObjectMeta: v12.ObjectMeta{Name: "se1", Namespace: "random"}, Spec: twoEndpointSe, } @@ -1064,7 +1141,6 @@ func TestSkipDestructiveUpdate(t *testing.T) { Spec: oneEndpointSe, } - rcWarmupPhase := &RemoteController{ StartTime: time.Now(), } @@ -1076,61 +1152,60 @@ func TestSkipDestructiveUpdate(t *testing.T) { //Struct of test case info. Name is required. testCases := []struct { name string - rc *RemoteController - newSe *v1alpha32.ServiceEntry + rc *RemoteController + newSe *v1alpha32.ServiceEntry oldSe *v1alpha32.ServiceEntry skipDestructive bool - diff string + diff string }{ { - name: "Should return false when in warm up phase but not destructive", - rc: rcWarmupPhase, - newSe: newSeOneEndpoint, - oldSe: oldSeOneEndpoint, + name: "Should return false when in warm up phase but not destructive", + rc: rcWarmupPhase, + newSe: newSeOneEndpoint, + oldSe: oldSeOneEndpoint, skipDestructive: false, - diff: "", + diff: "", }, { - name: "Should return true when in warm up phase but is destructive", - rc: rcWarmupPhase, - newSe: newSeOneEndpoint, - oldSe: oldSeTwoEndpoints, + name: "Should return true when in warm up phase but is destructive", + rc: rcWarmupPhase, + newSe: newSeOneEndpoint, + oldSe: oldSeTwoEndpoints, skipDestructive: true, - diff: "Delete", + diff: "Delete", }, { - name: "Should return false when not in warm up phase but is destructive", - rc: rcNotinWarmupPhase, - newSe: newSeOneEndpoint, - oldSe: oldSeTwoEndpoints, + name: "Should return false when not in warm up phase but is destructive", + rc: rcNotinWarmupPhase, + newSe: newSeOneEndpoint, + oldSe: oldSeTwoEndpoints, skipDestructive: false, - diff: "Delete", + diff: "Delete", }, { - name: "Should return false when in warm up phase but is constructive", - rc: rcWarmupPhase, - newSe: newSeTwoEndpoints, - oldSe: oldSeOneEndpoint, + name: "Should return false when in warm up phase but is constructive", + rc: rcWarmupPhase, + newSe: newSeTwoEndpoints, + oldSe: oldSeOneEndpoint, skipDestructive: false, - diff: "Add", + diff: "Add", }, { - name: "Should return false when not in warm up phase but endpoints updated", - rc: rcNotinWarmupPhase, - newSe: newSeTwoEndpointsUpdated, - oldSe: oldSeTwoEndpoints, + name: "Should return false when not in warm up phase but endpoints updated", + rc: rcNotinWarmupPhase, + newSe: newSeTwoEndpointsUpdated, + oldSe: oldSeTwoEndpoints, skipDestructive: false, - diff: "Update", + diff: "Update", }, { - name: "Should return true when in warm up phase but endpoints are updated (destructive)", - rc: rcWarmupPhase, - newSe: newSeTwoEndpointsUpdated, - oldSe: oldSeTwoEndpoints, + name: "Should return true when in warm up phase but endpoints are updated (destructive)", + rc: rcWarmupPhase, + newSe: newSeTwoEndpointsUpdated, + oldSe: oldSeTwoEndpoints, skipDestructive: true, - diff: "Update", + diff: "Update", }, - } //Run the test for every provided case @@ -1142,7 +1217,7 @@ func TestSkipDestructiveUpdate(t *testing.T) { } else { t.Errorf("Result Failed. Got %v, expected %v", skipDestructive, c.skipDestructive) } - if c.diff == "" || (c.diff != "" && strings.Contains(diff, c.diff)) { + if c.diff == "" || (c.diff != "" && strings.Contains(diff, c.diff)) { //perfect } else { t.Errorf("Diff Failed. Got %v, expected %v", diff, c.diff) @@ -1153,7 +1228,6 @@ func TestSkipDestructiveUpdate(t *testing.T) { func TestAddUpdateServiceEntry(t *testing.T) { - fakeIstioClient := istiofake.NewSimpleClientset() seCtrl := &istio.ServiceEntryController{ @@ -1201,44 +1275,43 @@ func TestAddUpdateServiceEntry(t *testing.T) { rcWarmupPhase := &RemoteController{ ServiceEntryController: seCtrl, - StartTime: time.Now(), + StartTime: time.Now(), } rcNotinWarmupPhase := &RemoteController{ ServiceEntryController: seCtrl, - StartTime: time.Now().Add(time.Duration(-21) * time.Minute), + StartTime: time.Now().Add(time.Duration(-21) * time.Minute), } //Struct of test case info. Name is required. testCases := []struct { name string - rc *RemoteController - newSe *v1alpha32.ServiceEntry + rc *RemoteController + newSe *v1alpha32.ServiceEntry oldSe *v1alpha32.ServiceEntry skipDestructive bool }{ { - name: "Should add a new SE", - rc: rcWarmupPhase, - newSe: newSeOneEndpoint, - oldSe: nil, + name: "Should add a new SE", + rc: rcWarmupPhase, + newSe: newSeOneEndpoint, + oldSe: nil, skipDestructive: false, }, { - name: "Should not update SE when in warm up mode and the update is destructive", - rc: rcWarmupPhase, - newSe: newSeOneEndpoint, - oldSe: oldSeTwoEndpoints, + name: "Should not update SE when in warm up mode and the update is destructive", + rc: rcWarmupPhase, + newSe: newSeOneEndpoint, + oldSe: oldSeTwoEndpoints, skipDestructive: true, }, { - name: "Should update an SE", - rc: rcNotinWarmupPhase, - newSe: newSeOneEndpoint, - oldSe: oldSeTwoEndpoints, + name: "Should update an SE", + rc: rcNotinWarmupPhase, + newSe: newSeOneEndpoint, + oldSe: oldSeTwoEndpoints, skipDestructive: false, }, - } //Run the test for every provided case @@ -1255,4 +1328,4 @@ func TestAddUpdateServiceEntry(t *testing.T) { } }) } -} \ No newline at end of file +} diff --git a/admiral/pkg/clusters/serviceentry.go b/admiral/pkg/clusters/serviceentry.go index ff7a8c00..3f53dd6d 100644 --- a/admiral/pkg/clusters/serviceentry.go +++ b/admiral/pkg/clusters/serviceentry.go @@ -3,7 +3,6 @@ package clusters import ( "errors" "fmt" - v1 "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" "math" "math/rand" "reflect" @@ -11,6 +10,8 @@ import ( "strings" "time" + v1 "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" + argo "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" @@ -115,7 +116,7 @@ func modifyServiceEntryForNewServiceOrPod(event admiral.EventType, env string, s sourceServices[rc.ClusterID] = serviceInstance } - dependents := remoteRegistry.AdmiralCache.IdentityDependencyCache.Get(sourceIdentity) + dependents := remoteRegistry.AdmiralCache.IdentityDependencyCache.Get(sourceIdentity).Copy() dependentClusters := getDependentClusters(dependents, remoteRegistry.AdmiralCache.IdentityClusterCache, sourceServices) @@ -167,7 +168,7 @@ func modifyServiceEntryForNewServiceOrPod(event admiral.EventType, env string, s } } - for _, val := range dependents.Map() { + for _, val := range dependents { remoteRegistry.AdmiralCache.DependencyNamespaceCache.Put(val, serviceInstance.Namespace, localFqdn, map[string]string{cname: "1"}) } diff --git a/admiral/pkg/controller/common/types.go b/admiral/pkg/controller/common/types.go index c7457ecc..8f4e4c5e 100644 --- a/admiral/pkg/controller/common/types.go +++ b/admiral/pkg/controller/common/types.go @@ -64,8 +64,8 @@ type LabelSet struct { AdmiralIgnoreLabel string WorkloadIdentityKey string //Should always be used for both label and annotation (using label as the primary, and falling back to annotation if the label is not found) GlobalTrafficDeploymentLabel string //label used to tie together deployments and globaltrafficpolicy objects. Configured separately from the identity key because this one _must_ be a label - EnvKey string //key used to group deployments by env. The order would be to use annotation `EnvKey` and then label `EnvKey` and then fallback to label `env` label - GatewayApp string //the value for `app` key that will be used to fetch the loadblancer for cross cluster calls, also referred to as east west gateway + EnvKey string //key used to group deployments by env. The order would be to use annotation `EnvKey` and then label `EnvKey` and then fallback to label `env` label + GatewayApp string //the value for `app` key that will be used to fetch the loadblancer for cross cluster calls, also referred to as east west gateway } func NewSidecarEgressMap() *SidecarEgressMap { @@ -105,14 +105,28 @@ func (s *Map) Delete(key string) { delete(s.cache, key) } -func (s *Map) Map() map[string]string { +func (s *Map) Copy() map[string]string { if s != nil { - return s.cache + defer s.mutex.Unlock() + s.mutex.Lock() + var copy = make(map[string]string) + for k, v := range s.cache { + copy[k] = v + } + return copy } else { return nil } } +func (s *Map) Range(fn func(k string, v string)) { + s.mutex.Lock() + for k, v := range s.cache { + fn(k, v) + } + s.mutex.Unlock() +} + func (s *MapOfMaps) Put(pkey string, key string, value string) { defer s.mutex.Unlock() s.mutex.Lock() @@ -125,7 +139,10 @@ func (s *MapOfMaps) Put(pkey string, key string, value string) { } func (s *MapOfMaps) Get(key string) *Map { - return s.cache[key] + s.mutex.Lock() + val := s.cache[key] + s.mutex.Unlock() + return val } func (s *MapOfMaps) Delete(key string) { @@ -138,6 +155,14 @@ func (s *MapOfMaps) Map() map[string]*Map { return s.cache } +func (s *MapOfMaps) Range(fn func(k string, v *Map)) { + s.mutex.Lock() + for k, v := range s.cache { + fn(k, v) + } + s.mutex.Unlock() +} + func (s *SidecarEgressMap) Put(identity string, namespace string, fqdn string, cnames map[string]string) { defer s.mutex.Unlock() s.mutex.Lock() diff --git a/admiral/pkg/controller/common/types_test.go b/admiral/pkg/controller/common/types_test.go index b911af2a..ebb33d2c 100644 --- a/admiral/pkg/controller/common/types_test.go +++ b/admiral/pkg/controller/common/types_test.go @@ -1,9 +1,14 @@ package common import ( - "github.com/google/go-cmp/cmp" + "context" "strings" "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/util/uuid" ) func TestMapOfMaps(t *testing.T) { @@ -84,3 +89,105 @@ func TestAdmiralParams(t *testing.T) { t.Errorf("AdmiralParams String doesn't have the expected Stringified value expected to contain %v", expectedContainsStr) } } + +func TestMapOfMapConcurrency(t *testing.T) { + + mapOfMaps := NewMapOfMaps() + mapOfMaps.Put("pkey1", "dev.a.global2", "127.0.10.2") + mapOfMaps.Put("pkey2", "qa.a.global", "127.0.10.1") + mapOfMaps.Put("pkey3", "stage.a.global", "127.0.10.1") + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + go func(ctx context.Context) { + for { + select { + case <-ctx.Done(): + return + default: + mapOfMaps.Put(string(uuid.NewUUID()), "test1", "value1") + } + } + }(ctx) + + time.Sleep(1 * time.Second) + + mapOfMaps.Range(func(k string, v *Map) { + assert.NotNil(t, k) + }) + +} + +func TestMapOfMapsRange(t *testing.T) { + + mapOfMaps := NewMapOfMaps() + mapOfMaps.Put("pkey1", "dev.a.global2", "127.0.10.2") + mapOfMaps.Put("pkey2", "qa.a.global", "127.0.10.1") + mapOfMaps.Put("pkey3", "stage.a.global", "127.0.10.1") + + keys := make(map[string]string, len(mapOfMaps.Map())) + for _, k := range keys { + keys[k] = k + } + + numOfIter := 0 + mapOfMaps.Range(func(k string, v *Map) { + assert.NotNil(t, keys[k]) + numOfIter++ + }) + + assert.Equal(t, 3, numOfIter) + +} + +func TestMapConcurrency(t *testing.T) { + + m := NewMap() + m.Put("pkey1", "127.0.10.2") + m.Put("pkey2", "127.0.10.1") + m.Put("pkey3", "127.0.10.1") + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + go func(ctx context.Context) { + for { + select { + case <-ctx.Done(): + return + default: + m.Put(string(uuid.NewUUID()), "value1") + } + } + }(ctx) + + time.Sleep(1 * time.Second) + + m.Range(func(k string, v string) { + assert.NotNil(t, k) + }) + +} + +func TestMapRange(t *testing.T) { + + m := NewMap() + m.Put("pkey1", "127.0.10.2") + m.Put("pkey2", "127.0.10.1") + m.Put("pkey3", "127.0.10.1") + + keys := make(map[string]string, len(m.cache)) + for _, k := range keys { + keys[k] = k + } + + numOfIter := 0 + m.Range(func(k string, v string) { + assert.NotNil(t, keys[k]) + numOfIter++ + }) + + assert.Equal(t, 3, numOfIter) + +} diff --git a/go.mod b/go.mod index 9211dfce..ae100d95 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( github.com/argoproj/argo-rollouts v0.8.3 github.com/cenkalti/backoff v2.2.1+incompatible github.com/emicklei/go-restful v2.11.2+incompatible // indirect - github.com/go-openapi/jsonreference v0.19.3 // indirect github.com/go-openapi/spec v0.19.6 // indirect github.com/go-openapi/swag v0.19.7 // indirect github.com/gogo/protobuf v1.3.1 @@ -23,7 +22,7 @@ require ( github.com/prometheus/common v0.9.1 github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v0.0.5 - github.com/stretchr/testify v1.4.0 + github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.0.0-20191108234033-bd318be0434a // indirect golang.org/x/net v0.0.0-20200301022130-244492dfa37a // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect @@ -37,7 +36,6 @@ require ( k8s.io/apimachinery v0.17.3 k8s.io/client-go v0.17.3 k8s.io/kube-openapi v0.0.0-20200204173128-addea2498afe // indirect - k8s.io/kubernetes v1.17.3 // indirect sigs.k8s.io/yaml v1.2.0 // indirect ) diff --git a/go.sum b/go.sum index 3b7c4950..e564be84 100644 --- a/go.sum +++ b/go.sum @@ -45,8 +45,6 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antlr/antlr4 v0.0.0-20191011202612-ad2bd05285ca/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y= github.com/antonmedv/expr v1.4.2/go.mod h1:xesgliOuukGf21740qhh8PvFdN66yZ9lJJ/PzSFAmzI= -github.com/argoproj/argo-rollouts v0.7.2 h1:eUtsstL3DWNv+SxjnwJEBOHH2KOF5lu5G/zF5yKgC3A= -github.com/argoproj/argo-rollouts v0.7.2/go.mod h1:zjMEXhycwvFGimOzpeiSmt/Cv58I63nGgVuROKFIfB8= github.com/argoproj/argo-rollouts v0.8.3 h1:tjudGKI0+igRLtuQ6QWuYnldY1ylxtA1A5bBL/9R17A= github.com/argoproj/argo-rollouts v0.8.3/go.mod h1:+RujwsBG/yTbpW8PdduyS3QxpmeEbePcUeFUoTuY+c4= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -135,7 +133,6 @@ github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= @@ -222,7 +219,6 @@ github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoM github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc= github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= -github.com/gobuffalo/flect v0.2.0 h1:EWCvMGGxOjsgwlWaP+f4+Hh6yrrte7JeFL2S6b+0hdM= github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus v0.0.0-20181101234600-2ff6f7ffd60f/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= @@ -336,7 +332,6 @@ github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 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 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= @@ -387,13 +382,11 @@ github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7 github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= @@ -470,8 +463,6 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1: 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/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -534,8 +525,9 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/thecodeteam/goscaleio v0.1.0/go.mod h1:68sdkZAsK8bvEwBlbQnlLS+xU+hvLYM/iQ8KXej1AwM= @@ -553,7 +545,6 @@ github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU github.com/valyala/quicktemplate v1.1.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= -github.com/vektra/mockery v0.0.0-20181123154057-e78b021dcbb5 h1:Xim2mBRFdXzXmKRO8DJg/FJtn/8Fj9NOEpO6+WuMPmk= github.com/vektra/mockery v0.0.0-20181123154057-e78b021dcbb5/go.mod h1:ppEjwdhyy7Y31EnHRDm1JkChoC7LXIJ7Ex0VYLWtZtQ= github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netns v0.0.0-20171111001504-be1fbeda1936/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= @@ -665,8 +656,6 @@ golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd h1:3x5uuvBgE6oaXJjCOvpCC1IpgJogqQ+PqGGU3ZxAgII= -golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -709,7 +698,6 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190617190820-da514acc4774/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-20190909030654-5b82db07426d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72 h1:bw9doJza/SFBEweII/rHQh338oozWyiFsBRHtrflcws= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= @@ -767,8 +755,9 @@ 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 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966 h1:B0J02caTR6tpSJozBJyiAzT6CtBzjclw4pgm9gg8Ys0= gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/gotestsum v0.3.5/go.mod h1:Mnf3e5FUzXbkCfynWBGOwLssY7gTQgCHObK9tMpAriY= @@ -785,34 +774,22 @@ istio.io/client-go v0.0.0-20200226182959-cde3e69bd9dd/go.mod h1:wLFtAm266NbVvt1Y istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a/go.mod h1:OzpAts7jljZceG4Vqi5/zXy/pOg1b209T3jb7Nv5wIs= istio.io/gogo-genproto v0.0.0-20191024203824-d079cc8b1d55 h1:nvpx66mnuGvXYP4IfCWfUqB9YhiXBF3MvUDsclNnDzI= istio.io/gogo-genproto v0.0.0-20191024203824-d079cc8b1d55/go.mod h1:OzpAts7jljZceG4Vqi5/zXy/pOg1b209T3jb7Nv5wIs= -k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI= -k8s.io/api v0.17.2 h1:NF1UFXcKN7/OOv1uxdRz3qfra8AHsPav5M93hlV9+Dc= -k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4= k8s.io/api v0.17.3 h1:XAm3PZp3wnEdzekNkcmj/9Y1zdmQYJ1I4GKSBBZ8aG0= k8s.io/api v0.17.3/go.mod h1:YZ0OTkuw7ipbe305fMpIdf3GLXZKRigjtZaV5gzC2J0= -k8s.io/apiextensions-apiserver v0.17.3 h1:WDZWkPcbgvchEdDd7ysL21GGPx3UKZQLDZXEkevT6n4= k8s.io/apiextensions-apiserver v0.17.3/go.mod h1:CJbCyMfkKftAd/X/V6OTHYhVn7zXnDdnkUjS1h0GTeY= -k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= -k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= -k8s.io/apimachinery v0.17.3 h1:f+uZV6rm4/tHE7xXgLyToprg6xWairaClGVkm2t8omg= -k8s.io/apimachinery v0.17.3/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= k8s.io/apimachinery v0.17.5-beta.0 h1:fpRN0B+B9lyvz8JSgcNAYsP+MosoQlXAmhsta15Ez9g= k8s.io/apimachinery v0.17.5-beta.0/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= k8s.io/apiserver v0.17.3/go.mod h1:iJtsPpu1ZpEnHaNawpSV0nYTGBhhX2dUlnn7/QS7QiY= k8s.io/cli-runtime v0.17.3/go.mod h1:X7idckYphH4SZflgNpOOViSxetiMj6xI0viMAjM81TA= -k8s.io/client-go v0.17.2 h1:ndIfkfXEGrNhLIgkr0+qhRguSD3u6DCmonepn1O6NYc= -k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI= k8s.io/client-go v0.17.3 h1:deUna1Ksx05XeESH6XGCyONNFfiQmDdqeqUvicvP6nU= k8s.io/client-go v0.17.3/go.mod h1:cLXlTMtWHkuK4tD360KpWz2gG2KtdWEr/OT02i3emRQ= k8s.io/cloud-provider v0.17.3/go.mod h1:JBkKSQpbcjcYGDqH5PbifFrcgQ/7WOXRswnfLVbXpI8= k8s.io/cluster-bootstrap v0.17.3/go.mod h1:ujIYnCKnxY/MecpgPx9WgiYCVCFvici6tVIfI2FiI1g= -k8s.io/code-generator v0.17.5-beta.0 h1:mVexNYmXZGP0m+ETIMuQOaTzmhRtldNQdxbE39KX59w= k8s.io/code-generator v0.17.5-beta.0/go.mod h1:l8BLVwASXQZTo2xamW5mQNFCe1XPiAesVq7Y1t7PiQQ= k8s.io/component-base v0.17.3/go.mod h1:GeQf4BrgelWm64PXkIXiPh/XS0hnO42d9gx9BtbZRp8= k8s.io/cri-api v0.17.18-rc.0/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= k8s.io/csi-translation-lib v0.17.3/go.mod h1:FBya8XvGIqDm2/3evLQNxaFXqv/C2UcZa5JgJt6/qqY= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20190822140433-26a664648505 h1:ZY6yclUKVbZ+SdWnkfY+Je5vrMpKOxmGeKRbsXVmqYM= k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/heapster v1.2.0-beta.1/go.mod h1:h1uhptVXMwC8xtZBYsPXKVi8fpdlYkTs6k949KozGrM= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= @@ -828,7 +805,6 @@ k8s.io/kube-proxy v0.17.3/go.mod h1:ds8R8bUYPWtQlspC47Sff7o5aQhWDsv6jpQJATDuqaQ= k8s.io/kube-scheduler v0.17.3/go.mod h1:36HgrrPqzK+rOLTRtDG//b89KjrAZqFI4PXOpdH351M= k8s.io/kubectl v0.17.3/go.mod h1:NUn4IBY7f7yCMwSop2HCXlw/MVYP4HJBiUmOR3n9w28= k8s.io/kubelet v0.17.3/go.mod h1:Nh8owUHZcUXtnDAtmGnip36Nw+X6c4rbmDQlVyIhwMQ= -k8s.io/kubernetes v1.17.3 h1:zWCppkLfHM+hoLqfbsrQ0cJnYw+4vAvedI92oQnjo/Q= k8s.io/kubernetes v1.17.3/go.mod h1:gt28rfzaskIzJ8d82TSJmGrJ0XZD0BBy8TcQvTuCI3w= k8s.io/legacy-cloud-providers v0.17.3/go.mod h1:ujZML5v8efVQxiXXTG+nck7SjP8KhMRjUYNIsoSkYI0= k8s.io/metrics v0.17.3/go.mod h1:HEJGy1fhHOjHggW9rMDBJBD3YuGroH3Y1pnIRw9FFaI= @@ -845,7 +821,6 @@ modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY= -sigs.k8s.io/controller-tools v0.2.5 h1:kH7HKWed9XO42OTxyhUtqyImiefdZV2Q9Jbrytvhf18= sigs.k8s.io/controller-tools v0.2.5/go.mod h1:+t0Hz6tOhJQCdd7IYO0mNzimmiM9sqMU0021u6UCF2o= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=