From 91243aabd50704cf1a820a518f3889c36245f88b Mon Sep 17 00:00:00 2001 From: Gao Pan Date: Mon, 13 Jan 2020 10:27:56 -0800 Subject: [PATCH 01/45] Added secret update function Added process logic for secret update Signed-off-by: Gao Pan --- admiral/cmd/admiral/cmd/root.go | 2 + .../pkg/controller/secret/secretcontroller.go | 82 +++++++++++-------- 2 files changed, 50 insertions(+), 34 deletions(-) diff --git a/admiral/cmd/admiral/cmd/root.go b/admiral/cmd/admiral/cmd/root.go index cac52050..6a77384b 100644 --- a/admiral/cmd/admiral/cmd/root.go +++ b/admiral/cmd/admiral/cmd/root.go @@ -5,6 +5,7 @@ import ( "flag" "fmt" "github.com/istio-ecosystem/admiral/admiral/pkg/clusters" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" "istio.io/istio/pkg/log" "os" "os/signal" @@ -25,6 +26,7 @@ func GetRootCmd(args []string) *cobra.Command { var () params := clusters.AdmiralParams{} + params.LabelSet = &common.LabelSet{} rootCmd := &cobra.Command{ Use: "Admiral", diff --git a/admiral/pkg/controller/secret/secretcontroller.go b/admiral/pkg/controller/secret/secretcontroller.go index 151b4a7d..56e0913d 100644 --- a/admiral/pkg/controller/secret/secretcontroller.go +++ b/admiral/pkg/controller/secret/secretcontroller.go @@ -145,6 +145,13 @@ func NewController( queue.Add(key) } }, + UpdateFunc: func(oldObj interface{}, newObj interface{}) { + key, err := cache.MetaNamespaceKeyFunc(newObj) + log.Infof("Processing update: %s", key) + if err == nil { + queue.Add(key) + } + }, DeleteFunc: func(obj interface{}) { key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) log.Infof("Processing delete: %s", key) @@ -237,47 +244,54 @@ func (c *Controller) addMemberCluster(secretName string, s *corev1.Secret) { for clusterID, kubeConfig := range s.Data { // clusterID must be unique even across multiple secrets if _, ok := c.cs.remoteClusters[clusterID]; !ok { - if len(kubeConfig) == 0 { - log.Infof("Data '%s' in the secret %s in namespace %s is empty, and disregarded ", - clusterID, secretName, s.ObjectMeta.Namespace) - continue - } + log.Infof("Adding new cluster member: %s", clusterID) + c.cs.remoteClusters[clusterID] = &RemoteCluster{} + c.cs.remoteClusters[clusterID].secretName = secretName + } else { + log.Infof("Cluster %s in the secret %s in namespace %s already exists. Reloading secret...", + clusterID, c.cs.remoteClusters[clusterID].secretName, s.ObjectMeta.Namespace) + } - kubeConfig, err := c.secretResolver.FetchKubeConfig(clusterID, kubeConfig) + if len(kubeConfig) == 0 { + log.Infof("Data '%s' in the secret %s in namespace %s is empty, and disregarded ", + clusterID, secretName, s.ObjectMeta.Namespace) + continue + } - if err != nil { - log.Errorf("Failed to fetch kubeconfig for cluster '%s' using secret resolver: %v, err: %v", - clusterID, c.secretResolver, err) - continue - } + kubeConfig, err := c.secretResolver.FetchKubeConfig(clusterID, kubeConfig) - clusterConfig, err := LoadKubeConfig(kubeConfig) - if err != nil { - log.Infof("Data '%s' in the secret %s in namespace %s is not a kubeconfig: %v", - clusterID, secretName, s.ObjectMeta.Namespace, err) - log.Infof("KubeConfig: '%s'", string(kubeConfig)) - continue - } + if err != nil { + log.Errorf("Failed to fetch kubeconfig for cluster '%s' using secret resolver: %v, err: %v", + clusterID, c.secretResolver, err) + continue + } - log.Infof("Adding new cluster member: %s", clusterID) - c.cs.remoteClusters[clusterID] = &RemoteCluster{} - c.cs.remoteClusters[clusterID].secretName = secretName - clientConfig := clientcmd.NewDefaultClientConfig(*clusterConfig, &clientcmd.ConfigOverrides{}) + clusterConfig, err := LoadKubeConfig(kubeConfig) - var restConfig *rest.Config - restConfig, err = clientConfig.ClientConfig() + if err != nil { + log.Infof("Data '%s' in the secret %s in namespace %s is not a kubeconfig: %v", + clusterID, secretName, s.ObjectMeta.Namespace, err) + log.Infof("KubeConfig: '%s'", string(kubeConfig)) + continue + } - if err != nil { - log.Errorf("error during conversion of secret to client config: %v", err) - } + clientConfig := clientcmd.NewDefaultClientConfig(*clusterConfig, &clientcmd.ConfigOverrides{}) - err = c.addCallback(restConfig, clusterID, 2 * time.Minute) - if err != nil { - log.Errorf("error during create of clusterID: %s %v", clusterID, err) - } - } else { - log.Infof("Cluster %s in the secret %s in namespace %s already exists", - clusterID, c.cs.remoteClusters[clusterID].secretName, s.ObjectMeta.Namespace) + var restConfig *rest.Config + restConfig, err = clientConfig.ClientConfig() + + if err != nil { + log.Errorf("error during conversion of secret to client config: %v", err) + continue + } + + err = c.addCallback(restConfig, clusterID, 2 * time.Minute) + + if err != nil { + log.Errorf("error during secret loading for clusterID: %s %v", clusterID, err) + continue + }else{ + log.Infof("Secret loaded for cluster %s in the secret %s in namespace %s.",clusterID,c.cs.remoteClusters[clusterID].secretName, s.ObjectMeta.Namespace) } } log.Infof("Number of remote clusters: %d", len(c.cs.remoteClusters)) From af43510e7fea9c597d8c0f0ae22ce8887310d5ed Mon Sep 17 00:00:00 2001 From: Madeline Date: Tue, 14 Apr 2020 14:38:53 -0700 Subject: [PATCH 02/45] finished first draft for recognizing admiral ignore annotation, ready for review, fixed some typos and 404 link for helm install --- admiral/pkg/controller/admiral/controller.go | 2 +- admiral/pkg/controller/admiral/deployment.go | 18 +++++++++++++++++- admiral/pkg/controller/common/common.go | 1 + admiral/pkg/controller/common/types.go | 2 +- docs/Examples.md | 2 +- install/admiralremote/base/remote.yaml | 2 +- install/sample/base/webapp.yaml | 2 ++ 7 files changed, 24 insertions(+), 5 deletions(-) diff --git a/admiral/pkg/controller/admiral/controller.go b/admiral/pkg/controller/admiral/controller.go index 0213e944..f214cd85 100644 --- a/admiral/pkg/controller/admiral/controller.go +++ b/admiral/pkg/controller/admiral/controller.go @@ -67,7 +67,7 @@ func NewController(stopCh <-chan struct{}, delegator Delegator, informer cache.S return controller } -// Run starts the controller until it receves a message over stopCh +// Run starts the controller until it receives a message over stopCh func (c *Controller) Run(stopCh <-chan struct{}) { defer utilruntime.HandleCrash() defer c.queue.ShutDown() diff --git a/admiral/pkg/controller/admiral/deployment.go b/admiral/pkg/controller/admiral/deployment.go index 3c73b112..2a576881 100644 --- a/admiral/pkg/controller/admiral/deployment.go +++ b/admiral/pkg/controller/admiral/deployment.go @@ -9,6 +9,7 @@ import ( "k8s.io/client-go/rest" "time" + log "github.com/sirupsen/logrus" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" @@ -175,8 +176,23 @@ func (d *DeploymentController) shouldIgnoreBasedOnLabels(deployment *k8sAppsV1.D if deployment.Spec.Template.Labels[d.labelSet.AdmiralIgnoreLabel] == "true" { //if we should ignore, do that and who cares what else is there return true } + if deployment.Spec.Template.Annotations[d.labelSet.DeploymentAnnotation] != "true" { //Not sidecar injected, we don't want to inject - return true + return true + } + + if deployment.Annotations[common.AdmiralIgnoreAnnotation] == "true" { + return true + } + + ns, err := d.K8sClient.CoreV1().Namespaces().Get(deployment.Namespace, meta_v1.GetOptions{}) + if err != nil { + log.Warnf("Failed to get namespace object for deployment with namespace %v, err: %v", deployment.Namespace, err) + return false + } + + if ns.Annotations[common.AdmiralIgnoreAnnotation] == "true" { + return true } return false //labels are fine, we should not ignore } diff --git a/admiral/pkg/controller/common/common.go b/admiral/pkg/controller/common/common.go index 5becd0a9..6c39de71 100644 --- a/admiral/pkg/controller/common/common.go +++ b/admiral/pkg/controller/common/common.go @@ -27,6 +27,7 @@ const ( SpiffePrefix = "spiffe://" SidecarEnabledPorts = "traffic.sidecar.istio.io/includeInboundPorts" Default = "default" + AdmiralIgnoreAnnotation = "admiral.io/ignore" ) type Event int diff --git a/admiral/pkg/controller/common/types.go b/admiral/pkg/controller/common/types.go index 2c375ec2..c6d1f86e 100644 --- a/admiral/pkg/controller/common/types.go +++ b/admiral/pkg/controller/common/types.go @@ -60,7 +60,7 @@ type LabelSet struct { NamespaceSidecarInjectionLabelValue string 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 seperately from the identity key because this one _must_ be a label + GlobalTrafficDeploymentLabel string //label used to tie together deployments and globaltrafficpolicy objects. Configured separately from the identity key because this one _must_ be a label } func NewSidecarEgressMap() *SidecarEgressMap { diff --git a/docs/Examples.md b/docs/Examples.md index 2ed3345a..261fccfc 100644 --- a/docs/Examples.md +++ b/docs/Examples.md @@ -11,7 +11,7 @@ One or more k8s clusters will need the following steps executed * Install [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) * Install [minikube](https://istio.io/docs/setup/platform-setup/minikube/) to bring up a k8s cluster locally (Make sure your `$KUBECONFIG` points to `minikube` before proceeding) -* Install [helm](https://github.com/helm/helm/blob/master/docs/install.md) +* Install [helm](https://helm.sh/docs/intro/install/) * Install [wget](https://www.gnu.org/software/wget/) #### Install Istio diff --git a/install/admiralremote/base/remote.yaml b/install/admiralremote/base/remote.yaml index 772423a7..1c1e2018 100644 --- a/install/admiralremote/base/remote.yaml +++ b/install/admiralremote/base/remote.yaml @@ -18,7 +18,7 @@ metadata: name: admiral-sync-read rules: - apiGroups: ['', 'apps'] - resources: [ 'pods', 'services', 'nodes', 'deployments'] + resources: [ 'pods', 'services', 'nodes', 'deployments', 'namespaces'] verbs: ['get', 'watch', 'list'] - apiGroups: ["networking.istio.io"] resources: ['virtualservices', 'destinationrules', 'serviceentries', 'envoyfilters' ,'gateways', 'sidecars'] diff --git a/install/sample/base/webapp.yaml b/install/sample/base/webapp.yaml index 011262e7..851d3e9b 100644 --- a/install/sample/base/webapp.yaml +++ b/install/sample/base/webapp.yaml @@ -8,6 +8,8 @@ apiVersion: extensions/v1beta1 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 template: From bcf6f84ad55b8365cc25c11cc44b79d6db91f0cd Mon Sep 17 00:00:00 2001 From: Madeline Date: Mon, 20 Apr 2020 12:14:34 -0700 Subject: [PATCH 03/45] changed test --- .../secret/secretcontroller_test.go | 152 +++++++++++++++++- go.mod | 1 + 2 files changed, 152 insertions(+), 1 deletion(-) diff --git a/admiral/pkg/controller/secret/secretcontroller_test.go b/admiral/pkg/controller/secret/secretcontroller_test.go index 6a66cc4b..5c112e2b 100644 --- a/admiral/pkg/controller/secret/secretcontroller_test.go +++ b/admiral/pkg/controller/secret/secretcontroller_test.go @@ -16,10 +16,14 @@ package secret import ( "context" + "fmt" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "sync" "testing" "time" + . "github.com/onsi/gomega" "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" @@ -34,6 +38,13 @@ const secretNameSpace string = "istio-system" var testCreateControllerCalled bool var testDeleteControllerCalled bool +var ( + mu sync.Mutex + added string + updated string + deleted string +) + func testCreateController(clientConfig *rest.Config, clusterID string, resyncPeriod time.Duration) error { testCreateControllerCalled = true return nil @@ -71,7 +82,15 @@ func deleteMultiClusterSecret(k8s *fake.Clientset) error { } func mockLoadKubeConfig(kubeconfig []byte) (*clientcmdapi.Config, error) { - return &clientcmdapi.Config{}, nil + config := clientcmdapi.NewConfig() + config.Clusters["clean"] = &clientcmdapi.Cluster{ + Server: "https://anything.com:8080", + } + config.Contexts["clean"] = &clientcmdapi.Context{ + Cluster: "clean", + } + config.CurrentContext = "clean" + return config, nil } func verifyControllerDeleted(t *testing.T, timeoutName string) { @@ -86,6 +105,7 @@ func verifyControllerCreated(t *testing.T, timeoutName string) { }) } +/* func Test_SecretController(t *testing.T) { LoadKubeConfig = mockLoadKubeConfig @@ -127,4 +147,134 @@ func Test_SecretController(t *testing.T) { if testCreateControllerCalled != false { t.Fatalf("Test failed on delete secret, create callback function called") } +}*/ + +func resetCallbackData() { + added = "" + updated = "" + deleted = "" +} + +func addCallback(config *rest.Config, id string, resyncPeriod time.Duration) error { + mu.Lock() + defer mu.Unlock() + added = id + return nil +} + +func deleteCallback(id string) error { + mu.Lock() + defer mu.Unlock() + deleted = id + return nil +} + +func TestMock(t *testing.T) { + config := clientcmdapi.NewConfig() + config.Clusters["clean"] = &clientcmdapi.Cluster{ + Server: "https://anything.com:8080", + } + /* + config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{ + Token: "the-token", + }*/ + config.Contexts["clean"] = &clientcmdapi.Context{ + Cluster: "clean", + //AuthInfo: "clean", + } + config.CurrentContext = "clean" + clientConfig := NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{}) + fmt.Println(clientConfig) + restConfig, err := clientConfig.ClientConfig() + fmt.Println(err) + fmt.Println(restConfig) +} + +func makeSecret(secret, clusterID string, kubeconfig []byte) *v1.Secret { + return &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secret, + Namespace: secretNameSpace, + Labels: map[string]string{ + filterLabel: "true", + }, + }, + Data: map[string][]byte{ + clusterID: kubeconfig, + }, + } +} + +func Test_SecretController(t *testing.T) { + g := NewWithT(t) + + LoadKubeConfig = mockLoadKubeConfig + //NewDefaultClientConfig = mockNewDefaultClientConfig + + clientset := fake.NewSimpleClientset() + + var ( + secret0 = makeSecret("s0", "c0", []byte("kubeconfig0-0")) + //secret0UpdateKubeconfigSame = makeSecret("s0", "c0", []byte("kubeconfig0-1")) + secret1 = makeSecret("s1", "c1", []byte("kubeconfig1-0")) + ) + //secret0UpdateKubeconfigSame.Annotations = map[string]string{"foo": "bar"} + + steps := []struct { + // only set one of these per step. The others should be nil. + add *v1.Secret + delete *v1.Secret + + // only set one of these per step. The others should be empty. + wantAdded string + wantDeleted string + }{ + {add: secret0, wantAdded: "c0"}, + {add: secret1, wantAdded: "c1"}, + {delete: secret0, wantDeleted: "c0"}, + {delete: secret1, wantDeleted: "c1"}, + } + + // Start the secret controller and sleep to allow secret process to start. + g.Expect( + StartSecretController(clientset, addCallback, deleteCallback, secretNameSpace, context.TODO(), "")). + Should(Succeed()) + + for i, step := range steps { + resetCallbackData() + + t.Run(fmt.Sprintf("[%v]", i), func(t *testing.T) { + g := NewWithT(t) + + switch { + case step.add != nil: + _, err := clientset.CoreV1().Secrets(secretNameSpace).Create(step.add) + g.Expect(err).Should(BeNil()) + case step.delete != nil: + g.Expect(clientset.CoreV1().Secrets(secretNameSpace).Delete(step.delete.Name, &metav1.DeleteOptions{})). + Should(Succeed()) + } + + switch { + case step.wantAdded != "": + g.Eventually(func() string { + mu.Lock() + defer mu.Unlock() + return added + }, 10*time.Second).Should(Equal(step.wantAdded)) + case step.wantDeleted != "": + g.Eventually(func() string { + mu.Lock() + defer mu.Unlock() + return deleted + }, 10*time.Second).Should(Equal(step.wantDeleted)) + default: + g.Consistently(func() bool { + mu.Lock() + defer mu.Unlock() + return added == "" && updated == "" && deleted == "" + }).Should(Equal(true)) + } + }) + } } diff --git a/go.mod b/go.mod index e928bea7..4af7a5ec 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/mailru/easyjson v0.7.1 // indirect github.com/natefinch/lumberjack v0.0.0-20170531160350-a96e63847dc3 // indirect github.com/onsi/ginkgo v1.10.2 // indirect + github.com/onsi/gomega v1.7.0 github.com/prometheus/common v0.7.0 // indirect github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v0.0.5 From 2e05ff14466044ebf601b8039d84a7a0bc344949 Mon Sep 17 00:00:00 2001 From: Madeline Date: Mon, 20 Apr 2020 15:40:26 -0700 Subject: [PATCH 04/45] local test passed, need to double check in cluyster --- .../pkg/controller/secret/secretcontroller.go | 129 ++++++++++++------ .../secret/secretcontroller_test.go | 118 ++++++++-------- 2 files changed, 145 insertions(+), 102 deletions(-) diff --git a/admiral/pkg/controller/secret/secretcontroller.go b/admiral/pkg/controller/secret/secretcontroller.go index 43c0ab99..a3954c25 100644 --- a/admiral/pkg/controller/secret/secretcontroller.go +++ b/admiral/pkg/controller/secret/secretcontroller.go @@ -47,6 +47,9 @@ var LoadKubeConfig = clientcmd.Load // addSecretCallback prototype for the add secret callback function. type addSecretCallback func(config *rest.Config, dataKey string, resyncPeriod time.Duration) error +// updateSecretCallback prototype for the update secret callback function. +type updateSecretCallback func(config *rest.Config, dataKey string, resyncPeriod time.Duration) error + // removeSecretCallback prototype for the remove secret callback function. type removeSecretCallback func(dataKey string) error @@ -58,6 +61,7 @@ type Controller struct { queue workqueue.RateLimitingInterface informer cache.SharedIndexInformer addCallback addSecretCallback + updateCallback updateSecretCallback removeCallback removeSecretCallback secretResolver resolver.SecretResolver } @@ -86,6 +90,7 @@ func NewController( namespace string, cs *ClusterStore, addCallback addSecretCallback, + updateCallback updateSecretCallback, removeCallback removeSecretCallback, secretResolverType string) *Controller { @@ -126,6 +131,7 @@ func NewController( informer: secretsInformer, queue: queue, addCallback: addCallback, + updateCallback: updateCallback, removeCallback: removeCallback, secretResolver: secretResolver, } @@ -179,10 +185,17 @@ func (c *Controller) Run(stopCh <-chan struct{}) { } // StartSecretController creates the secret controller. -func StartSecretController(k8s kubernetes.Interface, addCallback addSecretCallback, removeCallback removeSecretCallback, namespace string, ctx context.Context, secretResolverType string) error { +func StartSecretController( + k8s kubernetes.Interface, + addCallback addSecretCallback, + updateCallback updateSecretCallback, + removeCallback removeSecretCallback, + namespace string, + ctx context.Context, + secretResolverType string) error { clusterStore := newClustersStore() - controller := NewController(k8s, namespace, clusterStore, addCallback, removeCallback, secretResolverType) + controller := NewController(k8s, namespace, clusterStore, addCallback, updateCallback, removeCallback, secretResolverType) go controller.Run(ctx.Done()) @@ -234,59 +247,91 @@ func (c *Controller) processItem(secretName string) error { return nil } +func (c *Controller) createRemoteCluster(kubeConfig []byte, secretName string, clusterID string, namespace string) (*RemoteCluster, *rest.Config, error) { + if len(kubeConfig) == 0 { + log.Infof("Data '%s' in the secret %s in namespace %s is empty, and disregarded ", + clusterID, secretName, namespace) + return nil, nil, errors.New("kubeconfig is empty") + } + + kubeConfig, err := c.secretResolver.FetchKubeConfig(clusterID, kubeConfig) + + if err != nil { + log.Errorf("Failed to fetch kubeconfig for cluster '%s' using secret resolver: %v, err: %v", + clusterID, c.secretResolver, err) + return nil, nil, errors.New("kubeconfig cannot be fetched") + } + + clusterConfig, err := LoadKubeConfig(kubeConfig) + + if err != nil { + log.Infof("Data '%s' in the secret %s in namespace %s is not a kubeconfig: %v", + clusterID, secretName, namespace, err) + log.Infof("KubeConfig: '%s'", string(kubeConfig)) + return nil, nil, errors.New("clusterConfig cannot be loaded") + } + + clientConfig := clientcmd.NewDefaultClientConfig(*clusterConfig, &clientcmd.ConfigOverrides{}) + + var restConfig *rest.Config + restConfig, err = clientConfig.ClientConfig() + + if err != nil { + log.Errorf("error during conversion of secret to client config: %v", err) + return nil, nil, errors.New("restConfig cannot be built") + } + + return &RemoteCluster{ + secretName: secretName, + }, restConfig, nil +} + func (c *Controller) addMemberCluster(secretName string, s *corev1.Secret) { for clusterID, kubeConfig := range s.Data { // clusterID must be unique even across multiple secrets - if _, ok := c.cs.remoteClusters[clusterID]; !ok { - log.Infof("Adding new cluster member: %s", clusterID) - c.cs.remoteClusters[clusterID] = &RemoteCluster{} - c.cs.remoteClusters[clusterID].secretName = secretName - } else { - log.Infof("Cluster %s in the secret %s in namespace %s already exists. Reloading secret...", - clusterID, c.cs.remoteClusters[clusterID].secretName, s.ObjectMeta.Namespace) - } - - if len(kubeConfig) == 0 { - log.Infof("Data '%s' in the secret %s in namespace %s is empty, and disregarded ", - clusterID, secretName, s.ObjectMeta.Namespace) - continue - } + if prev, ok := c.cs.remoteClusters[clusterID]; !ok { + log.Infof("Adding cluster_id=%v from secret=%v", clusterID, secretName) - kubeConfig, err := c.secretResolver.FetchKubeConfig(clusterID, kubeConfig) + remoteCluster, restConfig, err := c.createRemoteCluster(kubeConfig, secretName, clusterID, s.ObjectMeta.Namespace) - if err != nil { - log.Errorf("Failed to fetch kubeconfig for cluster '%s' using secret resolver: %v, err: %v", - clusterID, c.secretResolver, err) - continue - } + if err != nil { + log.Errorf("Failed to add remote cluster from secret=%v for cluster_id=%v: %v", + secretName, clusterID, err) + continue + } - clusterConfig, err := LoadKubeConfig(kubeConfig) + c.cs.remoteClusters[clusterID] = remoteCluster - if err != nil { - log.Infof("Data '%s' in the secret %s in namespace %s is not a kubeconfig: %v", - clusterID, secretName, s.ObjectMeta.Namespace, err) - log.Infof("KubeConfig: '%s'", string(kubeConfig)) - continue - } + if err := c.addCallback(restConfig, clusterID, 2 * time.Minute); err != nil { + log.Errorf("error during secret loading for clusterID: %s %v", clusterID, err) + continue + } - clientConfig := clientcmd.NewDefaultClientConfig(*clusterConfig, &clientcmd.ConfigOverrides{}) + log.Infof("Secret loaded for cluster %s in the secret %s in namespace %s.",clusterID,c.cs.remoteClusters[clusterID].secretName, s.ObjectMeta.Namespace) - var restConfig *rest.Config - restConfig, err = clientConfig.ClientConfig() + } else { + if prev.secretName != secretName { + log.Errorf("ClusterID reused in two different secrets: %v and %v. ClusterID "+ + "must be unique across all secrets", prev.secretName, secretName) + continue + } - if err != nil { - log.Errorf("error during conversion of secret to client config: %v", err) - continue - } + log.Infof("Updating cluster %v from secret %v", clusterID, secretName) - err = c.addCallback(restConfig, clusterID, 2 * time.Minute) + remoteCluster, restConfig, err := c.createRemoteCluster(kubeConfig, secretName, clusterID, s.ObjectMeta.Namespace) + if err != nil { + log.Errorf("Error updating cluster_id=%v from secret=%v: %v", + clusterID, secretName, err) + continue + } - if err != nil { - log.Errorf("error during secret loading for clusterID: %s %v", clusterID, err) - continue - }else{ - log.Infof("Secret loaded for cluster %s in the secret %s in namespace %s.",clusterID,c.cs.remoteClusters[clusterID].secretName, s.ObjectMeta.Namespace) + c.cs.remoteClusters[clusterID] = remoteCluster + if err := c.updateCallback(restConfig, clusterID, 2 * time.Minute); err != nil { + log.Errorf("Error updating cluster_id from secret=%v: %s %v", + clusterID, secretName, err) + } } + } log.Infof("Number of remote clusters: %d", len(c.cs.remoteClusters)) } diff --git a/admiral/pkg/controller/secret/secretcontroller_test.go b/admiral/pkg/controller/secret/secretcontroller_test.go index 5c112e2b..61f17bba 100644 --- a/admiral/pkg/controller/secret/secretcontroller_test.go +++ b/admiral/pkg/controller/secret/secretcontroller_test.go @@ -18,7 +18,6 @@ import ( "context" "fmt" "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" "sync" "testing" "time" @@ -38,6 +37,22 @@ const secretNameSpace string = "istio-system" var testCreateControllerCalled bool var testDeleteControllerCalled bool + +func makeSecret(secret, clusterID string, kubeconfig []byte) *v1.Secret { + return &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secret, + Namespace: secretNameSpace, + Labels: map[string]string{ + filterLabel: "true", + }, + }, + Data: map[string][]byte{ + clusterID: kubeconfig, + }, + } +} + var ( mu sync.Mutex added string @@ -45,6 +60,34 @@ var ( deleted string ) +func addCallback(config *rest.Config, id string, resyncPeriod time.Duration) error { + mu.Lock() + defer mu.Unlock() + added = id + return nil +} + +func updateCallback(config *rest.Config, id string, resyncPeriod time.Duration) error { + mu.Lock() + defer mu.Unlock() + updated = id + return nil +} + +func deleteCallback(id string) error { + mu.Lock() + defer mu.Unlock() + deleted = id + return nil +} + +func resetCallbackData() { + added = "" + updated = "" + deleted = "" +} + + func testCreateController(clientConfig *rest.Config, clusterID string, resyncPeriod time.Duration) error { testCreateControllerCalled = true return nil @@ -149,87 +192,33 @@ func Test_SecretController(t *testing.T) { } }*/ -func resetCallbackData() { - added = "" - updated = "" - deleted = "" -} - -func addCallback(config *rest.Config, id string, resyncPeriod time.Duration) error { - mu.Lock() - defer mu.Unlock() - added = id - return nil -} - -func deleteCallback(id string) error { - mu.Lock() - defer mu.Unlock() - deleted = id - return nil -} - -func TestMock(t *testing.T) { - config := clientcmdapi.NewConfig() - config.Clusters["clean"] = &clientcmdapi.Cluster{ - Server: "https://anything.com:8080", - } - /* - config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{ - Token: "the-token", - }*/ - config.Contexts["clean"] = &clientcmdapi.Context{ - Cluster: "clean", - //AuthInfo: "clean", - } - config.CurrentContext = "clean" - clientConfig := NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{}) - fmt.Println(clientConfig) - restConfig, err := clientConfig.ClientConfig() - fmt.Println(err) - fmt.Println(restConfig) -} - -func makeSecret(secret, clusterID string, kubeconfig []byte) *v1.Secret { - return &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: secret, - Namespace: secretNameSpace, - Labels: map[string]string{ - filterLabel: "true", - }, - }, - Data: map[string][]byte{ - clusterID: kubeconfig, - }, - } -} func Test_SecretController(t *testing.T) { g := NewWithT(t) LoadKubeConfig = mockLoadKubeConfig - //NewDefaultClientConfig = mockNewDefaultClientConfig clientset := fake.NewSimpleClientset() var ( secret0 = makeSecret("s0", "c0", []byte("kubeconfig0-0")) - //secret0UpdateKubeconfigSame = makeSecret("s0", "c0", []byte("kubeconfig0-1")) + secret0UpdateKubeconfigChanged = makeSecret("s0", "c0", []byte("kubeconfig0-1")) secret1 = makeSecret("s1", "c1", []byte("kubeconfig1-0")) ) - //secret0UpdateKubeconfigSame.Annotations = map[string]string{"foo": "bar"} steps := []struct { // only set one of these per step. The others should be nil. add *v1.Secret + update *v1.Secret delete *v1.Secret // only set one of these per step. The others should be empty. wantAdded string + wantUpdated string wantDeleted string }{ {add: secret0, wantAdded: "c0"}, + {update: secret0UpdateKubeconfigChanged, wantUpdated: "c0"}, {add: secret1, wantAdded: "c1"}, {delete: secret0, wantDeleted: "c0"}, {delete: secret1, wantDeleted: "c1"}, @@ -237,7 +226,7 @@ func Test_SecretController(t *testing.T) { // Start the secret controller and sleep to allow secret process to start. g.Expect( - StartSecretController(clientset, addCallback, deleteCallback, secretNameSpace, context.TODO(), "")). + StartSecretController(clientset, addCallback, updateCallback, deleteCallback, secretNameSpace, context.TODO(), "")). Should(Succeed()) for i, step := range steps { @@ -250,6 +239,9 @@ func Test_SecretController(t *testing.T) { case step.add != nil: _, err := clientset.CoreV1().Secrets(secretNameSpace).Create(step.add) g.Expect(err).Should(BeNil()) + case step.update != nil: + _, err := clientset.CoreV1().Secrets(secretNameSpace).Update(step.update) + g.Expect(err).Should(BeNil()) case step.delete != nil: g.Expect(clientset.CoreV1().Secrets(secretNameSpace).Delete(step.delete.Name, &metav1.DeleteOptions{})). Should(Succeed()) @@ -262,6 +254,12 @@ func Test_SecretController(t *testing.T) { defer mu.Unlock() return added }, 10*time.Second).Should(Equal(step.wantAdded)) + case step.wantUpdated != "": + g.Eventually(func() string { + mu.Lock() + defer mu.Unlock() + return updated + }, 10*time.Second).Should(Equal(step.wantUpdated)) case step.wantDeleted != "": g.Eventually(func() string { mu.Lock() From 56553a8b3992aab1b6dbe2fd9697fc45d4286824 Mon Sep 17 00:00:00 2001 From: Madeline Date: Mon, 20 Apr 2020 16:24:15 -0700 Subject: [PATCH 05/45] finish adding call back function for update --- admiral/pkg/clusters/registry.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/admiral/pkg/clusters/registry.go b/admiral/pkg/clusters/registry.go index f9f03fef..986bf1ec 100644 --- a/admiral/pkg/clusters/registry.go +++ b/admiral/pkg/clusters/registry.go @@ -88,6 +88,7 @@ func createSecretController(ctx context.Context, w *RemoteRegistry) error { err = secret.StartSecretController(w.secretClient, w.createCacheController, + w.updateCacheController, w.deleteCacheController, common.GetClusterRegistriesNamespace(), ctx, common.GetSecretResolver()) @@ -182,6 +183,13 @@ func (r *RemoteRegistry) createCacheController(clientConfig *rest.Config, cluste return nil } +func (r *RemoteRegistry) updateCacheController(clientConfig *rest.Config, clusterID string, resyncPeriod time.Duration) error { + if err := r.deleteCacheController(clusterID); err != nil { + return err + } + return r.createCacheController(clientConfig, clusterID, resyncPeriod) +} + func (r *RemoteRegistry) deleteCacheController(clusterID string) error { controller, ok := r.remoteControllers[clusterID] From de7cef09ad4870e7dbdcfde8ff7ee6f9ddc1e8cd Mon Sep 17 00:00:00 2001 From: josephpeacock <51184065+josephpeacock@users.noreply.github.com> Date: Fri, 3 Jan 2020 16:39:34 -0800 Subject: [PATCH 06/45] Tests for creating controllers (#52) * Tests for creating controllers Signed-off-by: Joe Peacock * Additional pod test - found/fixed a bug Signed-off-by: Joe Peacock * One more test Signed-off-by: Joe Peacock Signed-off-by: Madeline --- .../pkg/controller/admiral/dependency_test.go | 22 +++ .../controller/admiral/globaltraffic_test.go | 27 ++++ admiral/pkg/controller/admiral/node_test.go | 26 ++++ admiral/pkg/controller/admiral/pod.go | 18 ++- admiral/pkg/controller/admiral/pod_test.go | 125 ++++++++++++++++++ admiral/pkg/test/mock.go | 22 +++ 6 files changed, 235 insertions(+), 5 deletions(-) create mode 100644 admiral/pkg/controller/admiral/dependency_test.go create mode 100644 admiral/pkg/controller/admiral/globaltraffic_test.go create mode 100644 admiral/pkg/controller/admiral/node_test.go create mode 100644 admiral/pkg/controller/admiral/pod_test.go diff --git a/admiral/pkg/controller/admiral/dependency_test.go b/admiral/pkg/controller/admiral/dependency_test.go new file mode 100644 index 00000000..ffbc46b1 --- /dev/null +++ b/admiral/pkg/controller/admiral/dependency_test.go @@ -0,0 +1,22 @@ +package admiral + +import ( + "github.com/istio-ecosystem/admiral/admiral/pkg/test" + "testing" + "time" +) + +func TestNewDependencyController(t *testing.T) { + stop := make(chan struct{}) + handler := test.MockDependencyHandler{} + + dependencyController, err := NewDependencyController(stop, &handler, "../../test/resources/admins@fake-cluster.k8s.local", "ns", time.Duration(1000)) + + if err != nil { + t.Errorf("Unexpected err %v", err) + } + + if dependencyController == nil { + t.Errorf("Dependency controller should never be nil without an error thrown") + } +} diff --git a/admiral/pkg/controller/admiral/globaltraffic_test.go b/admiral/pkg/controller/admiral/globaltraffic_test.go new file mode 100644 index 00000000..0a7165a4 --- /dev/null +++ b/admiral/pkg/controller/admiral/globaltraffic_test.go @@ -0,0 +1,27 @@ +package admiral + +import ( + "github.com/istio-ecosystem/admiral/admiral/pkg/test" + "k8s.io/client-go/tools/clientcmd" + "testing" + "time" +) + +func TestNewGlobalTrafficController(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{}) + handler := test.MockGlobalTrafficHandler{} + + globalTrafficController, err := NewGlobalTrafficController(stop, &handler, config, time.Duration(1000)) + + if err != nil { + t.Errorf("Unexpected err %v", err) + } + + if globalTrafficController == nil { + t.Errorf("GlobalTraffic controller should never be nil without an error thrown") + } +} diff --git a/admiral/pkg/controller/admiral/node_test.go b/admiral/pkg/controller/admiral/node_test.go new file mode 100644 index 00000000..bb496869 --- /dev/null +++ b/admiral/pkg/controller/admiral/node_test.go @@ -0,0 +1,26 @@ +package admiral + +import ( + "github.com/istio-ecosystem/admiral/admiral/pkg/test" + "k8s.io/client-go/tools/clientcmd" + "testing" +) + +func TestNewNodeController(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{}) + handler := test.MockNodeHandler{} + + nodeController, err := NewNodeController(stop, &handler, config) + + if err != nil { + t.Errorf("Unexpected err %v", err) + } + + if nodeController == nil { + t.Errorf("Node controller should never be nil without an error thrown") + } +} diff --git a/admiral/pkg/controller/admiral/pod.go b/admiral/pkg/controller/admiral/pod.go index 8f7f7934..bd9623ea 100644 --- a/admiral/pkg/controller/admiral/pod.go +++ b/admiral/pkg/controller/admiral/pod.go @@ -92,8 +92,8 @@ func (d *PodController) GetPods() ([]*k8sV1.Pod, error) { ns := d.K8sClient.CoreV1().Namespaces() - sidecarInjectionNamespaceFilter := d.labelSet.NamespaceSidecarInjectionLabel+"="+d.labelSet.NamespaceSidecarInjectionLabelValue - istioEnabledNs, err := ns.List(meta_v1.ListOptions{LabelSelector: sidecarInjectionNamespaceFilter}) + namespaceSidecarInjectionLabelFilter := d.labelSet.NamespaceSidecarInjectionLabel+"="+d.labelSet.NamespaceSidecarInjectionLabelValue + istioEnabledNs, err := ns.List(meta_v1.ListOptions{LabelSelector: namespaceSidecarInjectionLabelFilter}) if err != nil { return nil, fmt.Errorf("error getting istio labled namespaces: %v", err) @@ -104,14 +104,22 @@ func (d *PodController) GetPods() ([]*k8sV1.Pod, error) { for _, v := range istioEnabledNs.Items { pods := d.K8sClient.CoreV1().Pods(v.Name) - admiralEnabledLabelFilter := d.labelSet.DeploymentAnnotation +"=true" - podsList, err := pods.List(meta_v1.ListOptions{LabelSelector: admiralEnabledLabelFilter}) + podsList, err := pods.List(meta_v1.ListOptions{}) + if err != nil { + return nil, fmt.Errorf("error listing pods: %v", err) + } + var admiralDeployments []k8sV1.Pod + for _, pod := range podsList.Items { + if pod.Annotations[d.labelSet.DeploymentAnnotation] == "true" { + admiralDeployments = append(admiralDeployments, pod) + } + } if err != nil { return nil, fmt.Errorf("error getting istio labled namespaces: %v", err) } - for _, pi := range podsList.Items { + for _, pi := range admiralDeployments { res = append(res, &pi) } } diff --git a/admiral/pkg/controller/admiral/pod_test.go b/admiral/pkg/controller/admiral/pod_test.go new file mode 100644 index 00000000..f80e9a4f --- /dev/null +++ b/admiral/pkg/controller/admiral/pod_test.go @@ -0,0 +1,125 @@ +package admiral + +import ( + "github.com/google/go-cmp/cmp" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + "github.com/istio-ecosystem/admiral/admiral/pkg/test" + "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes/fake" + "k8s.io/client-go/tools/clientcmd" + "sync" + "testing" + "time" +) + +func TestNewPodController(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{}) + handler := test.MockPodHandler{} + + podController, err := NewPodController(stop, &handler, config, time.Duration(1000)) + + if err != nil { + t.Errorf("Unexpected err %v", err) + } + + if podController == nil { + t.Errorf("Pod controller should never be nil without an error thrown") + } +} + +func TestPodController_GetPods(t *testing.T) { + controller := PodController{ + labelSet: common.LabelSet{ + DeploymentAnnotation: "sidecar.istio.io/inject", + NamespaceSidecarInjectionLabel: "istio-injection", + NamespaceSidecarInjectionLabelValue: "enabled", + AdmiralIgnoreLabel: "admiral-ignore", + }, + } + + client := fake.NewSimpleClientset() + ns := v1.Namespace{} + ns.Labels = map[string]string{"istio-injection": "enabled"} + ns.Name = "test-ns" + + _, err := client.CoreV1().Namespaces().Create(&ns) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + pod := v1.Pod{} + pod.Namespace = "test-ns" + pod.Name="pod" + pod.Labels = map[string]string{"identity": "id", "istio-injected": "true"} + pod.Annotations = map[string]string{"sidecar.istio.io/inject": "true"} + podWithBadLabels := v1.Pod{} + podWithBadLabels.Namespace = "test-ns" + podWithBadLabels.Name="podWithBadLabels" + podWithBadLabels.Labels = map[string]string{"identity": "id", "random-label": "true"} + podWithBadLabels.Annotations = map[string]string{"woo": "yay"} + _, err = client.CoreV1().Pods("test-ns").Create(&pod) + _, err = client.CoreV1().Pods("test-ns").Create(&podWithBadLabels) + + if err != nil { + t.Errorf("Unexpected err %v", err) + } + + controller.K8sClient = client + + podsList, err := controller.GetPods() + + if err != nil { + t.Errorf("Unexpected err %v", err) + } + if !cmp.Equal(podsList[0].Name, pod.Name) || !cmp.Equal(podsList[0].Annotations, pod.Annotations) || !cmp.Equal(podsList[0].Labels, pod.Labels) { + t.Errorf("Incorrect pod found. Mismatch: %v", cmp.Diff(podsList[0], pod)) + } + if len(podsList) != 1 { + t.Errorf("Too many pods found. Expected 1, found %v", podsList) + } + +} + +func TestPodCache_AppendPodToCluster(t *testing.T) { + podCache := podCache{} + podCache.cache = make(map[string]*PodClusterEntry) + podCache.mutex = &sync.Mutex{} + + pod := &v1.Pod{} + pod.Name="foobar" + pod.Namespace = "ns" + pod.Labels = map[string]string{"identity":"my-first-pod"} + + podCache.AppendPodToCluster("ns", pod) + + if podCache.getKey(pod) != "my-first-pod" { + t.Errorf("Incorrect key. Got %v, expected ns", podCache.getKey(pod)) + } + if !cmp.Equal(podCache.Get("ns").Pods["ns"][0], pod) { + t.Errorf("Incorrect pod fount. Diff: %v", cmp.Diff(podCache.Get("ns").Pods["ns"], pod)) + } + + length := len(podCache.Get("ns").Pods["ns"]) + + podCache.AppendPodToCluster("ns", pod) + + if podCache.getKey(pod) != "my-first-pod" { + t.Errorf("Incorrect key. Got %v, expected ns", podCache.getKey(pod)) + } + if !cmp.Equal(podCache.Get("ns").Pods["ns"][0], pod) { + t.Errorf("Incorrect pod fount. Diff: %v", cmp.Diff(podCache.Get("ns").Pods["ns"], pod)) + } + if (length+1) != len(podCache.Get("ns").Pods["ns"]) { + t.Errorf("Didn't add a second pod, expected %v, got %v", length+1, len(podCache.Get("ns").Pods["ns"])) + } + + podCache.Delete(podCache.Get("ns")) + + if podCache.Get("ns") != nil { + t.Errorf("Didn't delete successfully, expected nil, got %v", podCache.Get("ns")) + } +} \ No newline at end of file diff --git a/admiral/pkg/test/mock.go b/admiral/pkg/test/mock.go index 6f9633ce..ead07723 100644 --- a/admiral/pkg/test/mock.go +++ b/admiral/pkg/test/mock.go @@ -73,6 +73,17 @@ func (m *MockServiceHandler) Deleted(obj *k8sCoreV1.Service) { } +type MockPodHandler struct { +} + +func (m MockPodHandler) Added (obj *k8sCoreV1.Pod) { + +} + +func (m MockPodHandler) Deleted (obj *k8sCoreV1.Pod) { + +} + type MockNodeHandler struct { } @@ -94,3 +105,14 @@ func (m *MockDependencyHandler) Added(obj *v1.Dependency) { func (m *MockDependencyHandler) Deleted(obj *v1.Dependency) { } + +type MockGlobalTrafficHandler struct { +} + +func (m *MockGlobalTrafficHandler) Added(obj *v1.GlobalTrafficPolicy) { + +} + +func (m *MockGlobalTrafficHandler) Deleted(obj *v1.GlobalTrafficPolicy) { + +} From 4d7e468457e327996225779bb5908f45f4c2c4b0 Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Tue, 7 Jan 2020 13:29:42 -0800 Subject: [PATCH 07/45] Add Istio blog post link for Admiral blog post Signed-off-by: Madeline --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b6e89dac..a6c6f08f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![CircleCI](https://circleci.com/gh/istio-ecosystem/admiral/tree/master.svg?style=svg)](https://circleci.com/gh/istio-ecosystem/admiral/tree/master) [![codecov](https://codecov.io/gh/istio-ecosystem/admiral/branch/master/graph/badge.svg)](https://codecov.io/gh/istio-ecosystem/admiral) -**Admiral provides automatic configuration for multiple istio deployments to work as a single mesh** +**Admiral provides automatic configuration and service discovery for multicluster Istio service mesh** Istio has a very robust set of multi-cluster capabilities. Managing this configuration across multiple clusters at scale is challenging. Admiral takes an opinionated view on this configuration and provides automatic provisioning and syncing across clusters. This removes the complexity from developers and mesh operators pushing this complexity into automation. @@ -343,5 +343,7 @@ Organizations below are **officially** using Admiral. Please send a PR with your 1. [Stitching a Service Mesh Across Hundreds of Discrete Networks](https://www.youtube.com/watch?v=EWyNbBn1vns) +2. [Multicluster Istio configuration and service discovery using Admiral](https://istio.io/blog/2020/multi-cluster-mesh-automation/) + ## Contributing Refer to [Contributing doc](./CONTRIBUTING.md) From 9a2d73c30ef4220b6bafb58eb8cbb15d4571072d Mon Sep 17 00:00:00 2001 From: Gao Pan Date: Mon, 13 Jan 2020 10:27:56 -0800 Subject: [PATCH 08/45] Added secret update function Added process logic for secret update Signed-off-by: Gao Pan Signed-off-by: Madeline --- admiral/cmd/admiral/cmd/root.go | 2 + .../pkg/controller/secret/secretcontroller.go | 82 +++++++++++-------- 2 files changed, 50 insertions(+), 34 deletions(-) diff --git a/admiral/cmd/admiral/cmd/root.go b/admiral/cmd/admiral/cmd/root.go index cac52050..6a77384b 100644 --- a/admiral/cmd/admiral/cmd/root.go +++ b/admiral/cmd/admiral/cmd/root.go @@ -5,6 +5,7 @@ import ( "flag" "fmt" "github.com/istio-ecosystem/admiral/admiral/pkg/clusters" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" "istio.io/istio/pkg/log" "os" "os/signal" @@ -25,6 +26,7 @@ func GetRootCmd(args []string) *cobra.Command { var () params := clusters.AdmiralParams{} + params.LabelSet = &common.LabelSet{} rootCmd := &cobra.Command{ Use: "Admiral", diff --git a/admiral/pkg/controller/secret/secretcontroller.go b/admiral/pkg/controller/secret/secretcontroller.go index 151b4a7d..56e0913d 100644 --- a/admiral/pkg/controller/secret/secretcontroller.go +++ b/admiral/pkg/controller/secret/secretcontroller.go @@ -145,6 +145,13 @@ func NewController( queue.Add(key) } }, + UpdateFunc: func(oldObj interface{}, newObj interface{}) { + key, err := cache.MetaNamespaceKeyFunc(newObj) + log.Infof("Processing update: %s", key) + if err == nil { + queue.Add(key) + } + }, DeleteFunc: func(obj interface{}) { key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) log.Infof("Processing delete: %s", key) @@ -237,47 +244,54 @@ func (c *Controller) addMemberCluster(secretName string, s *corev1.Secret) { for clusterID, kubeConfig := range s.Data { // clusterID must be unique even across multiple secrets if _, ok := c.cs.remoteClusters[clusterID]; !ok { - if len(kubeConfig) == 0 { - log.Infof("Data '%s' in the secret %s in namespace %s is empty, and disregarded ", - clusterID, secretName, s.ObjectMeta.Namespace) - continue - } + log.Infof("Adding new cluster member: %s", clusterID) + c.cs.remoteClusters[clusterID] = &RemoteCluster{} + c.cs.remoteClusters[clusterID].secretName = secretName + } else { + log.Infof("Cluster %s in the secret %s in namespace %s already exists. Reloading secret...", + clusterID, c.cs.remoteClusters[clusterID].secretName, s.ObjectMeta.Namespace) + } - kubeConfig, err := c.secretResolver.FetchKubeConfig(clusterID, kubeConfig) + if len(kubeConfig) == 0 { + log.Infof("Data '%s' in the secret %s in namespace %s is empty, and disregarded ", + clusterID, secretName, s.ObjectMeta.Namespace) + continue + } - if err != nil { - log.Errorf("Failed to fetch kubeconfig for cluster '%s' using secret resolver: %v, err: %v", - clusterID, c.secretResolver, err) - continue - } + kubeConfig, err := c.secretResolver.FetchKubeConfig(clusterID, kubeConfig) - clusterConfig, err := LoadKubeConfig(kubeConfig) - if err != nil { - log.Infof("Data '%s' in the secret %s in namespace %s is not a kubeconfig: %v", - clusterID, secretName, s.ObjectMeta.Namespace, err) - log.Infof("KubeConfig: '%s'", string(kubeConfig)) - continue - } + if err != nil { + log.Errorf("Failed to fetch kubeconfig for cluster '%s' using secret resolver: %v, err: %v", + clusterID, c.secretResolver, err) + continue + } - log.Infof("Adding new cluster member: %s", clusterID) - c.cs.remoteClusters[clusterID] = &RemoteCluster{} - c.cs.remoteClusters[clusterID].secretName = secretName - clientConfig := clientcmd.NewDefaultClientConfig(*clusterConfig, &clientcmd.ConfigOverrides{}) + clusterConfig, err := LoadKubeConfig(kubeConfig) - var restConfig *rest.Config - restConfig, err = clientConfig.ClientConfig() + if err != nil { + log.Infof("Data '%s' in the secret %s in namespace %s is not a kubeconfig: %v", + clusterID, secretName, s.ObjectMeta.Namespace, err) + log.Infof("KubeConfig: '%s'", string(kubeConfig)) + continue + } - if err != nil { - log.Errorf("error during conversion of secret to client config: %v", err) - } + clientConfig := clientcmd.NewDefaultClientConfig(*clusterConfig, &clientcmd.ConfigOverrides{}) - err = c.addCallback(restConfig, clusterID, 2 * time.Minute) - if err != nil { - log.Errorf("error during create of clusterID: %s %v", clusterID, err) - } - } else { - log.Infof("Cluster %s in the secret %s in namespace %s already exists", - clusterID, c.cs.remoteClusters[clusterID].secretName, s.ObjectMeta.Namespace) + var restConfig *rest.Config + restConfig, err = clientConfig.ClientConfig() + + if err != nil { + log.Errorf("error during conversion of secret to client config: %v", err) + continue + } + + err = c.addCallback(restConfig, clusterID, 2 * time.Minute) + + if err != nil { + log.Errorf("error during secret loading for clusterID: %s %v", clusterID, err) + continue + }else{ + log.Infof("Secret loaded for cluster %s in the secret %s in namespace %s.",clusterID,c.cs.remoteClusters[clusterID].secretName, s.ObjectMeta.Namespace) } } log.Infof("Number of remote clusters: %d", len(c.cs.remoteClusters)) From 25ebf4c258781c9128b458755015fc452412f221 Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Tue, 21 Jan 2020 11:46:30 -0800 Subject: [PATCH 09/45] Add slack channel Signed-off-by: Madeline --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index a6c6f08f..d865f295 100644 --- a/README.md +++ b/README.md @@ -345,5 +345,9 @@ Organizations below are **officially** using Admiral. Please send a PR with your 2. [Multicluster Istio configuration and service discovery using Admiral](https://istio.io/blog/2020/multi-cluster-mesh-automation/) +## Collaboration and Communication + +[Admiral Slack Channel] (https://istio.slack.com/archives/GP0QM9ZJ8) - `Note:` This channel is under Istio slack org, please fill out this [form](https://docs.google.com/forms/d/e/1FAIpQLSfdsupDfOWBtNVvVvXED6ULxtR4UIsYGCH_cQcRr0VcG1ZqQQ/viewform) to get access to Istio slack. + ## Contributing Refer to [Contributing doc](./CONTRIBUTING.md) From 638bcefbadf9123e569d0d7031fff0ea3f5923fb Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Tue, 21 Jan 2020 11:47:11 -0800 Subject: [PATCH 10/45] Add slack channel (updates) Signed-off-by: Madeline --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d865f295..3671c36a 100644 --- a/README.md +++ b/README.md @@ -347,7 +347,7 @@ Organizations below are **officially** using Admiral. Please send a PR with your ## Collaboration and Communication -[Admiral Slack Channel] (https://istio.slack.com/archives/GP0QM9ZJ8) - `Note:` This channel is under Istio slack org, please fill out this [form](https://docs.google.com/forms/d/e/1FAIpQLSfdsupDfOWBtNVvVvXED6ULxtR4UIsYGCH_cQcRr0VcG1ZqQQ/viewform) to get access to Istio slack. +[Admiral Slack Channel](https://istio.slack.com/archives/GP0QM9ZJ8) - `Note:` This channel is under Istio slack org, please fill out this [form](https://docs.google.com/forms/d/e/1FAIpQLSfdsupDfOWBtNVvVvXED6ULxtR4UIsYGCH_cQcRr0VcG1ZqQQ/viewform) to get access to Istio slack. ## Contributing Refer to [Contributing doc](./CONTRIBUTING.md) From 264da18b3414ac88f541ec5917814ecaa90345ae Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Wed, 22 Jan 2020 13:34:14 -0800 Subject: [PATCH 11/45] Fix local SE generation. Fix related bugs. (#59) Fixes https://github.com/istio-ecosystem/admiral/issues/56 Fixes https://github.com/istio-ecosystem/admiral/issues/53 Signed-off-by: Madeline --- admiral/cmd/admiral/cmd/root.go | 4 ++ admiral/pkg/clusters/registry.go | 10 ++--- admiral/pkg/clusters/serviceentry.go | 12 +++--- admiral/pkg/clusters/serviceentry_test.go | 2 +- admiral/pkg/clusters/types.go | 7 +++- admiral/pkg/controller/admiral/deployment.go | 13 +++---- .../pkg/controller/admiral/deployment_test.go | 4 +- admiral/pkg/controller/common/common.go | 23 ++++++++--- admiral/pkg/controller/common/common_test.go | 39 +++++++++++++++++++ 9 files changed, 86 insertions(+), 28 deletions(-) diff --git a/admiral/cmd/admiral/cmd/root.go b/admiral/cmd/admiral/cmd/root.go index 6a77384b..9996270b 100644 --- a/admiral/cmd/admiral/cmd/root.go +++ b/admiral/cmd/admiral/cmd/root.go @@ -25,8 +25,12 @@ func GetRootCmd(args []string) *cobra.Command { var () +<<<<<<< HEAD params := clusters.AdmiralParams{} params.LabelSet = &common.LabelSet{} +======= + params := clusters.AdmiralParams{LabelSet: &common.LabelSet{}} +>>>>>>> Fix local SE generation. Fix related bugs. (#59) rootCmd := &cobra.Command{ Use: "Admiral", diff --git a/admiral/pkg/clusters/registry.go b/admiral/pkg/clusters/registry.go index a18370a8..bd85db0d 100644 --- a/admiral/pkg/clusters/registry.go +++ b/admiral/pkg/clusters/registry.go @@ -274,35 +274,35 @@ func (r *RemoteRegistry) createCacheController(clientConfig *rest.Config, cluste return fmt.Errorf(" Error with Istio controller init: %v", err) } - log.Infof("starting global traffic policy controller custerID: %v", clusterID) + log.Infof("starting global traffic policy controller clusterID: %v", clusterID) rc.GlobalTraffic, err = admiral.NewGlobalTrafficController(stop, &GlobalTrafficHandler{RemoteRegistry: r}, clientConfig, resyncPeriod) if err != nil { return fmt.Errorf(" Error with GlobalTrafficController controller init: %v", err) } - log.Infof("starting deployment controller custerID: %v", clusterID) + log.Infof("starting deployment controller clusterID: %v", clusterID) rc.DeploymentController, err = admiral.NewDeploymentController(stop, &DeploymentHandler{RemoteRegistry: r}, clientConfig, resyncPeriod) if err != nil { return fmt.Errorf(" Error with DeploymentController controller init: %v", err) } - log.Infof("starting pod controller custerID: %v", clusterID) + log.Infof("starting pod controller clusterID: %v", clusterID) rc.PodController, err = admiral.NewPodController(stop, &PodHandler{RemoteRegistry: r}, clientConfig, resyncPeriod) if err != nil { return fmt.Errorf(" Error with PodController controller init: %v", err) } - log.Infof("starting node controller custerID: %v", clusterID) + log.Infof("starting node controller clusterID: %v", clusterID) rc.NodeController, err = admiral.NewNodeController(stop, &NodeHandler{RemoteRegistry: r}, clientConfig) if err != nil { return fmt.Errorf(" Error with NodeController controller init: %v", err) } - log.Infof("starting service controller custerID: %v", clusterID) + log.Infof("starting service controller clusterID: %v", clusterID) rc.ServiceController, err = admiral.NewServiceController(stop, &ServiceHandler{RemoteRegistry: r}, clientConfig, resyncPeriod) if err != nil { diff --git a/admiral/pkg/clusters/serviceentry.go b/admiral/pkg/clusters/serviceentry.go index 6d80afef..0944ef58 100644 --- a/admiral/pkg/clusters/serviceentry.go +++ b/admiral/pkg/clusters/serviceentry.go @@ -102,7 +102,7 @@ func createServiceEntry(identifier string, rc *RemoteController, config AdmiralP return tmpSe } -func createServiceEntryForNewServiceOrPod(namespace string, sourceIdentity string, remoteRegistry *RemoteRegistry, syncNamespace string) map[string]*networking.ServiceEntry { +func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, remoteRegistry *RemoteRegistry) map[string]*networking.ServiceEntry { //create a service entry, destination rule and virtual service in the local cluster sourceServices := make(map[string]*k8sV1.Service) @@ -116,15 +116,15 @@ func createServiceEntryForNewServiceOrPod(namespace string, sourceIdentity strin deployment := rc.DeploymentController.Cache.Get(sourceIdentity) - if deployment == nil || deployment.Deployments[namespace] == nil { + if deployment == nil || deployment.Deployments[env] == nil { continue } - deploymentInstance := deployment.Deployments[namespace] + deploymentInstance := deployment.Deployments[env] - serviceInstance := getServiceForDeployment(rc, deploymentInstance[0], namespace) + serviceInstance := getServiceForDeployment(rc, deploymentInstance[0], env) - cname = common.GetCname(deploymentInstance[0], "identity", cname) + cname = common.GetCname(deploymentInstance[0], remoteRegistry.config.LabelSet.WorkloadIdentityLabel, cname) remoteRegistry.AdmiralCache.IdentityClusterCache.Put(sourceIdentity, rc.ClusterID, rc.ClusterID) remoteRegistry.AdmiralCache.CnameClusterCache.Put(cname, rc.ClusterID, rc.ClusterID) @@ -133,7 +133,7 @@ func createServiceEntryForNewServiceOrPod(namespace string, sourceIdentity strin sourceDeployments[rc.ClusterID] = deploymentInstance[0] - createServiceEntry("identity", rc, remoteRegistry.config, remoteRegistry.AdmiralCache, deploymentInstance[0], serviceEntries) + createServiceEntry(remoteRegistry.config.LabelSet.WorkloadIdentityLabel, rc, remoteRegistry.config, remoteRegistry.AdmiralCache, deploymentInstance[0], serviceEntries) } diff --git a/admiral/pkg/clusters/serviceentry_test.go b/admiral/pkg/clusters/serviceentry_test.go index f2b45462..623d55a9 100644 --- a/admiral/pkg/clusters/serviceentry_test.go +++ b/admiral/pkg/clusters/serviceentry_test.go @@ -117,7 +117,7 @@ func TestCreateServiceEntryForNewServiceOrPod(t *testing.T) { }) rr.remoteControllers["test.cluster"] = rc - createServiceEntryForNewServiceOrPod("test", "bar", rr, "sync") + createServiceEntryForNewServiceOrPod("test", "bar", rr) } diff --git a/admiral/pkg/clusters/types.go b/admiral/pkg/clusters/types.go index 4e0452b0..297bb434 100644 --- a/admiral/pkg/clusters/types.go +++ b/admiral/pkg/clusters/types.go @@ -155,7 +155,9 @@ func (pc *DeploymentHandler) Added(obj *k8sAppsV1.Deployment) { return } - createServiceEntryForNewServiceOrPod(obj.Namespace, globalIdentifier, pc.RemoteRegistry, pc.RemoteRegistry.config.SyncNamespace) + env := common.GetEnv(obj) + + createServiceEntryForNewServiceOrPod(env, globalIdentifier, pc.RemoteRegistry) } func (pc *DeploymentHandler) Deleted(obj *k8sAppsV1.Deployment) { @@ -172,7 +174,8 @@ func (pc *PodHandler) Added(obj *k8sV1.Pod) { return } - createServiceEntryForNewServiceOrPod(obj.Namespace, globalIdentifier, pc.RemoteRegistry, pc.RemoteRegistry.config.SyncNamespace) + //TODO Skip pod events until GTP is implemented + //createServiceEntryForNewServiceOrPod(obj.Namespace, globalIdentifier, pc.RemoteRegistry) } func (pc *PodHandler) Deleted(obj *k8sV1.Pod) { diff --git a/admiral/pkg/controller/admiral/deployment.go b/admiral/pkg/controller/admiral/deployment.go index 7c4c2a6e..c896d8d1 100644 --- a/admiral/pkg/controller/admiral/deployment.go +++ b/admiral/pkg/controller/admiral/deployment.go @@ -74,17 +74,16 @@ func (p *deploymentCache) AppendDeploymentToCluster(key string, deployment *k8sA } p.cache[v.Identity] = v } + env := common.GetEnv(deployment) + envDeployments := v.Deployments[env] - //TODO this is assuming globally unquie names name which might not alway be the case. This would need a cluster name too - namespaceDeployments := v.Deployments[deployment.Namespace] - - if namespaceDeployments == nil { - namespaceDeployments = make([]*k8sAppsV1.Deployment, 0) + if envDeployments == nil { + envDeployments = make([]*k8sAppsV1.Deployment, 0) } - namespaceDeployments = append(namespaceDeployments, deployment) + envDeployments = append(envDeployments, deployment) - v.Deployments[deployment.Namespace] = namespaceDeployments + v.Deployments[env] = envDeployments } diff --git a/admiral/pkg/controller/admiral/deployment_test.go b/admiral/pkg/controller/admiral/deployment_test.go index 4c84c9fa..5619898c 100644 --- a/admiral/pkg/controller/admiral/deployment_test.go +++ b/admiral/pkg/controller/admiral/deployment_test.go @@ -74,9 +74,9 @@ func TestDeploymentController_Added(t *testing.T) { } } else if len(depController.Cache.cache)==0 && c.expectedCacheSize != 0 { t.Errorf("Unexpectedly empty cache. Length should have been %v but was 0", c.expectedCacheSize) - }else if len(depController.Cache.cache["id"].Deployments) < 1 && len(depController.Cache.cache["id"].Deployments[""]) != c.expectedCacheSize { + }else if len(depController.Cache.cache["id"].Deployments) < 1 && len(depController.Cache.cache["id"].Deployments[common.Default]) != c.expectedCacheSize { t.Errorf("Deployment controller cache the wrong size. Got %v, expected %v", len(depController.Cache.cache["id"].Deployments[""]), c.expectedCacheSize) - } else if depController.Cache.cache["id"].Deployments[""][0] != &deployment { + } else if depController.Cache.cache["id"].Deployments[common.Default][0] != &deployment { t.Errorf("Incorrect deployment added to deployment controller cache. Got %v expected %v", depController.Cache.cache["id"].Deployments[""][0], deployment) } diff --git a/admiral/pkg/controller/common/common.go b/admiral/pkg/controller/common/common.go index 0afafd85..c108d01e 100644 --- a/admiral/pkg/controller/common/common.go +++ b/admiral/pkg/controller/common/common.go @@ -4,6 +4,7 @@ import ( "github.com/sirupsen/logrus" k8sAppsV1 "k8s.io/api/apps/v1" k8sV1 "k8s.io/api/core/v1" + "strings" ) const ( @@ -24,6 +25,7 @@ const ( NodeRegionLabel = "failure-domain.beta.kubernetes.io/region" SpiffePrefix = "spiffe://" SidecarEnabledPorts = "traffic.sidecar.istio.io/includeInboundPorts" + Default = "default" ) func GetPodGlobalIdentifier(pod *k8sV1.Pod) string { @@ -49,11 +51,7 @@ func DefaultGlobalIdentifier() string { // GetCname returns cname in the format ..global, Ex: stage.Admiral.services.registry.global func GetCname(deployment *k8sAppsV1.Deployment, identifier string, nameSuffix string) string { - var environment = deployment.Spec.Template.Labels[Env] - if len(environment) == 0 { - environment = "default" - logrus.Warnf("%v label missing on %v in namespace %v. Using 'default' as the value.", Env, deployment.Name, deployment.Namespace) - } + var environment = GetEnv(deployment) alias := deployment.Spec.Template.Labels[identifier] if len(alias) == 0 { logrus.Warnf("%v label missing on service %v in namespace %v. Falling back to annotation to create cname.", identifier, deployment.Name, deployment.Namespace) @@ -66,6 +64,21 @@ func GetCname(deployment *k8sAppsV1.Deployment, identifier string, nameSuffix st return environment + Sep + alias + Sep + nameSuffix } +func GetEnv(deployment *k8sAppsV1.Deployment) string { + var environment = deployment.Spec.Template.Labels[Env] + if len(environment) == 0 { + environment = deployment.Spec.Template.Annotations[Env] + } + if len(environment) == 0 { + splitNamespace := strings.Split(deployment.Namespace, Dash) + environment = splitNamespace[len(splitNamespace) - 1] + } + if len(environment) == 0 { + environment = Default + } + return environment +} + // GetSAN returns SAN for a service entry in the format spiffe:///, Ex: spiffe://subdomain.domain.com/Admiral.platform.mesh.server func GetSAN(domain string, deployment *k8sAppsV1.Deployment, identifier string) string { identifierVal := deployment.Spec.Template.Labels[identifier] diff --git a/admiral/pkg/controller/common/common_test.go b/admiral/pkg/controller/common/common_test.go index c726c611..ed92c19b 100644 --- a/admiral/pkg/controller/common/common_test.go +++ b/admiral/pkg/controller/common/common_test.go @@ -198,3 +198,42 @@ func TestGetPodGlobalIdentifier(t *testing.T) { }) } } + +func TestGetEnv(t *testing.T) { + + testCases := []struct { + name string + deployment k8sAppsV1.Deployment + expected string + }{ + { + name: "should return default env", + deployment: k8sAppsV1.Deployment{Spec: k8sAppsV1.DeploymentSpec{Template: v12.PodTemplateSpec{ObjectMeta: v1.ObjectMeta{Labels: map[string]string{}}}}}, + expected: Default, + }, + { + name: "should return valid env from label", + deployment: k8sAppsV1.Deployment{Spec: k8sAppsV1.DeploymentSpec{Template: v12.PodTemplateSpec{ObjectMeta: v1.ObjectMeta{Annotations: map[string]string{}, Labels: map[string]string{"env": "stage"}}}}}, + expected: "stage", + }, + { + name: "should return valid env from annotation", + deployment: k8sAppsV1.Deployment{Spec: k8sAppsV1.DeploymentSpec{Template: v12.PodTemplateSpec{ObjectMeta: v1.ObjectMeta{Annotations: map[string]string{"env": "stage"}}}}}, + expected: "stage", + }, + { + name: "should return env from namespace suffix", + deployment: k8sAppsV1.Deployment{Spec: k8sAppsV1.DeploymentSpec{Template: v12.PodTemplateSpec{ObjectMeta: v1.ObjectMeta{Labels: map[string]string{}}}}, ObjectMeta: v1.ObjectMeta{Namespace: "uswest2-prd"}}, + expected: "prd", + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + env := GetEnv(&c.deployment) + if !(env == c.expected) { + t.Errorf("Wanted Cname: %s, got: %s", c.expected, env) + } + }) + } +} From af1a95344936ec1e08e0f1ea6599a0a78d173513 Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Thu, 23 Jan 2020 18:00:40 -0800 Subject: [PATCH 12/45] Add multicluster example and bug fixes (#54) Fixes https://github.com/istio-ecosystem/admiral/issues/28 Signed-off-by: Madeline --- Makefile | 1 + README.md | 150 +++++++++++++----- admiral/pkg/clusters/registry.go | 17 +- admiral/pkg/clusters/registry_test.go | 14 +- admiral/pkg/clusters/serviceentry.go | 14 +- admiral/pkg/clusters/serviceentry_test.go | 28 +++- admiral/pkg/clusters/types.go | 1 + admiral/pkg/controller/admiral/deployment.go | 5 +- .../pkg/controller/admiral/deployment_test.go | 6 +- admiral/pkg/controller/admiral/pod.go | 5 +- admiral/pkg/controller/admiral/pod_test.go | 4 +- admiral/pkg/controller/common/common.go | 4 +- admiral/pkg/controller/common/common_test.go | 5 + install/admiral/base/kustomization.yaml | 2 +- .../demosinglecluster/kustomization.yaml | 4 + install/sample/base/greeting.yaml | 2 +- install/sample/overlays/remote/greeting.yaml | 35 ++++ .../sample/overlays/remote/kustomization.yaml | 11 ++ 18 files changed, 231 insertions(+), 77 deletions(-) create mode 100644 install/sample/overlays/remote/greeting.yaml create mode 100644 install/sample/overlays/remote/kustomization.yaml diff --git a/Makefile b/Makefile index 1b5412fe..b3f226f4 100644 --- a/Makefile +++ b/Makefile @@ -112,6 +112,7 @@ gen-yaml: kustomize build ./install/admiral/overlays/demosinglecluster/ > ./out/yaml/demosinglecluster.yaml kustomize build ./install/admiralremote/base/ > ./out/yaml/remotecluster.yaml kustomize build ./install/sample/base/ > ./out/yaml/sample.yaml + kustomize build ./install/sample/overlays/remote > ./out/yaml/remotecluster_sample.yaml cp ./install/sample/sample_dep.yaml ./out/yaml/sample_dep.yaml cp ./install/scripts/cluster-secret.sh ./out/scripts/cluster-secret.sh cp ./install/scripts/redirect-dns.sh ./out/scripts/redirect-dns.sh diff --git a/README.md b/README.md index 3671c36a..7a79f652 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ Istio has a very robust set of multi-cluster capabilities. Managing this config ### Prerequisite -One or more k8s clusters. +One or more k8s clusters will need the following steps executed -**Example setup for a K8s cluster** +#### Install the below utilities `Note`: If running in windows, a bash shell is required (cygwin) @@ -25,24 +25,24 @@ One or more k8s clusters. * Install [helm](https://github.com/helm/helm/blob/master/docs/install.md) * Install [wget](https://www.gnu.org/software/wget/) -``` -#Download & extract Istio +#### Install Istio +``` #Download -wget https://github.com/istio/istio/releases/download/1.3.3/istio-1.3.3-osx.tar.gz +wget https://github.com/istio/istio/releases/download/1.4.3/istio-1.4.3-osx.tar.gz OR -wget https://github.com/istio/istio/releases/download/1.3.3/istio-1.3.3-linux.tar.gz +wget https://github.com/istio/istio/releases/download/1.4.3/istio-1.4.3-linux.tar.gz OR -wget https://github.com/istio/istio/releases/download/1.3.3/istio-1.3.3-win.tar.gz +wget https://github.com/istio/istio/releases/download/1.4.3/istio-1.4.3-win.tar.gz #Extract -tar -xf istio-1.3.3-osx.tar.gz +tar -xf istio-1.4.3-osx.tar.gz OR -tar -xf istio-1.3.3-linux.tar.gz +tar -xf istio-1.4.3-linux.tar.gz OR -tar -xf istio-1.3.3-win.tar.gz +tar -xf istio-1.4.3-win.tar.gz ``` ``` @@ -54,15 +54,15 @@ kubectl create ns istio-system #Create k8s secret to be used by Citadel for mTLS cert generation kubectl create secret generic cacerts -n istio-system \ - --from-file=istio-1.3.3/samples/certs/ca-cert.pem \ - --from-file=istio-1.3.3/samples/certs/ca-key.pem \ - --from-file=istio-1.3.3/samples/certs/root-cert.pem \ - --from-file=istio-1.3.3/samples/certs/cert-chain.pem + --from-file=istio-1.4.3/samples/certs/ca-cert.pem \ + --from-file=istio-1.4.3/samples/certs/ca-key.pem \ + --from-file=istio-1.4.3/samples/certs/root-cert.pem \ + --from-file=istio-1.4.3/samples/certs/cert-chain.pem ``` ``` #Generate, install and verify Istio CRDs -helm template istio-1.3.3/install/kubernetes/helm/istio-init --name istio-init --namespace istio-system | kubectl apply -f - +helm template istio-1.4.3/install/kubernetes/helm/istio-init --name istio-init --namespace istio-system | kubectl apply -f - #Make sure Istio crds are installed @@ -71,35 +71,54 @@ kubectl get crds | grep 'istio.io' | wc -l ``` #Generate & Install Istio -helm template istio-1.3.3/install/kubernetes/helm/istio --name istio --namespace istio-system \ - -f istio-1.3.3/install/kubernetes/helm/istio/example-values/values-istio-multicluster-gateways.yaml | kubectl apply -f - +helm template istio-1.4.3/install/kubernetes/helm/istio --name istio --namespace istio-system \ + -f istio-1.4.3/install/kubernetes/helm/istio/example-values/values-istio-multicluster-gateways.yaml | kubectl apply -f - #Verify that istio pods are up kubectl get pods -n istio-system ``` +#### DNS setup +In a k8s cluster, you will have a DNS component that would resolve names. Admiral generates names ending in global (Ex: `stage.greeting.global`) which can be resolved by istiocoredns (as its watching Istio ServiceEntries created by Admiral with those names) installed as part of Istio. +So you have to point DNS resolution for names ending in `global` to point to `ClusterIp` of istiocoredns service. The below step is to point coredns in a k8s cluster to istiocoredns. If you are using kube-dns, you can tweak this script. + +```Note: The below script wipes out existing codedns config map, please manually edit it if you want to try this in a cluster with real services/traffic``` + +``` +#Run the below script for having coredns point to istiocoredns for dns lookups of names ending in global + +./admiral-install-v0.1-beta/scripts/redirect-dns.sh +``` + +#### Remove envoy cluster rewrite filter +Delete Istio's envoy filter for translating `global` to `svc.cluster.local` at istio-ingressgateway because we don't need that as Admiral generates Service Entries for cross cluster communication to just work! +``` +# Delete envoy filter for translating `global` to `svc.cluster.local` +kubectl delete envoyfilter istio-multicluster-ingressgateway -n istio-system +``` + `Reference:` [K8s cluster installed with Istio_replicated control planes](https://istio.io/docs/setup/install/multicluster/gateways/#deploy-the-istio-control-plane-in-each-cluster) -## Examples +## Example Installations & Demos ### Single cluster -#### Setup Admiral +#### Install/Run Admiral ``` #Download and extract admiral -wget https://github.com/istio-ecosystem/admiral/releases/download/v0.1-alpha/admiral-install-v0.1-alpha.tar.gz -tar xvf admiral-install-v0.1-alpha.tar.gz +wget https://github.com/istio-ecosystem/admiral/releases/download/v0.1-beta/admiral-install-v0.1-beta.tar.gz +tar xvf admiral-install-v0.1-beta.tar.gz ``` ``` #Install admiral -kubectl apply -f ./admiral-install-v0.1-alpha/yaml/remotecluster.yaml -kubectl apply -f ./admiral-install-v0.1-alpha/yaml/demosinglecluster.yaml +kubectl apply -f ./admiral-install-v0.1-beta/yaml/remotecluster.yaml +kubectl apply -f ./admiral-install-v0.1-beta/yaml/demosinglecluster.yaml #Verify admiral is running @@ -110,28 +129,24 @@ kubectl get pods -n admiral #Create the secret for admiral to monitor. #Since this is for a single cluster demo the remote and local context are the same -./admiral-install-v0.1-alpha/scripts/cluster-secret.sh $KUBECONFIG $KUBECONFIG admiral - +./admiral-install-v0.1-beta/scripts/cluster-secret.sh $KUBECONFIG $KUBECONFIG admiral +``` +``` #Verify the secret - kubectl get secrets -n admiral ``` -``` -#Point hosts ending in global to be resolved by istio coredns -./admiral-install-v0.1-alpha/scripts/redirect-dns.sh -``` -#### Setup Sample Apps +#### Deploy Sample Services ``` #Install test services -kubectl apply -f ./admiral-install-v0.1-alpha/yaml/sample.yaml +kubectl apply -f ./admiral-install-v0.1-beta/yaml/sample.yaml ``` ``` -#Install the dependency CR +#Install the dependency CR (this is optional) -kubectl apply -f ./admiral-install-v0.1-alpha/yaml/sample_dep.yaml +kubectl apply -f ./admiral-install-v0.1-beta/yaml/sample_dep.yaml #Verify that admiral created service names for 'greeting' service @@ -139,13 +154,13 @@ kubectl get serviceentry -n admiral-sync ``` -#### Test +#### Demo +Now, run the command below that uses the CNAME generated by Admiral ``` kubectl exec --namespace=sample -it $(kubectl get pod -l "app=webapp" --namespace=sample -o jsonpath='{.items[0].metadata.name}') -c webapp -- curl -v http://default.greeting.global ``` - #### Generated configuration Admiral generated Istio configuration. @@ -195,9 +210,72 @@ spec: number: 80 protocol: http resolution: DNS +``` + + +### Multicluster + +Finish steps from Single Cluster to have Admiral running and ready to watch other clusters (lets call them remote clusters) which we will be setting in the steps below. + +Let's call the cluster used in Single cluster set up `Cluster 1`. Now we will use the steps below to add `Cluster 2` to the mesh and have it monitored by Admiral + +Finish the steps from `Prerequisites` section for `Cluster 2` + +#### Add Cluster 2 to Admiral's watcher +``` +# Set CLUSTER_1 env variable +export CLUSTER_1= +# Set CLUSTER_2 env variable +export CLUSTER_2= ``` +``` +# Switch kubectx to Cluster 2 +export KUBECONFIG=$CLUSTER_2 +# Create admiral role and bindings on Cluster 2 +kubectl apply -f ./admiral-install-v0.1-beta/yaml/remotecluster.yaml +``` + +``` +#Switch kubectx to Cluster 1 +export KUBECONFIG=$CLUSTER_1 + +# Create the k8s secret for admiral to monitor Cluster 2. +./admiral-install-v0.1-beta/scripts/cluster-secret.sh $CLUSTER_1 $CLUSTER_2 admiral +``` + +At this point, admiral is watching `Cluster 2` + +#### Deploy Sample Services in Cluster 2 +``` +#Switch kubectx to Cluster 2 +export KUBECONFIG=$CLUSTER_2 + +#Install test services in Cluster 2 + +kubectl apply -f ./admiral-install-v0.1-beta/yaml/remotecluster_sample.yaml +``` + +#### Verify + +``` +#Switch kubectx to Cluster 1 +export KUBECONFIG=$CLUSTER_1 + +# Verify that the ServiceEntry for greeting service in Cluster 1 now has second endpoint (Cluster 2's istio-ingressgateway address) +kubectl get serviceentry default.greeting.global-se -n admiral-sync -o yaml +``` + +#### Demo + +Now run the below request multiple times and see the requests being load balanced between local (Cluster 1) and remote (Cluster 2) instances of greeting service (You can see the response payload change based on which greeting's instance served the request) + +``` +kubectl exec --namespace=sample -it $(kubectl get pod -l "app=webapp" --namespace=sample -o jsonpath='{.items[0].metadata.name}') -c webapp -- curl -v http://default.greeting.global +``` + + ## Admiral Architecture ![alt text](https://user-images.githubusercontent.com/35096265/65183155-b8244b00-da17-11e9-9f2d-cce5a96fe2e8.png "Admiral Architecture") diff --git a/admiral/pkg/clusters/registry.go b/admiral/pkg/clusters/registry.go index bd85db0d..9685bfbc 100644 --- a/admiral/pkg/clusters/registry.go +++ b/admiral/pkg/clusters/registry.go @@ -79,7 +79,7 @@ func handleDependencyRecord(identifier string, sourceIdentity string, admiralCac } //TODO pass deployment - tmpSe := createServiceEntry(identifier, rc, config, admiralCache, deployment[0], serviceEntries) + tmpSe := createServiceEntry(rc, config, admiralCache, deployment[0], serviceEntries) if tmpSe == nil { continue @@ -135,15 +135,18 @@ func getDestinationRule(host string) *networking.DestinationRule { TrafficPolicy: &networking.TrafficPolicy{Tls: &networking.TLSSettings{Mode: networking.TLSSettings_ISTIO_MUTUAL}}} } -func getServiceForDeployment(rc *RemoteController, deployment *k8sAppsV1.Deployment, namespace string) *k8sV1.Service { +func getServiceForDeployment(rc *RemoteController, deployment *k8sAppsV1.Deployment) *k8sV1.Service { - cachedService := rc.ServiceController.Cache.Get(namespace) + if deployment == nil { + return nil + } + cachedService := rc.ServiceController.Cache.Get(deployment.Namespace) if cachedService == nil { return nil } var matchedService *k8sV1.Service - for _, service := range cachedService.Service[namespace] { + for _, service := range cachedService.Service[deployment.Namespace] { var match = true for lkey, lvalue := range service.Spec.Selector { value, ok := deployment.Spec.Selector.MatchLabels[lkey] @@ -282,14 +285,14 @@ func (r *RemoteRegistry) createCacheController(clientConfig *rest.Config, cluste } log.Infof("starting deployment controller clusterID: %v", clusterID) - rc.DeploymentController, err = admiral.NewDeploymentController(stop, &DeploymentHandler{RemoteRegistry: r}, clientConfig, resyncPeriod) + rc.DeploymentController, err = admiral.NewDeploymentController(stop, &DeploymentHandler{RemoteRegistry: r}, clientConfig, resyncPeriod, r.config.LabelSet) if err != nil { return fmt.Errorf(" Error with DeploymentController controller init: %v", err) } log.Infof("starting pod controller clusterID: %v", clusterID) - rc.PodController, err = admiral.NewPodController(stop, &PodHandler{RemoteRegistry: r}, clientConfig, resyncPeriod) + rc.PodController, err = admiral.NewPodController(stop, &PodHandler{RemoteRegistry: r}, clientConfig, resyncPeriod, r.config.LabelSet) if err != nil { return fmt.Errorf(" Error with PodController controller init: %v", err) @@ -531,7 +534,7 @@ func createDestinationRuleForLocal(remoteController *RemoteController, localDrNa break } - serviceInstance := getServiceForDeployment(remoteController, deploymentInstance, deploymentInstance.Namespace) + serviceInstance := getServiceForDeployment(remoteController, deploymentInstance) cname := common.GetCname(deploymentInstance, identifier, nameSuffix) if cname == destinationRule.Host { diff --git a/admiral/pkg/clusters/registry_test.go b/admiral/pkg/clusters/registry_test.go index 4a58391e..d92357d9 100644 --- a/admiral/pkg/clusters/registry_test.go +++ b/admiral/pkg/clusters/registry_test.go @@ -6,6 +6,7 @@ import ( depModel "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/model" "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/istio" "github.com/istio-ecosystem/admiral/admiral/pkg/test" networking "istio.io/api/networking/v1alpha3" @@ -120,7 +121,7 @@ func TestCreateDestinationRuleForLocalNoDeployLabel(t *testing.T) { Host: "localhost", } - d, e := admiral.NewDeploymentController(make(chan struct{}), &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300)) + d, e := admiral.NewDeploymentController(make(chan struct{}), &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300), &common.LabelSet{}) if e != nil { t.Fail() @@ -177,7 +178,7 @@ func createMockRemoteController(f func(interface{})) (*RemoteController, error) Host: "localhost", } stop := make(chan struct{}) - d, e := admiral.NewDeploymentController(stop, &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300)) + d, e := admiral.NewDeploymentController(stop, &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300), &common.LabelSet{}) s, e := admiral.NewServiceController(stop, &test.MockServiceHandler{}, &config, time.Second*time.Duration(300)) n, e := admiral.NewNodeController(stop, &test.MockNodeHandler{}, &config) @@ -462,36 +463,31 @@ func TestGetServiceForDeployment(t *testing.T) { deploymentWithSelector := k8sAppsV1.Deployment{} deploymentWithSelector.Name = "dep2" deploymentWithSelector.Namespace = "under-test" - deploymentWithSelector.Spec.Selector = &metav1.LabelSelector{} - deploymentWithSelector.Spec.Selector.MatchLabels = map[string]string{"under-test":"true"} + deploymentWithSelector.Spec.Selector = &metav1.LabelSelector{MatchLabels: map[string]string{"under-test":"true"}} //Struct of test case info. Name is required. testCases := []struct { name string controller *RemoteController deployment *k8sAppsV1.Deployment - namespace string expectedService *k8sCoreV1.Service }{ { name: "Should return nil with nothing in the cache", controller:baseRc, deployment:nil, - namespace:"foobar", expectedService:nil, }, { name: "Should not match if selectors don't match", controller:rcWithService, deployment:&deploymentWithNoSelector, - namespace:"under-test", expectedService:nil, }, { name: "Should return proper service", controller:rcWithService, deployment:&deploymentWithSelector, - namespace:"under-test", expectedService:&service, }, } @@ -499,7 +495,7 @@ func TestGetServiceForDeployment(t *testing.T) { //Run the test for every provided case for _, c := range testCases { t.Run(c.name, func(t *testing.T) { - resultingService := getServiceForDeployment(c.controller, c.deployment, c.namespace) + resultingService := getServiceForDeployment(c.controller, c.deployment) if resultingService == nil && c.expectedService == nil { //perfect } else { diff --git a/admiral/pkg/clusters/serviceentry.go b/admiral/pkg/clusters/serviceentry.go index 0944ef58..f742b033 100644 --- a/admiral/pkg/clusters/serviceentry.go +++ b/admiral/pkg/clusters/serviceentry.go @@ -20,12 +20,10 @@ import ( "time" ) -const cnameSuffix = "mesh" - -func createServiceEntry(identifier string, rc *RemoteController, config AdmiralParams, admiralCache *AdmiralCache, +func createServiceEntry(rc *RemoteController, config AdmiralParams, admiralCache *AdmiralCache, destDeployment *k8sAppsV1.Deployment, serviceEntries map[string]*networking.ServiceEntry) *networking.ServiceEntry { - globalFqdn := common.GetCname(destDeployment, identifier, cnameSuffix) + globalFqdn := common.GetCname(destDeployment, config.LabelSet.WorkloadIdentityLabel, config.HostnameSuffix) //Handling retries for getting/putting service entries from/in cache @@ -61,9 +59,9 @@ func createServiceEntry(identifier string, rc *RemoteController, config AdmiralP var san []string if config.EnableSAN { - tmpSan := common.GetSAN(config.SANPrefix, destDeployment, identifier) + tmpSan := common.GetSAN(config.SANPrefix, destDeployment, config.LabelSet.WorkloadIdentityLabel) if len(tmpSan) > 0 { - san = []string{common.GetSAN(config.SANPrefix, destDeployment, identifier)} + san = []string{common.GetSAN(config.SANPrefix, destDeployment, config.LabelSet.WorkloadIdentityLabel)} } } else { san = nil @@ -122,7 +120,7 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem deploymentInstance := deployment.Deployments[env] - serviceInstance := getServiceForDeployment(rc, deploymentInstance[0], env) + serviceInstance := getServiceForDeployment(rc, deploymentInstance[0]) cname = common.GetCname(deploymentInstance[0], remoteRegistry.config.LabelSet.WorkloadIdentityLabel, cname) @@ -133,7 +131,7 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem sourceDeployments[rc.ClusterID] = deploymentInstance[0] - createServiceEntry(remoteRegistry.config.LabelSet.WorkloadIdentityLabel, rc, remoteRegistry.config, remoteRegistry.AdmiralCache, deploymentInstance[0], serviceEntries) + createServiceEntry(rc, remoteRegistry.config, remoteRegistry.AdmiralCache, deploymentInstance[0], serviceEntries) } diff --git a/admiral/pkg/clusters/serviceentry_test.go b/admiral/pkg/clusters/serviceentry_test.go index 623d55a9..6b008b7f 100644 --- a/admiral/pkg/clusters/serviceentry_test.go +++ b/admiral/pkg/clusters/serviceentry_test.go @@ -296,13 +296,15 @@ func buildFakeConfigMapFromAddressStore(addressStore *ServiceEntryAddressStore, func TestCreateServiceEntry(t *testing.T) { admiralCache := AdmiralCache{} + localAddress := common.LocalAddressPrefix + ".10.1" + cnameIdentityCache := sync.Map{} cnameIdentityCache.Store("dev.bar.global", "bar") admiralCache.CnameIdentityCache = &cnameIdentityCache admiralCache.ServiceEntryAddressStore = &ServiceEntryAddressStore{ - EntryAddresses: map[string]string{"e2e.my-first-service.mesh-se":"127.0.0.1"}, - Addresses: []string{"127'.0.0.1"}, + EntryAddresses: map[string]string{"e2e.my-first-service.mesh-se":localAddress}, + Addresses: []string{localAddress}, } admiralCache.CnameClusterCache = common.NewMapOfMaps() @@ -320,18 +322,34 @@ func TestCreateServiceEntry(t *testing.T) { params := AdmiralParams{ EnableSAN: true, SANPrefix: "prefix", + LabelSet:&common.LabelSet{WorkloadIdentityLabel:"identity"}, + HostnameSuffix: "mesh", + } + + cacheWithEntry := ServiceEntryAddressStore{ + EntryAddresses: map[string]string{"e2e.my-first-service.mesh": localAddress}, + Addresses: []string{localAddress}, } + cacheController := &test.FakeConfigMapController{ + GetError: nil, + PutError: nil, + ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithEntry, "123"), + } + + admiralCache.ConfigMapController = cacheController + deployment := v12.Deployment{} deployment.Spec.Template.Labels = map[string]string{"env":"e2e", "identity":"my-first-service", } - resultingEntry := createServiceEntry("identity", rc, params, &admiralCache, &deployment, map[string]*networking.ServiceEntry{}) + resultingEntry := createServiceEntry(rc, params, &admiralCache, &deployment, map[string]*networking.ServiceEntry{}) if resultingEntry.Hosts[0] != "e2e.my-first-service.mesh" { t.Errorf("Host mismatch. Got: %v, expected: e2e.my-first-service.mesh", resultingEntry.Hosts[0]) } - if resultingEntry.Addresses[0] != "127.0.0.1" { - t.Errorf("Address mismatch. Got: %v, expected: 127.0.0.1", resultingEntry.Addresses[0]) + + if resultingEntry.Addresses[0] != localAddress { + t.Errorf("Address mismatch. Got: %v, expected: " + localAddress, resultingEntry.Addresses[0]) } } \ No newline at end of file diff --git a/admiral/pkg/clusters/types.go b/admiral/pkg/clusters/types.go index 297bb434..f83d2b71 100644 --- a/admiral/pkg/clusters/types.go +++ b/admiral/pkg/clusters/types.go @@ -37,6 +37,7 @@ func (b AdmiralParams) String() string { fmt.Sprintf("DependenciesNamespace=%v ", b.DependenciesNamespace) + fmt.Sprintf("EnableSAN=%v ", b.EnableSAN) + fmt.Sprintf("SANPrefix=%v ", b.SANPrefix) + + fmt.Sprintf("LabelSet=%v ", b.LabelSet) + fmt.Sprintf("SecretResolver=%v ", b.SecretResolver) } diff --git a/admiral/pkg/controller/admiral/deployment.go b/admiral/pkg/controller/admiral/deployment.go index c896d8d1..afefb073 100644 --- a/admiral/pkg/controller/admiral/deployment.go +++ b/admiral/pkg/controller/admiral/deployment.go @@ -31,7 +31,7 @@ type DeploymentController struct { Cache *deploymentCache informer cache.SharedIndexInformer ctl *Controller - labelSet common.LabelSet + labelSet *common.LabelSet } type deploymentCache struct { @@ -126,10 +126,11 @@ func (d *DeploymentController) GetDeployments() ([]*k8sAppsV1.Deployment, error) return res, nil } -func NewDeploymentController(stopCh <-chan struct{}, handler DeploymentHandler, config *rest.Config, resyncPeriod time.Duration) (*DeploymentController, error) { +func NewDeploymentController(stopCh <-chan struct{}, handler DeploymentHandler, config *rest.Config, resyncPeriod time.Duration, labelSet *common.LabelSet) (*DeploymentController, error) { deploymentController := DeploymentController{} deploymentController.DeploymentHandler = handler + deploymentController.labelSet = labelSet deploymentCache := deploymentCache{} deploymentCache.cache = make(map[string]*DeploymentClusterEntry) diff --git a/admiral/pkg/controller/admiral/deployment_test.go b/admiral/pkg/controller/admiral/deployment_test.go index 5619898c..aee75f77 100644 --- a/admiral/pkg/controller/admiral/deployment_test.go +++ b/admiral/pkg/controller/admiral/deployment_test.go @@ -28,7 +28,7 @@ func TestDeploymentController_Added(t *testing.T) { depController := DeploymentController{ DeploymentHandler: &mdh, Cache: &cache, - labelSet: labelset, + labelSet: &labelset, } deployment := k8sAppsV1.Deployment{} deployment.Spec.Template.Labels = map[string]string{"identity": "id", "istio-injected": "true"} @@ -90,7 +90,7 @@ func TestDeploymentController_Added(t *testing.T) { func TestDeploymentController_GetDeployments(t *testing.T) { depController := DeploymentController{ - labelSet: common.LabelSet{ + labelSet: &common.LabelSet{ DeploymentAnnotation: "sidecar.istio.io/inject", NamespaceSidecarInjectionLabel: "istio-injection", NamespaceSidecarInjectionLabelValue: "enabled", @@ -154,7 +154,7 @@ func TestNewDeploymentController(t *testing.T) { stop := make(chan struct{}) depHandler := test.MockDeploymentHandler{} - depCon, err := NewDeploymentController(stop, &depHandler, config, time.Duration(1000)) + depCon, err := NewDeploymentController(stop, &depHandler, config, time.Duration(1000), &common.LabelSet{}) if depCon == nil { t.Errorf("Deployment controller should not be nil") diff --git a/admiral/pkg/controller/admiral/pod.go b/admiral/pkg/controller/admiral/pod.go index bd9623ea..01ae27d7 100644 --- a/admiral/pkg/controller/admiral/pod.go +++ b/admiral/pkg/controller/admiral/pod.go @@ -32,7 +32,7 @@ type PodController struct { Cache *podCache informer cache.SharedIndexInformer ctl *Controller - labelSet common.LabelSet + labelSet *common.LabelSet } type podCache struct { @@ -127,10 +127,11 @@ func (d *PodController) GetPods() ([]*k8sV1.Pod, error) { return res, nil } -func NewPodController(stopCh <-chan struct{}, handler PodHandler, config *rest.Config, resyncPeriod time.Duration) (*PodController, error) { +func NewPodController(stopCh <-chan struct{}, handler PodHandler, config *rest.Config, resyncPeriod time.Duration, labelSet *common.LabelSet) (*PodController, error) { podController := PodController{} podController.PodHandler = handler + podController.labelSet = labelSet podCache := podCache{} podCache.cache = make(map[string]*PodClusterEntry) diff --git a/admiral/pkg/controller/admiral/pod_test.go b/admiral/pkg/controller/admiral/pod_test.go index f80e9a4f..61563615 100644 --- a/admiral/pkg/controller/admiral/pod_test.go +++ b/admiral/pkg/controller/admiral/pod_test.go @@ -20,7 +20,7 @@ func TestNewPodController(t *testing.T) { stop := make(chan struct{}) handler := test.MockPodHandler{} - podController, err := NewPodController(stop, &handler, config, time.Duration(1000)) + podController, err := NewPodController(stop, &handler, config, time.Duration(1000), &common.LabelSet{}) if err != nil { t.Errorf("Unexpected err %v", err) @@ -33,7 +33,7 @@ func TestNewPodController(t *testing.T) { func TestPodController_GetPods(t *testing.T) { controller := PodController{ - labelSet: common.LabelSet{ + labelSet: &common.LabelSet{ DeploymentAnnotation: "sidecar.istio.io/inject", NamespaceSidecarInjectionLabel: "istio-injection", NamespaceSidecarInjectionLabelValue: "enabled", diff --git a/admiral/pkg/controller/common/common.go b/admiral/pkg/controller/common/common.go index c108d01e..1dbfb795 100644 --- a/admiral/pkg/controller/common/common.go +++ b/admiral/pkg/controller/common/common.go @@ -71,7 +71,9 @@ func GetEnv(deployment *k8sAppsV1.Deployment) string { } if len(environment) == 0 { splitNamespace := strings.Split(deployment.Namespace, Dash) - environment = splitNamespace[len(splitNamespace) - 1] + if len(splitNamespace) > 1 { + environment = splitNamespace[len(splitNamespace)-1] + } } if len(environment) == 0 { environment = Default diff --git a/admiral/pkg/controller/common/common_test.go b/admiral/pkg/controller/common/common_test.go index ed92c19b..304837e9 100644 --- a/admiral/pkg/controller/common/common_test.go +++ b/admiral/pkg/controller/common/common_test.go @@ -226,6 +226,11 @@ func TestGetEnv(t *testing.T) { deployment: k8sAppsV1.Deployment{Spec: k8sAppsV1.DeploymentSpec{Template: v12.PodTemplateSpec{ObjectMeta: v1.ObjectMeta{Labels: map[string]string{}}}}, ObjectMeta: v1.ObjectMeta{Namespace: "uswest2-prd"}}, expected: "prd", }, + { + name: "should return default when namespace doesn't have blah..region-env format", + deployment: k8sAppsV1.Deployment{Spec: k8sAppsV1.DeploymentSpec{Template: v12.PodTemplateSpec{ObjectMeta: v1.ObjectMeta{Labels: map[string]string{}}}}, ObjectMeta: v1.ObjectMeta{Namespace: "sample"}}, + expected: Default, + }, } for _, c := range testCases { diff --git a/install/admiral/base/kustomization.yaml b/install/admiral/base/kustomization.yaml index ec8acd33..17515e01 100644 --- a/install/admiral/base/kustomization.yaml +++ b/install/admiral/base/kustomization.yaml @@ -2,7 +2,7 @@ apiversion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - - name: hub.docker.com/r/admiralproj/admiral + - name: docker.io/admiralproj/admiral newTag: latest resources: diff --git a/install/admiral/overlays/demosinglecluster/kustomization.yaml b/install/admiral/overlays/demosinglecluster/kustomization.yaml index 84d4d8b4..edf6350e 100644 --- a/install/admiral/overlays/demosinglecluster/kustomization.yaml +++ b/install/admiral/overlays/demosinglecluster/kustomization.yaml @@ -3,6 +3,10 @@ kind: Kustomization namespace: admiral +images: + - name: docker.io/admiralproj/admiral + newTag: v0.1-beta + bases: - ../../base diff --git a/install/sample/base/greeting.yaml b/install/sample/base/greeting.yaml index 45d2fd4a..7d4c3f21 100644 --- a/install/sample/base/greeting.yaml +++ b/install/sample/base/greeting.yaml @@ -46,7 +46,7 @@ spec: sidecar.istio.io/inject: "true" labels: app: greeting - identity: greetin + identity: greeting spec: containers: - name: greeting diff --git a/install/sample/overlays/remote/greeting.yaml b/install/sample/overlays/remote/greeting.yaml new file mode 100644 index 00000000..537ff390 --- /dev/null +++ b/install/sample/overlays/remote/greeting.yaml @@ -0,0 +1,35 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: nginx-conf +data: + nginx.conf: | + user nginx; + worker_processes 3; + error_log /var/log/nginx/error.log; + events { + worker_connections 10240; + } + http { + log_format main + 'remote_addr:$remote_addr\t' + 'time_local:$time_local\t' + 'method:$request_method\t' + 'uri:$request_uri\t' + 'host:$host\t' + 'status:$status\t' + 'bytes_sent:$body_bytes_sent\t' + 'referer:$http_referer\t' + 'useragent:$http_user_agent\t' + 'forwardedfor:$http_x_forwarded_for\t' + 'request_time:$request_time'; + access_log /var/log/nginx/access.log main; + server { + listen 80; + server_name _; + location / { + return 200 "Remote cluster says: Hello World! - Admiral!!"; + } + } + } +--- \ No newline at end of file diff --git a/install/sample/overlays/remote/kustomization.yaml b/install/sample/overlays/remote/kustomization.yaml new file mode 100644 index 00000000..c3e697b3 --- /dev/null +++ b/install/sample/overlays/remote/kustomization.yaml @@ -0,0 +1,11 @@ +apiversion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: sample + +bases: + - ../../base + +patchesStrategicMerge: + - greeting.yaml + From 935301f888b7575c03ae822b92cb507443233da3 Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Fri, 24 Jan 2020 12:14:51 -0800 Subject: [PATCH 13/45] Fix docker image publish (#61) Signed-off-by: Madeline --- Makefile | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index b3f226f4..d0f199db 100644 --- a/Makefile +++ b/Makefile @@ -78,21 +78,36 @@ crd-gen: build-linux: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) -o $(BINARY_NAME) -v $(MAIN_PATH_ADMIRAL) +#set tag to `latest` if master branch and TAG is not set +#set tag to commit sha if TAG is not set and is not master branch set-tag: -ifeq ($(strip $(TAG)),) +ifndef TAG +ifeq ($(BRANCH),master) override TAG=latest endif +endif +ifndef TAG +override TAG=$(SHA) +endif docker-build: set-tag #NOTE: Assumes binary has already been built (admiral) docker build -t $(IMAGE):$(TAG) -f ./admiral/docker/Dockerfile.admiral . -docker-publish: set-tag -ifeq ($(BRANCH),master) +docker-publish: echo "$(DOCKER_PASS)" | docker login -u $(DOCKER_USER) --password-stdin +ifeq ($(TAG),) + echo "This is not a Tag/Release, skipping docker publish" +else docker push $(IMAGE):$(TAG) +endif +#no tag set and its master branch, in this case publish `latest` tag +ifeq ($(TAG),) +ifeq ($(BRANCH),master) + docker push $(IMAGE):latest else - echo "Skipping publish for branch: $(BRANCH), artifacts are published only from master branch" + echo "This is not master branch, skipping to publish 'latest' tag" +endif endif download-kustomize: From f146041d3c52596231d6b08127e3764a3edd0b2c Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Mon, 27 Jan 2020 11:15:18 -0800 Subject: [PATCH 14/45] Update slack channel Signed-off-by: Madeline --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7a79f652..8978f96f 100644 --- a/README.md +++ b/README.md @@ -425,7 +425,7 @@ Organizations below are **officially** using Admiral. Please send a PR with your ## Collaboration and Communication -[Admiral Slack Channel](https://istio.slack.com/archives/GP0QM9ZJ8) - `Note:` This channel is under Istio slack org, please fill out this [form](https://docs.google.com/forms/d/e/1FAIpQLSfdsupDfOWBtNVvVvXED6ULxtR4UIsYGCH_cQcRr0VcG1ZqQQ/viewform) to get access to Istio slack. +[Admiral Slack Channel](https://istio.slack.com/archives/CT3F18T08) - `Note:` This channel is under Istio slack org, please fill out this [form](https://docs.google.com/forms/d/e/1FAIpQLSfdsupDfOWBtNVvVvXED6ULxtR4UIsYGCH_cQcRr0VcG1ZqQQ/viewform) to get access to Istio slack. ## Contributing Refer to [Contributing doc](./CONTRIBUTING.md) From 902768f14773d46622b242e923f973fda1274f5b Mon Sep 17 00:00:00 2001 From: josephpeacock <51184065+josephpeacock@users.noreply.github.com> Date: Wed, 12 Feb 2020 15:30:00 -0800 Subject: [PATCH 15/45] Fix configmap roles (#67) * Fixing rbac issues reported in https://github.com/istio-ecosystem/admiral/issues/64 Signed-off-by: Joe Peacock * improving logging for SE address generation Signed-off-by: Joe Peacock Signed-off-by: Madeline --- admiral/pkg/clusters/serviceentry.go | 5 +++++ install/admiral/base/role_bindings.yaml | 14 ++++++++++++++ install/admiral/base/roles.yaml | 14 +++++++++++++- install/admiral/base/service_accounts.yaml | 1 - 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/admiral/pkg/clusters/serviceentry.go b/admiral/pkg/clusters/serviceentry.go index f742b033..4185d985 100644 --- a/admiral/pkg/clusters/serviceentry.go +++ b/admiral/pkg/clusters/serviceentry.go @@ -37,6 +37,11 @@ func createServiceEntry(rc *RemoteController, config AdmiralParams, admiralCache for err==nil && counter Date: Wed, 19 Feb 2020 11:00:40 -0800 Subject: [PATCH 16/45] =?UTF-8?q?Formalizing=20the=20behavior=20to=20fall?= =?UTF-8?q?=20back=20to=20annotation=20if=20label=20isn't=20pr=E2=80=A6=20?= =?UTF-8?q?(#68)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * formalizing the behavior to fall back to annotation if label isn't present Signed-off-by: Joe Peacock * Removing the hardcoded references to a default identity label Signed-off-by: Joe Peacock * Nil check Signed-off-by: Joe Peacock * Refactoring to add the rest of the admiral params to the singleton (and to enforce singleton-ness Signed-off-by: Joe Peacock * Another test Signed-off-by: Joe Peacock * coverage bump Signed-off-by: Joe Peacock * Whoops broke a test Signed-off-by: Joe Peacock Signed-off-by: Madeline --- admiral/cmd/admiral/cmd/root.go | 11 +-- admiral/pkg/clusters/registry.go | 65 +++++++++-------- admiral/pkg/clusters/registry_test.go | 39 +++++----- admiral/pkg/clusters/serviceentry.go | 26 +++---- admiral/pkg/clusters/serviceentry_test.go | 13 +--- admiral/pkg/clusters/types.go | 35 +-------- admiral/pkg/controller/admiral/configmap.go | 6 +- .../pkg/controller/admiral/configmap_test.go | 6 +- admiral/pkg/controller/admiral/deployment.go | 4 +- .../pkg/controller/admiral/deployment_test.go | 2 +- admiral/pkg/controller/admiral/pod.go | 4 +- admiral/pkg/controller/admiral/pod_test.go | 2 +- admiral/pkg/controller/common/common.go | 32 +++++---- admiral/pkg/controller/common/common_test.go | 11 ++- admiral/pkg/controller/common/config.go | 72 +++++++++++++++++++ .../controller/common/configInitializer.go | 25 +++++++ admiral/pkg/controller/common/config_test.go | 72 +++++++++++++++++++ admiral/pkg/controller/common/types.go | 30 +++++++- admiral/pkg/controller/util/util.go | 2 +- 19 files changed, 318 insertions(+), 139 deletions(-) create mode 100644 admiral/pkg/controller/common/config.go create mode 100644 admiral/pkg/controller/common/configInitializer.go create mode 100644 admiral/pkg/controller/common/config_test.go diff --git a/admiral/cmd/admiral/cmd/root.go b/admiral/cmd/admiral/cmd/root.go index 9996270b..040af170 100644 --- a/admiral/cmd/admiral/cmd/root.go +++ b/admiral/cmd/admiral/cmd/root.go @@ -25,12 +25,7 @@ func GetRootCmd(args []string) *cobra.Command { var () -<<<<<<< HEAD - params := clusters.AdmiralParams{} - params.LabelSet = &common.LabelSet{} -======= - params := clusters.AdmiralParams{LabelSet: &common.LabelSet{}} ->>>>>>> Fix local SE generation. Fix related bugs. (#59) + params := common.AdmiralParams{LabelSet: &common.LabelSet{}} rootCmd := &cobra.Command{ Use: "Admiral", @@ -89,8 +84,8 @@ func GetRootCmd(args []string) *cobra.Command { "The label value, on a namespace, which tells Istio to perform sidecar injection") rootCmd.PersistentFlags().StringVar(¶ms.HostnameSuffix, "hostname_suffix", "global", "The hostname suffix to customize the cname generated by admiral. Default suffix value will be \"global\"") - rootCmd.PersistentFlags().StringVar(¶ms.LabelSet.WorkloadIdentityLabel, "workload_identity_label", "identity", - "The workload identity label key, on deployment which holds identity value used to generate cname by admiral. Default label key will be \"identity\"") + rootCmd.PersistentFlags().StringVar(¶ms.LabelSet.WorkloadIdentityKey, "workload_identity_key", "identity", + "The workload identity key, on deployment which holds identity value used to generate cname by admiral. Default label key will be \"identity\" Admiral will look for a label with this key. If present, that will be used. If not, it will try an annotation (for use cases where an identity is longer than 63 chars)") loggingOptions.AttachCobraFlags(rootCmd) return rootCmd diff --git a/admiral/pkg/clusters/registry.go b/admiral/pkg/clusters/registry.go index 9685bfbc..906eda09 100644 --- a/admiral/pkg/clusters/registry.go +++ b/admiral/pkg/clusters/registry.go @@ -40,7 +40,7 @@ func updateIdentityDependencyCache(sourceIdentity string, identityDependencyCach log.Infof(LogFormat, "Update", "dependency-cache", dr.Name, "", "Updated=true namespace="+dr.Namespace) } -func handleDependencyRecord(identifier string, sourceIdentity string, admiralCache *AdmiralCache, rcs map[string]*RemoteController, config AdmiralParams, obj *v1.Dependency) { +func handleDependencyRecord(identifier string, sourceIdentity string, admiralCache *AdmiralCache, rcs map[string]*RemoteController, obj *v1.Dependency) { destinationIdentitys := obj.Spec.Destinations @@ -79,7 +79,7 @@ func handleDependencyRecord(identifier string, sourceIdentity string, admiralCac } //TODO pass deployment - tmpSe := createServiceEntry(rc, config, admiralCache, deployment[0], serviceEntries) + tmpSe := createServiceEntry(rc, admiralCache, deployment[0], serviceEntries) if tmpSe == nil { continue @@ -110,7 +110,7 @@ func handleDependencyRecord(identifier string, sourceIdentity string, admiralCac } //add service entries for all dependencies in source cluster - AddServiceEntriesWithDr(admiralCache, sourceClusters, rcs, serviceEntries, config.SyncNamespace) + AddServiceEntriesWithDr(admiralCache, sourceClusters, rcs, serviceEntries) } func getIstioResourceName(host string, suffix string) string { @@ -187,10 +187,12 @@ func getDependentClusters(dependents *common.Map, identityClusterCache *common.M return dependentClusters } -func InitAdmiral(ctx context.Context, params AdmiralParams) (*RemoteRegistry, error) { +func InitAdmiral(ctx context.Context, params common.AdmiralParams) (*RemoteRegistry, error) { log.Infof("Initializing Admiral with params: %v", params) + common.InitializeConfig(params) + w := RemoteRegistry{ ctx: ctx, } @@ -205,7 +207,6 @@ func InitAdmiral(ctx context.Context, params AdmiralParams) (*RemoteRegistry, er return nil, fmt.Errorf(" Error with dependency controller init: %v", err) } - w.config = params w.remoteControllers = make(map[string]*RemoteController) w.AdmiralCache = &AdmiralCache{ @@ -218,14 +219,14 @@ func InitAdmiral(ctx context.Context, params AdmiralParams) (*RemoteRegistry, er SubsetServiceEntryIdentityCache: &sync.Map{}, ServiceEntryAddressStore: &ServiceEntryAddressStore{EntryAddresses: map[string]string{}, Addresses: []string{},}} - configMapController, err := admiral.NewConfigMapController(w.config.KubeconfigPath, w.config.SyncNamespace) + configMapController, err := admiral.NewConfigMapController() if err != nil { return nil, fmt.Errorf(" Error with configmap controller init: %v", err) } w.AdmiralCache.ConfigMapController = configMapController loadServiceEntryCacheData(w.AdmiralCache.ConfigMapController, w.AdmiralCache) - err = createSecretController(ctx, &w, params) + err = createSecretController(ctx, &w) if err != nil { return nil, fmt.Errorf(" Error with secret control init: %v", err) } @@ -235,10 +236,10 @@ func InitAdmiral(ctx context.Context, params AdmiralParams) (*RemoteRegistry, er return &w, nil } -func createSecretController(ctx context.Context, w *RemoteRegistry, params AdmiralParams) error { +func createSecretController(ctx context.Context, w *RemoteRegistry) error { var err error - w.secretClient, err = admiral.K8sClientFromPath(params.KubeconfigPath) + w.secretClient, err = admiral.K8sClientFromPath(common.GetKubeconfigPath()) if err != nil { return fmt.Errorf("could not create K8s client: %v", err) } @@ -246,8 +247,8 @@ func createSecretController(ctx context.Context, w *RemoteRegistry, params Admir err = secret.StartSecretController(w.secretClient, w.createCacheController, w.deleteCacheController, - w.config.ClusterRegistriesNamespace, - ctx, params.SecretResolver) + common.GetClusterRegistriesNamespace(), + ctx, common.GetSecretResolver()) if err != nil { return fmt.Errorf("could not start secret controller: %v", err) @@ -285,14 +286,14 @@ func (r *RemoteRegistry) createCacheController(clientConfig *rest.Config, cluste } log.Infof("starting deployment controller clusterID: %v", clusterID) - rc.DeploymentController, err = admiral.NewDeploymentController(stop, &DeploymentHandler{RemoteRegistry: r}, clientConfig, resyncPeriod, r.config.LabelSet) + rc.DeploymentController, err = admiral.NewDeploymentController(stop, &DeploymentHandler{RemoteRegistry: r}, clientConfig, resyncPeriod) if err != nil { return fmt.Errorf(" Error with DeploymentController controller init: %v", err) } log.Infof("starting pod controller clusterID: %v", clusterID) - rc.PodController, err = admiral.NewPodController(stop, &PodHandler{RemoteRegistry: r}, clientConfig, resyncPeriod, r.config.LabelSet) + rc.PodController, err = admiral.NewPodController(stop, &PodHandler{RemoteRegistry: r}, clientConfig, resyncPeriod) if err != nil { return fmt.Errorf(" Error with PodController controller init: %v", err) @@ -323,6 +324,7 @@ func (r *RemoteRegistry) createCacheController(clientConfig *rest.Config, cluste func (r *RemoteRegistry) createIstioController(clientConfig *rest.Config, opts istioKube.ControllerOptions, remoteController *RemoteController, stop <-chan struct{}, clusterID string) error { configClient, err := istio.NewClient(clientConfig, "", istio.IstioConfigTypes, opts.DomainSuffix) + syncNamespace := common.GetSyncNamespace() if err != nil { return fmt.Errorf("error creating istio client to the remote clusterId: %s error:%v", clusterID, err) } @@ -333,7 +335,7 @@ func (r *RemoteRegistry) createIstioController(clientConfig *rest.Config, opts i clusterId := remoteController.ClusterID - if m.Namespace == r.config.SyncNamespace { + if m.Namespace == common.GetSyncNamespace() { log.Infof(LogFormat, "Event", e.String(), m.Name, clusterId, "Skipping the namespace: "+m.Namespace) return } @@ -361,11 +363,11 @@ func (r *RemoteRegistry) createIstioController(clientConfig *rest.Config, opts i if istio.EventDelete == e { log.Infof(LogFormat, "Delete", istioModel.DestinationRule.Type, m.Name, clusterId, "Success") - rc.IstioConfigStore.Delete(istioModel.DestinationRule.Type, m.Name, r.config.SyncNamespace) + rc.IstioConfigStore.Delete(istioModel.DestinationRule.Type, m.Name, syncNamespace) } else { - exist := rc.IstioConfigStore.Get(istioModel.VirtualService.Type, m.Name, r.config.SyncNamespace) + exist := rc.IstioConfigStore.Get(istioModel.VirtualService.Type, m.Name, syncNamespace) //change destination host for all http routes .. to same as host on the virtual service for _, httpRoute := range virtualService.Http { @@ -382,7 +384,7 @@ func (r *RemoteRegistry) createIstioController(clientConfig *rest.Config, opts i } } - addUpdateIstioResource(rc, m, exist, istioModel.VirtualService.Type, r.config.SyncNamespace) + addUpdateIstioResource(rc, m, exist, istioModel.VirtualService.Type, syncNamespace) } } @@ -399,7 +401,7 @@ func (r *RemoteRegistry) createIstioController(clientConfig *rest.Config, opts i var localIdentityId string - if m.Namespace == r.config.SyncNamespace || m.Namespace == common.NamespaceKubeSystem { + if m.Namespace == syncNamespace || m.Namespace == common.NamespaceKubeSystem { log.Infof(LogFormat, "Event", e.String(), m.Name, clusterId, "Skipping the namespace: "+m.Namespace) return } @@ -437,7 +439,7 @@ func (r *RemoteRegistry) createIstioController(clientConfig *rest.Config, opts i var drServiceEntries = make(map[string]*networking.ServiceEntry) - exist := rc.IstioConfigStore.Get(istioModel.ServiceEntry.Type, basicSEName, r.config.SyncNamespace) + exist := rc.IstioConfigStore.Get(istioModel.ServiceEntry.Type, basicSEName, syncNamespace) var identityId = "" @@ -463,36 +465,36 @@ func (r *RemoteRegistry) createIstioController(clientConfig *rest.Config, opts i if istio.EventDelete == e { - rc.IstioConfigStore.Delete(istioModel.DestinationRule.Type, m.Name, r.config.SyncNamespace) + rc.IstioConfigStore.Delete(istioModel.DestinationRule.Type, m.Name, syncNamespace) log.Infof(LogFormat, "Delete", istioModel.ServiceEntry.Type, m.Name, clusterId, "success") - rc.IstioConfigStore.Delete(istioModel.ServiceEntry.Type, seName, r.config.SyncNamespace) + rc.IstioConfigStore.Delete(istioModel.ServiceEntry.Type, seName, syncNamespace) log.Infof(LogFormat, "Delete", istioModel.ServiceEntry.Type, seName, clusterId, "success") for _, subset := range destinationRule.Subsets { sseName := seName + common.Dash + subset.Name - rc.IstioConfigStore.Delete(istioModel.ServiceEntry.Type, sseName, r.config.SyncNamespace) + rc.IstioConfigStore.Delete(istioModel.ServiceEntry.Type, sseName, syncNamespace) log.Infof(LogFormat, "Delete", istioModel.ServiceEntry.Type, sseName, clusterId, "success") } - rc.IstioConfigStore.Delete(istioModel.DestinationRule.Type, localDrName, r.config.SyncNamespace) + rc.IstioConfigStore.Delete(istioModel.DestinationRule.Type, localDrName, syncNamespace) log.Infof(LogFormat, "Delete", istioModel.DestinationRule.Type, localDrName, clusterId, "success") } else { - exist := rc.IstioConfigStore.Get(istioModel.DestinationRule.Type, m.Name, r.config.SyncNamespace) + exist := rc.IstioConfigStore.Get(istioModel.DestinationRule.Type, m.Name, syncNamespace) //copy destination rule only to other clusters if dependentCluster != clusterId { - addUpdateIstioResource(rc, m, exist, istioModel.DestinationRule.Type, r.config.SyncNamespace) + addUpdateIstioResource(rc, m, exist, istioModel.DestinationRule.Type, syncNamespace) } if drServiceEntries != nil { for _seName, se := range drServiceEntries { - existsServiceEntry = rc.IstioConfigStore.Get(istioModel.ServiceEntry.Type, _seName, r.config.SyncNamespace) - newServiceEntry, err = createIstioConfig(istio.ServiceEntryProto, se, _seName, r.config.SyncNamespace) + existsServiceEntry = rc.IstioConfigStore.Get(istioModel.ServiceEntry.Type, _seName, syncNamespace) + newServiceEntry, err = createIstioConfig(istio.ServiceEntryProto, se, _seName, syncNamespace) if err != nil { log.Warnf(LogErrFormat, "Create", istioModel.ServiceEntry.Type, seName, clusterId, err) } if newServiceEntry != nil { - addUpdateIstioResource(rc, *newServiceEntry, existsServiceEntry, istioModel.ServiceEntry.Type, r.config.SyncNamespace) + addUpdateIstioResource(rc, *newServiceEntry, existsServiceEntry, istioModel.ServiceEntry.Type, syncNamespace) } //cache the subset service entries for updating them later for pod events if dependentCluster == clusterId && se.Resolution == networking.ServiceEntry_STATIC { @@ -503,7 +505,7 @@ func (r *RemoteRegistry) createIstioController(clientConfig *rest.Config, opts i if dependentCluster == clusterId { //we need a destination rule with local fqdn for destination rules created with cnames to work in local cluster - createDestinationRuleForLocal(remoteController, localDrName, localIdentityId, clusterId, destinationRule, r.config.SyncNamespace, r.config.HostnameSuffix, r.config.LabelSet.WorkloadIdentityLabel) + createDestinationRuleForLocal(remoteController, localDrName, localIdentityId, clusterId, destinationRule) } } @@ -518,8 +520,11 @@ func (r *RemoteRegistry) createIstioController(clientConfig *rest.Config, opts i } func createDestinationRuleForLocal(remoteController *RemoteController, localDrName string, identityId string, clusterId string, - destinationRule *networking.DestinationRule, syncNamespace string, nameSuffix string, identifier string) { + destinationRule *networking.DestinationRule) { + syncNamespace := common.GetSyncNamespace() + nameSuffix := common.GetHostnameSuffix() + identifier := common.GetWorkloadIdentifier() deployment := remoteController.DeploymentController.Cache.Get(identityId) if deployment == nil || len(deployment.Deployments) == 0 { diff --git a/admiral/pkg/clusters/registry_test.go b/admiral/pkg/clusters/registry_test.go index d92357d9..f76a9dd3 100644 --- a/admiral/pkg/clusters/registry_test.go +++ b/admiral/pkg/clusters/registry_test.go @@ -20,6 +20,7 @@ import ( "time" ) + func TestDeleteCacheControllerThatDoesntExist(t *testing.T) { w := RemoteRegistry{ @@ -121,7 +122,7 @@ func TestCreateDestinationRuleForLocalNoDeployLabel(t *testing.T) { Host: "localhost", } - d, e := admiral.NewDeploymentController(make(chan struct{}), &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300), &common.LabelSet{}) + d, e := admiral.NewDeploymentController(make(chan struct{}), &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300)) if e != nil { t.Fail() @@ -144,7 +145,7 @@ func TestCreateDestinationRuleForLocalNoDeployLabel(t *testing.T) { }, } - createDestinationRuleForLocal(&rc, "local.name", "identity", "cluster1", &des, "sync", ".global", "identity") + createDestinationRuleForLocal(&rc, "local.name", "identity", "cluster1", &des) } @@ -169,7 +170,7 @@ func TestCreateDestinationRuleForLocal(t *testing.T) { }, } - createDestinationRuleForLocal(rc, "local.name", "bar", "cluster1", &des, "sync", ".global", "identity") + createDestinationRuleForLocal(rc, "local.name", "bar", "cluster1", &des) } @@ -178,7 +179,7 @@ func createMockRemoteController(f func(interface{})) (*RemoteController, error) Host: "localhost", } stop := make(chan struct{}) - d, e := admiral.NewDeploymentController(stop, &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300), &common.LabelSet{}) + d, e := admiral.NewDeploymentController(stop, &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300)) s, e := admiral.NewServiceController(stop, &test.MockServiceHandler{}, &config, time.Second*time.Duration(300)) n, e := admiral.NewNodeController(stop, &test.MockNodeHandler{}, &config) @@ -231,23 +232,19 @@ func createMockRemoteController(f func(interface{})) (*RemoteController, error) func TestCreateSecretController(t *testing.T) { - p := AdmiralParams{ - KubeconfigPath: "testdata/fake.config", - } - rr := RemoteRegistry{} - err := createSecretController(context.Background(), &rr, p) + err := createSecretController(context.Background(), &rr) if err != nil { t.Fail() } - p = AdmiralParams{ - KubeconfigPath: "fail", - } + common.SetKubeconfigPath("fail") rr = RemoteRegistry{} - err = createSecretController(context.Background(), &rr, p) + err = createSecretController(context.Background(), &rr) + + common.SetKubeconfigPath("testdata/fake.config") if err == nil { t.Fail() @@ -256,10 +253,12 @@ func TestCreateSecretController(t *testing.T) { func TestInitAdmiral(t *testing.T) { - p := AdmiralParams{ + p := common.AdmiralParams{ KubeconfigPath: "testdata/fake.config", + LabelSet: &common.LabelSet{}, } + rr, err := InitAdmiral(context.Background(), p) if err != nil { @@ -268,11 +267,15 @@ func TestInitAdmiral(t *testing.T) { if len(rr.remoteControllers) != 0 { t.Fail() } + + if common.GetWorkloadIdentifier() != "identity" { + t.Errorf("Workload identity label override failed. Expected \"identity\", got %v", common.GetWorkloadIdentifier()) + } } func TestAdded(t *testing.T) { - p := AdmiralParams{ + p := common.AdmiralParams{ KubeconfigPath: "testdata/fake.config", } rr, _ := InitAdmiral(context.Background(), p) @@ -307,7 +310,7 @@ func TestAdded(t *testing.T) { func TestCreateIstioController(t *testing.T) { - p := AdmiralParams{ + p := common.AdmiralParams{ KubeconfigPath: "testdata/fake.config", } rr, _ := InitAdmiral(context.Background(), p) @@ -342,7 +345,7 @@ func TestMakeVirtualService(t *testing.T) { func TestDeploymentHandler(t *testing.T) { - p := AdmiralParams{ + p := common.AdmiralParams{ KubeconfigPath: "testdata/fake.config", } @@ -387,7 +390,7 @@ func TestDeploymentHandler(t *testing.T) { func TestPodHandler(t *testing.T) { - p := AdmiralParams{ + p := common.AdmiralParams{ KubeconfigPath: "testdata/fake.config", } diff --git a/admiral/pkg/clusters/serviceentry.go b/admiral/pkg/clusters/serviceentry.go index 4185d985..bf77b501 100644 --- a/admiral/pkg/clusters/serviceentry.go +++ b/admiral/pkg/clusters/serviceentry.go @@ -20,10 +20,11 @@ import ( "time" ) -func createServiceEntry(rc *RemoteController, config AdmiralParams, admiralCache *AdmiralCache, +func createServiceEntry(rc *RemoteController, admiralCache *AdmiralCache, destDeployment *k8sAppsV1.Deployment, serviceEntries map[string]*networking.ServiceEntry) *networking.ServiceEntry { - globalFqdn := common.GetCname(destDeployment, config.LabelSet.WorkloadIdentityLabel, config.HostnameSuffix) + workloadIdentityKey := common.GetWorkloadIdentifier() + globalFqdn := common.GetCname(destDeployment, workloadIdentityKey, common.GetHostnameSuffix()) //Handling retries for getting/putting service entries from/in cache @@ -63,10 +64,10 @@ func createServiceEntry(rc *RemoteController, config AdmiralParams, admiralCache } var san []string - if config.EnableSAN { - tmpSan := common.GetSAN(config.SANPrefix, destDeployment, config.LabelSet.WorkloadIdentityLabel) + if common.GetEnableSAN() { + tmpSan := common.GetSAN(common.GetSANPrefix(), destDeployment, workloadIdentityKey) if len(tmpSan) > 0 { - san = []string{common.GetSAN(config.SANPrefix, destDeployment, config.LabelSet.WorkloadIdentityLabel)} + san = []string{common.GetSAN(common.GetSANPrefix(), destDeployment, workloadIdentityKey)} } } else { san = nil @@ -127,7 +128,7 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem serviceInstance := getServiceForDeployment(rc, deploymentInstance[0]) - cname = common.GetCname(deploymentInstance[0], remoteRegistry.config.LabelSet.WorkloadIdentityLabel, cname) + cname = common.GetCname(deploymentInstance[0], common.GetWorkloadIdentifier(), cname) remoteRegistry.AdmiralCache.IdentityClusterCache.Put(sourceIdentity, rc.ClusterID, rc.ClusterID) remoteRegistry.AdmiralCache.CnameClusterCache.Put(cname, rc.ClusterID, rc.ClusterID) @@ -136,7 +137,7 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem sourceDeployments[rc.ClusterID] = deploymentInstance[0] - createServiceEntry(rc, remoteRegistry.config, remoteRegistry.AdmiralCache, deploymentInstance[0], serviceEntries) + createServiceEntry(rc, remoteRegistry.AdmiralCache, deploymentInstance[0], serviceEntries) } @@ -149,8 +150,7 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem remoteRegistry.AdmiralCache.CnameDependentClusterCache.Put(cname, clusterId, clusterId) } - AddServiceEntriesWithDr(remoteRegistry.AdmiralCache, dependentClusters, remoteRegistry.remoteControllers, serviceEntries, - remoteRegistry.config.SyncNamespace) + AddServiceEntriesWithDr(remoteRegistry.AdmiralCache, dependentClusters, remoteRegistry.remoteControllers, serviceEntries) //update the address to local fqdn for service entry in a cluster local to the service instance for sourceCluster, serviceInstance := range sourceServices { @@ -166,7 +166,7 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem oldPorts := ep.Ports ep.Ports = meshPorts AddServiceEntriesWithDr(remoteRegistry.AdmiralCache, map[string]string{sourceCluster: sourceCluster}, remoteRegistry.remoteControllers, - map[string]*networking.ServiceEntry{key: serviceEntry}, remoteRegistry.config.SyncNamespace) + map[string]*networking.ServiceEntry{key: serviceEntry}) //swap it back to use for next iteration ep.Address = clusterIngress ep.Ports = oldPorts @@ -235,8 +235,8 @@ func createSeWithDrLabels(remoteController *RemoteController, localCluster bool, return allSes } -func AddServiceEntriesWithDr(cache *AdmiralCache, sourceClusters map[string]string, rcs map[string]*RemoteController, serviceEntries map[string]*networking.ServiceEntry, - syncNamespace string) { +func AddServiceEntriesWithDr(cache *AdmiralCache, sourceClusters map[string]string, rcs map[string]*RemoteController, serviceEntries map[string]*networking.ServiceEntry) { + syncNamespace := common.GetSyncNamespace() for _, se := range serviceEntries { //add service entry @@ -259,7 +259,7 @@ func AddServiceEntriesWithDr(cache *AdmiralCache, sourceClusters map[string]stri //Add a label if identityId, ok := cache.CnameIdentityCache.Load(se.Hosts[0]); ok { - newServiceEntry.Labels = map[string]string{common.DefaultGlobalIdentifier(): fmt.Sprintf("%v", identityId)} + newServiceEntry.Labels = map[string]string{common.GetWorkloadIdentifier(): fmt.Sprintf("%v", identityId)} } if err == nil { diff --git a/admiral/pkg/clusters/serviceentry_test.go b/admiral/pkg/clusters/serviceentry_test.go index 6b008b7f..60489032 100644 --- a/admiral/pkg/clusters/serviceentry_test.go +++ b/admiral/pkg/clusters/serviceentry_test.go @@ -96,12 +96,12 @@ func TestAddServiceEntriesWithDr(t *testing.T) { } - AddServiceEntriesWithDr(&admiralCache, map[string]string{"cl1":"cl1"}, map[string]*RemoteController{"cl1":rc}, map[string]*networking.ServiceEntry{"se1": &se}, "admiral-sync") + AddServiceEntriesWithDr(&admiralCache, map[string]string{"cl1":"cl1"}, map[string]*RemoteController{"cl1":rc}, map[string]*networking.ServiceEntry{"se1": &se}) } func TestCreateServiceEntryForNewServiceOrPod(t *testing.T) { - p := AdmiralParams{ + p := common.AdmiralParams{ KubeconfigPath: "testdata/fake.config", } rr, _ := InitAdmiral(context.Background(), p) @@ -319,13 +319,6 @@ func TestCreateServiceEntry(t *testing.T) { } }) - params := AdmiralParams{ - EnableSAN: true, - SANPrefix: "prefix", - LabelSet:&common.LabelSet{WorkloadIdentityLabel:"identity"}, - HostnameSuffix: "mesh", - } - cacheWithEntry := ServiceEntryAddressStore{ EntryAddresses: map[string]string{"e2e.my-first-service.mesh": localAddress}, Addresses: []string{localAddress}, @@ -342,7 +335,7 @@ func TestCreateServiceEntry(t *testing.T) { deployment := v12.Deployment{} deployment.Spec.Template.Labels = map[string]string{"env":"e2e", "identity":"my-first-service", } - resultingEntry := createServiceEntry(rc, params, &admiralCache, &deployment, map[string]*networking.ServiceEntry{}) + resultingEntry := createServiceEntry(rc, &admiralCache, &deployment, map[string]*networking.ServiceEntry{}) if resultingEntry.Hosts[0] != "e2e.my-first-service.mesh" { t.Errorf("Host mismatch. Got: %v, expected: e2e.my-first-service.mesh", resultingEntry.Hosts[0]) diff --git a/admiral/pkg/clusters/types.go b/admiral/pkg/clusters/types.go index f83d2b71..d00ab71d 100644 --- a/admiral/pkg/clusters/types.go +++ b/admiral/pkg/clusters/types.go @@ -2,7 +2,6 @@ package clusters import ( "context" - "fmt" "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" @@ -13,35 +12,8 @@ import ( k8s "k8s.io/client-go/kubernetes" "sync" - "time" ) -type AdmiralParams struct { - KubeconfigPath string - CacheRefreshDuration time.Duration - ClusterRegistriesNamespace string - DependenciesNamespace string - SyncNamespace string - EnableSAN bool - SANPrefix string - SecretResolver string - LabelSet *common.LabelSet - HostnameSuffix string - -} - -func (b AdmiralParams) String() string { - return fmt.Sprintf("KubeconfigPath=%v ", b.KubeconfigPath) + - fmt.Sprintf("CacheRefreshDuration=%v ", b.CacheRefreshDuration) + - fmt.Sprintf("ClusterRegistriesNamespace=%v ", b.ClusterRegistriesNamespace) + - fmt.Sprintf("DependenciesNamespace=%v ", b.DependenciesNamespace) + - fmt.Sprintf("EnableSAN=%v ", b.EnableSAN) + - fmt.Sprintf("SANPrefix=%v ", b.SANPrefix) + - fmt.Sprintf("LabelSet=%v ", b.LabelSet) + - fmt.Sprintf("SecretResolver=%v ", b.SecretResolver) -} - - type RemoteController struct { ClusterID string IstioConfigStore istio.ConfigStoreCache @@ -67,7 +39,6 @@ type AdmiralCache struct { } type RemoteRegistry struct { - config AdmiralParams sync.Mutex remoteControllers map[string]*RemoteController secretClient k8s.Interface @@ -129,7 +100,7 @@ func (dh *DependencyHandler) Added(obj *v1.Dependency) { updateIdentityDependencyCache(sourceIdentity, dh.RemoteRegistry.AdmiralCache.IdentityDependencyCache, obj) - handleDependencyRecord(obj.Spec.IdentityLabel, sourceIdentity, dh.RemoteRegistry.AdmiralCache, dh.RemoteRegistry.remoteControllers, dh.RemoteRegistry.config, obj) + handleDependencyRecord(obj.Spec.IdentityLabel, sourceIdentity, dh.RemoteRegistry.AdmiralCache, dh.RemoteRegistry.remoteControllers, obj) } @@ -152,7 +123,7 @@ func (pc *DeploymentHandler) Added(obj *k8sAppsV1.Deployment) { globalIdentifier := common.GetDeploymentGlobalIdentifier(obj) if len(globalIdentifier) == 0 { - log.Infof(LogFormat, "Event", "deployment", obj.Name, "", "Skipped as '"+common.DefaultGlobalIdentifier()+" was not found', namespace="+obj.Namespace) + log.Infof(LogFormat, "Event", "deployment", obj.Name, "", "Skipped as '"+common.GetWorkloadIdentifier()+" was not found', namespace="+obj.Namespace) return } @@ -171,7 +142,7 @@ func (pc *PodHandler) Added(obj *k8sV1.Pod) { globalIdentifier := common.GetPodGlobalIdentifier(obj) if len(globalIdentifier) == 0 { - log.Infof(LogFormat, "Event", "deployment", obj.Name, "", "Skipped as '"+common.DefaultGlobalIdentifier()+" was not found', namespace="+obj.Namespace) + log.Infof(LogFormat, "Event", "deployment", obj.Name, "", "Skipped as '"+common.GetWorkloadIdentifier()+" was not found', namespace="+obj.Namespace) return } diff --git a/admiral/pkg/controller/admiral/configmap.go b/admiral/pkg/controller/admiral/configmap.go index 20ae0a74..3ac403cf 100644 --- a/admiral/pkg/controller/admiral/configmap.go +++ b/admiral/pkg/controller/admiral/configmap.go @@ -1,6 +1,7 @@ package admiral import ( + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" "k8s.io/api/core/v1" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -23,8 +24,9 @@ type ConfigMapController struct { //todo this is a temp state, eventually changes will have to be made to give each cluster it's own configmap -func NewConfigMapController(kubeconfigPath string, namespaceToUse string) (*ConfigMapController, error) { - +func NewConfigMapController() (*ConfigMapController, error) { + kubeconfigPath := common.GetKubeconfigPath() + namespaceToUse := common.GetSyncNamespace() if kubeconfigPath == "" { config, err := rest.InClusterConfig() if err != nil { diff --git a/admiral/pkg/controller/admiral/configmap_test.go b/admiral/pkg/controller/admiral/configmap_test.go index abed187a..00bf55e3 100644 --- a/admiral/pkg/controller/admiral/configmap_test.go +++ b/admiral/pkg/controller/admiral/configmap_test.go @@ -3,12 +3,15 @@ package admiral import ( "errors" "github.com/google/go-cmp/cmp" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + _ "github.com/istio-ecosystem/admiral/admiral/pkg/test" "github.com/sirupsen/logrus" "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes/fake" "testing" ) + func TestConfigMapController_GetConfigMap(t *testing.T) { configmapController := ConfigMapController{ ConfigmapNamespace: "admiral", @@ -99,7 +102,8 @@ func TestNewConfigMapController(t *testing.T) { for _, c := range testCases { t.Run(c.name, func(t *testing.T) { - controller, err := NewConfigMapController(c.kubeconfigPath, c.namespace) + common.SetKubeconfigPath(c.kubeconfigPath) + controller, err := NewConfigMapController() if err==nil && c.expectedError==nil { //only do these in an error-less context if c.namespace != controller.ConfigmapNamespace { diff --git a/admiral/pkg/controller/admiral/deployment.go b/admiral/pkg/controller/admiral/deployment.go index afefb073..39db07d9 100644 --- a/admiral/pkg/controller/admiral/deployment.go +++ b/admiral/pkg/controller/admiral/deployment.go @@ -126,11 +126,11 @@ func (d *DeploymentController) GetDeployments() ([]*k8sAppsV1.Deployment, error) return res, nil } -func NewDeploymentController(stopCh <-chan struct{}, handler DeploymentHandler, config *rest.Config, resyncPeriod time.Duration, labelSet *common.LabelSet) (*DeploymentController, error) { +func NewDeploymentController(stopCh <-chan struct{}, handler DeploymentHandler, config *rest.Config, resyncPeriod time.Duration) (*DeploymentController, error) { deploymentController := DeploymentController{} deploymentController.DeploymentHandler = handler - deploymentController.labelSet = labelSet + deploymentController.labelSet = common.GetLabelSet() deploymentCache := deploymentCache{} deploymentCache.cache = make(map[string]*DeploymentClusterEntry) diff --git a/admiral/pkg/controller/admiral/deployment_test.go b/admiral/pkg/controller/admiral/deployment_test.go index aee75f77..f2316ca2 100644 --- a/admiral/pkg/controller/admiral/deployment_test.go +++ b/admiral/pkg/controller/admiral/deployment_test.go @@ -154,7 +154,7 @@ func TestNewDeploymentController(t *testing.T) { stop := make(chan struct{}) depHandler := test.MockDeploymentHandler{} - depCon, err := NewDeploymentController(stop, &depHandler, config, time.Duration(1000), &common.LabelSet{}) + depCon, err := NewDeploymentController(stop, &depHandler, config, time.Duration(1000)) if depCon == nil { t.Errorf("Deployment controller should not be nil") diff --git a/admiral/pkg/controller/admiral/pod.go b/admiral/pkg/controller/admiral/pod.go index 01ae27d7..afeecae8 100644 --- a/admiral/pkg/controller/admiral/pod.go +++ b/admiral/pkg/controller/admiral/pod.go @@ -127,11 +127,11 @@ func (d *PodController) GetPods() ([]*k8sV1.Pod, error) { return res, nil } -func NewPodController(stopCh <-chan struct{}, handler PodHandler, config *rest.Config, resyncPeriod time.Duration, labelSet *common.LabelSet) (*PodController, error) { +func NewPodController(stopCh <-chan struct{}, handler PodHandler, config *rest.Config, resyncPeriod time.Duration) (*PodController, error) { podController := PodController{} podController.PodHandler = handler - podController.labelSet = labelSet + podController.labelSet = common.GetLabelSet() podCache := podCache{} podCache.cache = make(map[string]*PodClusterEntry) diff --git a/admiral/pkg/controller/admiral/pod_test.go b/admiral/pkg/controller/admiral/pod_test.go index 61563615..ebc68fa3 100644 --- a/admiral/pkg/controller/admiral/pod_test.go +++ b/admiral/pkg/controller/admiral/pod_test.go @@ -20,7 +20,7 @@ func TestNewPodController(t *testing.T) { stop := make(chan struct{}) handler := test.MockPodHandler{} - podController, err := NewPodController(stop, &handler, config, time.Duration(1000), &common.LabelSet{}) + podController, err := NewPodController(stop, &handler, config, time.Duration(1000)) if err != nil { t.Errorf("Unexpected err %v", err) diff --git a/admiral/pkg/controller/common/common.go b/admiral/pkg/controller/common/common.go index 1dbfb795..fe0a98b7 100644 --- a/admiral/pkg/controller/common/common.go +++ b/admiral/pkg/controller/common/common.go @@ -29,36 +29,28 @@ const ( ) func GetPodGlobalIdentifier(pod *k8sV1.Pod) string { - identity := pod.Labels[DefaultGlobalIdentifier()] + identity := pod.Labels[GetWorkloadIdentifier()] if len(identity) == 0 { - identity = pod.Annotations[DefaultGlobalIdentifier()] + identity = pod.Annotations[GetWorkloadIdentifier()] } return identity } func GetDeploymentGlobalIdentifier(deployment *k8sAppsV1.Deployment) string { - identity := deployment.Spec.Template.Labels[DefaultGlobalIdentifier()] + identity := deployment.Spec.Template.Labels[GetWorkloadIdentifier()] if len(identity) == 0 { //TODO can this be removed now? This was for backward compatibility - identity = deployment.Spec.Template.Annotations[DefaultGlobalIdentifier()] + identity = deployment.Spec.Template.Annotations[GetWorkloadIdentifier()] } return identity } -func DefaultGlobalIdentifier() string { - return Identity -} - // GetCname returns cname in the format ..global, Ex: stage.Admiral.services.registry.global func GetCname(deployment *k8sAppsV1.Deployment, identifier string, nameSuffix string) string { var environment = GetEnv(deployment) - alias := deployment.Spec.Template.Labels[identifier] - if len(alias) == 0 { - logrus.Warnf("%v label missing on service %v in namespace %v. Falling back to annotation to create cname.", identifier, deployment.Name, deployment.Namespace) - alias = deployment.Spec.Template.Annotations[identifier] - } + alias := GetValueForKeyFromDeployment(identifier, deployment) if len(alias) == 0 { - logrus.Errorf("Unable to get cname for service with name %v in namespace %v as it doesn't have the %v annotation", deployment.Name, deployment.Namespace, identifier) + logrus.Errorf("Unable to get cname for deployment with name %v in namespace %v as it doesn't have the %v annotation or label", deployment.Name, deployment.Namespace, identifier) return "" } return environment + Sep + alias + Sep + nameSuffix @@ -83,8 +75,9 @@ func GetEnv(deployment *k8sAppsV1.Deployment) string { // GetSAN returns SAN for a service entry in the format spiffe:///, Ex: spiffe://subdomain.domain.com/Admiral.platform.mesh.server func GetSAN(domain string, deployment *k8sAppsV1.Deployment, identifier string) string { - identifierVal := deployment.Spec.Template.Labels[identifier] + identifierVal := GetValueForKeyFromDeployment(identifier, deployment) if len(identifierVal) == 0 { + logrus.Errorf("Unable to get SAN for deployment with name %v in namespace %v as it doesn't have the %v annotation or label", deployment.Name, deployment.Namespace, identifier) return "" } if len(domain) > 0 { @@ -98,3 +91,12 @@ func GetNodeLocality(node *k8sV1.Node) string { region, _ := node.Labels[NodeRegionLabel] return region } + +func GetValueForKeyFromDeployment(key string, deployment *k8sAppsV1.Deployment) string { + value := deployment.Spec.Template.Labels[key] + if len(value) == 0 { + logrus.Warnf("%v label missing on deployment %v in namespace %v. Falling back to annotation.", key, deployment.Name, deployment.Namespace) + value = deployment.Spec.Template.Annotations[key] + } + return value +} diff --git a/admiral/pkg/controller/common/common_test.go b/admiral/pkg/controller/common/common_test.go index 304837e9..e6ca55fb 100644 --- a/admiral/pkg/controller/common/common_test.go +++ b/admiral/pkg/controller/common/common_test.go @@ -6,7 +6,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1" "reflect" "testing" -) + ) func TestGetSAN(t *testing.T) { t.Parallel() @@ -16,6 +16,7 @@ func TestGetSAN(t *testing.T) { domain := "preprd" deployment := k8sAppsV1.Deployment{Spec: k8sAppsV1.DeploymentSpec{Template: v12.PodTemplateSpec{ObjectMeta: v1.ObjectMeta{Labels: map[string]string{identifier: identifierVal}}}}} + deploymentWithAnnotation := k8sAppsV1.Deployment{Spec: k8sAppsV1.DeploymentSpec{Template: v12.PodTemplateSpec{ObjectMeta: v1.ObjectMeta{Annotations: map[string]string{identifier: identifierVal}}}}} deploymentWithNoIdentifier := k8sAppsV1.Deployment{} @@ -26,11 +27,17 @@ func TestGetSAN(t *testing.T) { wantSAN string }{ { - name: "should return valid SAN", + name: "should return valid SAN (from label)", deployment: deployment, domain: domain, wantSAN: "spiffe://" + domain + "/" + identifierVal, }, + { + name: "should return valid SAN (from annotation)", + deployment: deploymentWithAnnotation, + domain: domain, + wantSAN: "spiffe://" + domain + "/" + identifierVal, + }, { name: "should return valid SAN with no domain prefix", deployment: deployment, diff --git a/admiral/pkg/controller/common/config.go b/admiral/pkg/controller/common/config.go new file mode 100644 index 00000000..63493f51 --- /dev/null +++ b/admiral/pkg/controller/common/config.go @@ -0,0 +1,72 @@ +package common + +import ( + "sync" + "time" +) + +var admiralParams = AdmiralParams{ + LabelSet: &LabelSet{}, +} + +var once sync.Once + +func InitializeConfig(params AdmiralParams) { + once.Do(func() { + admiralParams = params + }) +} + +func GetAdmiralParams() AdmiralParams { + return admiralParams +} + +func GetKubeconfigPath() string { + return admiralParams.KubeconfigPath +} + +func GetCacheRefreshDuration() time.Duration { + return admiralParams.CacheRefreshDuration +} + +func GetClusterRegistriesNamespace() string { + return admiralParams.ClusterRegistriesNamespace +} + +func GetDependenciesNamespace() string { + return admiralParams.DependenciesNamespace +} + +func GetSyncNamespace() string { + return admiralParams.SyncNamespace +} + +func GetEnableSAN() bool { + return admiralParams.EnableSAN +} + +func GetSANPrefix() string { + return admiralParams.SANPrefix +} + +func GetSecretResolver() string { + return admiralParams.SecretResolver +} + +func GetLabelSet() *LabelSet { + return admiralParams.LabelSet +} + +func GetHostnameSuffix() string { + return admiralParams.HostnameSuffix +} + +func GetWorkloadIdentifier() string { + return admiralParams.LabelSet.WorkloadIdentityKey +} + +///Setters - be careful + +func SetKubeconfigPath(path string) { + admiralParams.KubeconfigPath = path +} \ No newline at end of file diff --git a/admiral/pkg/controller/common/configInitializer.go b/admiral/pkg/controller/common/configInitializer.go new file mode 100644 index 00000000..5080385c --- /dev/null +++ b/admiral/pkg/controller/common/configInitializer.go @@ -0,0 +1,25 @@ +package common + +import "time" + +//Because the admiralParams are not a global singleton, we'll initialize them here and then use setters as needed throughout the tests. Import this package to ensure the initialization happens in your test +//If you're not using anything from the package (and getting an unused import error), import it with the name of _ (_ "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common") +func init() { + p := AdmiralParams{ + KubeconfigPath: "testdata/fake.config", + LabelSet: &LabelSet{}, + EnableSAN: true, + SANPrefix: "prefix", + HostnameSuffix: "mesh", + SyncNamespace: "ns", + CacheRefreshDuration: time.Minute, + ClusterRegistriesNamespace: "default", + DependenciesNamespace: "default", + SecretResolver: "", + + } + + p.LabelSet.WorkloadIdentityKey="identity" + + InitializeConfig(p) +} \ No newline at end of file diff --git a/admiral/pkg/controller/common/config_test.go b/admiral/pkg/controller/common/config_test.go new file mode 100644 index 00000000..e7c5b39c --- /dev/null +++ b/admiral/pkg/controller/common/config_test.go @@ -0,0 +1,72 @@ +package common + +import ( + "testing" + "time" +) + +func TestConfigManagement(t *testing.T) { + + //Initial state comes from the init method in configInitializer.go + //p := AdmiralParams{ + // KubeconfigPath: "testdata/fake.config", + // LabelSet: &LabelSet{}, + // EnableSAN: true, + // SANPrefix: "prefix", + // HostnameSuffix: "mesh", + // SyncNamespace: "ns", + //} + // + //p.LabelSet.WorkloadIdentityKey="identity" + + //trying to initialize again. If the singleton pattern works, none of these will have changed + p := AdmiralParams{ + KubeconfigPath: "DIFFERENT", + LabelSet: &LabelSet{}, + EnableSAN: false, + SANPrefix: "BAD_PREFIX", + HostnameSuffix: "NOT_MESH", + SyncNamespace: "NOT_A_NAMESPACE", + CacheRefreshDuration: time.Hour, + ClusterRegistriesNamespace: "NOT_DEFAULT", + DependenciesNamespace: "NOT_DEFAULT", + SecretResolver: "INSECURE_RESOLVER", + + } + + p.LabelSet.WorkloadIdentityKey="BAD_LABEL" + + InitializeConfig(p) + + if GetWorkloadIdentifier() != "identity" { + t.Errorf("Workload identifier mismatch, expected identity, got %v", GetWorkloadIdentifier()) + } + if GetKubeconfigPath() != "testdata/fake.config" { + t.Errorf("Kubeconfig path mismatch, expected testdata/fake.config, got %v", GetKubeconfigPath()) + } + if GetSANPrefix() != "prefix" { + t.Errorf("San prefix mismatch, expected prefix, got %v", GetSANPrefix()) + } + if GetHostnameSuffix() != "mesh" { + t.Errorf("Hostname suffix mismatch, expected mesh, got %v", GetHostnameSuffix()) + } + if GetSyncNamespace() != "ns" { + t.Errorf("Sync namespace mismatch, expected ns, got %v", GetSyncNamespace()) + } + if GetEnableSAN() != true { + t.Errorf("Enable SAN mismatch, expected true, got %v", GetEnableSAN()) + } + if GetCacheRefreshDuration() != time.Minute { + t.Errorf("Cachee refresh duration mismatch, expected %v, got %v", time.Minute, GetCacheRefreshDuration()) + } + if GetClusterRegistriesNamespace() != "default" { + t.Errorf("Cluster registry namespace mismatch, expected default, got %v", GetClusterRegistriesNamespace()) + } + if GetDependenciesNamespace() != "default" { + t.Errorf("Dependency namespace mismatch, expected default, got %v", GetDependenciesNamespace()) + } + if GetSecretResolver() != "" { + t.Errorf("Secret resolver mismatch, expected empty string, got %v", GetSecretResolver()) + } + +} \ No newline at end of file diff --git a/admiral/pkg/controller/common/types.go b/admiral/pkg/controller/common/types.go index dbe2abda..93903a66 100644 --- a/admiral/pkg/controller/common/types.go +++ b/admiral/pkg/controller/common/types.go @@ -1,7 +1,9 @@ package common import ( + "fmt" "sync" + "time" ) type Map struct { @@ -15,13 +17,39 @@ type MapOfMaps struct { mutex *sync.Mutex } +type AdmiralParams struct { + KubeconfigPath string + CacheRefreshDuration time.Duration + ClusterRegistriesNamespace string + DependenciesNamespace string + SyncNamespace string + EnableSAN bool + SANPrefix string + SecretResolver string + LabelSet *LabelSet + HostnameSuffix string + +} + +func (b AdmiralParams) String() string { + return fmt.Sprintf("KubeconfigPath=%v ", b.KubeconfigPath) + + fmt.Sprintf("CacheRefreshDuration=%v ", b.CacheRefreshDuration) + + fmt.Sprintf("ClusterRegistriesNamespace=%v ", b.ClusterRegistriesNamespace) + + fmt.Sprintf("DependenciesNamespace=%v ", b.DependenciesNamespace) + + fmt.Sprintf("EnableSAN=%v ", b.EnableSAN) + + fmt.Sprintf("SANPrefix=%v ", b.SANPrefix) + + fmt.Sprintf("LabelSet=%v ", b.LabelSet) + + fmt.Sprintf("SecretResolver=%v ", b.SecretResolver) +} + + type LabelSet struct { DeploymentAnnotation string SubsetLabel string NamespaceSidecarInjectionLabel string NamespaceSidecarInjectionLabelValue string AdmiralIgnoreLabel string - WorkloadIdentityLabel 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) } func NewMap() *Map { diff --git a/admiral/pkg/controller/util/util.go b/admiral/pkg/controller/util/util.go index d468840b..f712db23 100644 --- a/admiral/pkg/controller/util/util.go +++ b/admiral/pkg/controller/util/util.go @@ -35,4 +35,4 @@ func Contains(vs []string, t string) bool { } } return false -} +} \ No newline at end of file From f94f6149e69ff22bb01a9b476befb8325908c6b3 Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Wed, 19 Feb 2020 12:25:28 -0800 Subject: [PATCH 17/45] Revert latest commit (#69) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Bootstrapping admiral * Add circle ci config * Fix the working directory for builds * Bootstrapping admiral Signed-off-by: aattuluri * Add circle ci config Signed-off-by: aattuluri * Fix the working directory for builds Signed-off-by: aattuluri * Add build status badge. Signed-off-by: aattuluri * Revert "Add build status badge." This reverts commit a9fa4095c7c3d8d157231664134ef850f0797a5c. * Publish images: i) latest from master, ii) TAG if its set and iii) commit sha if neither i) or ii) * Revert "Formalizing the behavior to fall back to annotation if label isn't pr… (#68)" This reverts commit 802de1707c1b6736180b4f9cada0f82597d8a453. Signed-off-by: Madeline --- admiral/cmd/admiral/cmd/root.go | 6 +- admiral/pkg/clusters/registry.go | 65 ++++++++--------- admiral/pkg/clusters/registry_test.go | 39 +++++----- admiral/pkg/clusters/serviceentry.go | 26 +++---- admiral/pkg/clusters/serviceentry_test.go | 13 +++- admiral/pkg/clusters/types.go | 35 ++++++++- admiral/pkg/controller/admiral/configmap.go | 6 +- .../pkg/controller/admiral/configmap_test.go | 6 +- admiral/pkg/controller/admiral/deployment.go | 4 +- .../pkg/controller/admiral/deployment_test.go | 2 +- admiral/pkg/controller/admiral/pod.go | 4 +- admiral/pkg/controller/admiral/pod_test.go | 2 +- admiral/pkg/controller/common/common.go | 32 ++++----- admiral/pkg/controller/common/common_test.go | 11 +-- admiral/pkg/controller/common/config.go | 72 ------------------- .../controller/common/configInitializer.go | 25 ------- admiral/pkg/controller/common/config_test.go | 72 ------------------- admiral/pkg/controller/common/types.go | 30 +------- admiral/pkg/controller/util/util.go | 2 +- 19 files changed, 134 insertions(+), 318 deletions(-) delete mode 100644 admiral/pkg/controller/common/config.go delete mode 100644 admiral/pkg/controller/common/configInitializer.go delete mode 100644 admiral/pkg/controller/common/config_test.go diff --git a/admiral/cmd/admiral/cmd/root.go b/admiral/cmd/admiral/cmd/root.go index 040af170..852d9940 100644 --- a/admiral/cmd/admiral/cmd/root.go +++ b/admiral/cmd/admiral/cmd/root.go @@ -25,7 +25,7 @@ func GetRootCmd(args []string) *cobra.Command { var () - params := common.AdmiralParams{LabelSet: &common.LabelSet{}} + params := clusters.AdmiralParams{LabelSet: &common.LabelSet{}} rootCmd := &cobra.Command{ Use: "Admiral", @@ -84,8 +84,8 @@ func GetRootCmd(args []string) *cobra.Command { "The label value, on a namespace, which tells Istio to perform sidecar injection") rootCmd.PersistentFlags().StringVar(¶ms.HostnameSuffix, "hostname_suffix", "global", "The hostname suffix to customize the cname generated by admiral. Default suffix value will be \"global\"") - rootCmd.PersistentFlags().StringVar(¶ms.LabelSet.WorkloadIdentityKey, "workload_identity_key", "identity", - "The workload identity key, on deployment which holds identity value used to generate cname by admiral. Default label key will be \"identity\" Admiral will look for a label with this key. If present, that will be used. If not, it will try an annotation (for use cases where an identity is longer than 63 chars)") + rootCmd.PersistentFlags().StringVar(¶ms.LabelSet.WorkloadIdentityLabel, "workload_identity_label", "identity", + "The workload identity label key, on deployment which holds identity value used to generate cname by admiral. Default label key will be \"identity\"") loggingOptions.AttachCobraFlags(rootCmd) return rootCmd diff --git a/admiral/pkg/clusters/registry.go b/admiral/pkg/clusters/registry.go index 906eda09..9685bfbc 100644 --- a/admiral/pkg/clusters/registry.go +++ b/admiral/pkg/clusters/registry.go @@ -40,7 +40,7 @@ func updateIdentityDependencyCache(sourceIdentity string, identityDependencyCach log.Infof(LogFormat, "Update", "dependency-cache", dr.Name, "", "Updated=true namespace="+dr.Namespace) } -func handleDependencyRecord(identifier string, sourceIdentity string, admiralCache *AdmiralCache, rcs map[string]*RemoteController, obj *v1.Dependency) { +func handleDependencyRecord(identifier string, sourceIdentity string, admiralCache *AdmiralCache, rcs map[string]*RemoteController, config AdmiralParams, obj *v1.Dependency) { destinationIdentitys := obj.Spec.Destinations @@ -79,7 +79,7 @@ func handleDependencyRecord(identifier string, sourceIdentity string, admiralCac } //TODO pass deployment - tmpSe := createServiceEntry(rc, admiralCache, deployment[0], serviceEntries) + tmpSe := createServiceEntry(rc, config, admiralCache, deployment[0], serviceEntries) if tmpSe == nil { continue @@ -110,7 +110,7 @@ func handleDependencyRecord(identifier string, sourceIdentity string, admiralCac } //add service entries for all dependencies in source cluster - AddServiceEntriesWithDr(admiralCache, sourceClusters, rcs, serviceEntries) + AddServiceEntriesWithDr(admiralCache, sourceClusters, rcs, serviceEntries, config.SyncNamespace) } func getIstioResourceName(host string, suffix string) string { @@ -187,12 +187,10 @@ func getDependentClusters(dependents *common.Map, identityClusterCache *common.M return dependentClusters } -func InitAdmiral(ctx context.Context, params common.AdmiralParams) (*RemoteRegistry, error) { +func InitAdmiral(ctx context.Context, params AdmiralParams) (*RemoteRegistry, error) { log.Infof("Initializing Admiral with params: %v", params) - common.InitializeConfig(params) - w := RemoteRegistry{ ctx: ctx, } @@ -207,6 +205,7 @@ func InitAdmiral(ctx context.Context, params common.AdmiralParams) (*RemoteRegis return nil, fmt.Errorf(" Error with dependency controller init: %v", err) } + w.config = params w.remoteControllers = make(map[string]*RemoteController) w.AdmiralCache = &AdmiralCache{ @@ -219,14 +218,14 @@ func InitAdmiral(ctx context.Context, params common.AdmiralParams) (*RemoteRegis SubsetServiceEntryIdentityCache: &sync.Map{}, ServiceEntryAddressStore: &ServiceEntryAddressStore{EntryAddresses: map[string]string{}, Addresses: []string{},}} - configMapController, err := admiral.NewConfigMapController() + configMapController, err := admiral.NewConfigMapController(w.config.KubeconfigPath, w.config.SyncNamespace) if err != nil { return nil, fmt.Errorf(" Error with configmap controller init: %v", err) } w.AdmiralCache.ConfigMapController = configMapController loadServiceEntryCacheData(w.AdmiralCache.ConfigMapController, w.AdmiralCache) - err = createSecretController(ctx, &w) + err = createSecretController(ctx, &w, params) if err != nil { return nil, fmt.Errorf(" Error with secret control init: %v", err) } @@ -236,10 +235,10 @@ func InitAdmiral(ctx context.Context, params common.AdmiralParams) (*RemoteRegis return &w, nil } -func createSecretController(ctx context.Context, w *RemoteRegistry) error { +func createSecretController(ctx context.Context, w *RemoteRegistry, params AdmiralParams) error { var err error - w.secretClient, err = admiral.K8sClientFromPath(common.GetKubeconfigPath()) + w.secretClient, err = admiral.K8sClientFromPath(params.KubeconfigPath) if err != nil { return fmt.Errorf("could not create K8s client: %v", err) } @@ -247,8 +246,8 @@ func createSecretController(ctx context.Context, w *RemoteRegistry) error { err = secret.StartSecretController(w.secretClient, w.createCacheController, w.deleteCacheController, - common.GetClusterRegistriesNamespace(), - ctx, common.GetSecretResolver()) + w.config.ClusterRegistriesNamespace, + ctx, params.SecretResolver) if err != nil { return fmt.Errorf("could not start secret controller: %v", err) @@ -286,14 +285,14 @@ func (r *RemoteRegistry) createCacheController(clientConfig *rest.Config, cluste } log.Infof("starting deployment controller clusterID: %v", clusterID) - rc.DeploymentController, err = admiral.NewDeploymentController(stop, &DeploymentHandler{RemoteRegistry: r}, clientConfig, resyncPeriod) + rc.DeploymentController, err = admiral.NewDeploymentController(stop, &DeploymentHandler{RemoteRegistry: r}, clientConfig, resyncPeriod, r.config.LabelSet) if err != nil { return fmt.Errorf(" Error with DeploymentController controller init: %v", err) } log.Infof("starting pod controller clusterID: %v", clusterID) - rc.PodController, err = admiral.NewPodController(stop, &PodHandler{RemoteRegistry: r}, clientConfig, resyncPeriod) + rc.PodController, err = admiral.NewPodController(stop, &PodHandler{RemoteRegistry: r}, clientConfig, resyncPeriod, r.config.LabelSet) if err != nil { return fmt.Errorf(" Error with PodController controller init: %v", err) @@ -324,7 +323,6 @@ func (r *RemoteRegistry) createCacheController(clientConfig *rest.Config, cluste func (r *RemoteRegistry) createIstioController(clientConfig *rest.Config, opts istioKube.ControllerOptions, remoteController *RemoteController, stop <-chan struct{}, clusterID string) error { configClient, err := istio.NewClient(clientConfig, "", istio.IstioConfigTypes, opts.DomainSuffix) - syncNamespace := common.GetSyncNamespace() if err != nil { return fmt.Errorf("error creating istio client to the remote clusterId: %s error:%v", clusterID, err) } @@ -335,7 +333,7 @@ func (r *RemoteRegistry) createIstioController(clientConfig *rest.Config, opts i clusterId := remoteController.ClusterID - if m.Namespace == common.GetSyncNamespace() { + if m.Namespace == r.config.SyncNamespace { log.Infof(LogFormat, "Event", e.String(), m.Name, clusterId, "Skipping the namespace: "+m.Namespace) return } @@ -363,11 +361,11 @@ func (r *RemoteRegistry) createIstioController(clientConfig *rest.Config, opts i if istio.EventDelete == e { log.Infof(LogFormat, "Delete", istioModel.DestinationRule.Type, m.Name, clusterId, "Success") - rc.IstioConfigStore.Delete(istioModel.DestinationRule.Type, m.Name, syncNamespace) + rc.IstioConfigStore.Delete(istioModel.DestinationRule.Type, m.Name, r.config.SyncNamespace) } else { - exist := rc.IstioConfigStore.Get(istioModel.VirtualService.Type, m.Name, syncNamespace) + exist := rc.IstioConfigStore.Get(istioModel.VirtualService.Type, m.Name, r.config.SyncNamespace) //change destination host for all http routes .. to same as host on the virtual service for _, httpRoute := range virtualService.Http { @@ -384,7 +382,7 @@ func (r *RemoteRegistry) createIstioController(clientConfig *rest.Config, opts i } } - addUpdateIstioResource(rc, m, exist, istioModel.VirtualService.Type, syncNamespace) + addUpdateIstioResource(rc, m, exist, istioModel.VirtualService.Type, r.config.SyncNamespace) } } @@ -401,7 +399,7 @@ func (r *RemoteRegistry) createIstioController(clientConfig *rest.Config, opts i var localIdentityId string - if m.Namespace == syncNamespace || m.Namespace == common.NamespaceKubeSystem { + if m.Namespace == r.config.SyncNamespace || m.Namespace == common.NamespaceKubeSystem { log.Infof(LogFormat, "Event", e.String(), m.Name, clusterId, "Skipping the namespace: "+m.Namespace) return } @@ -439,7 +437,7 @@ func (r *RemoteRegistry) createIstioController(clientConfig *rest.Config, opts i var drServiceEntries = make(map[string]*networking.ServiceEntry) - exist := rc.IstioConfigStore.Get(istioModel.ServiceEntry.Type, basicSEName, syncNamespace) + exist := rc.IstioConfigStore.Get(istioModel.ServiceEntry.Type, basicSEName, r.config.SyncNamespace) var identityId = "" @@ -465,36 +463,36 @@ func (r *RemoteRegistry) createIstioController(clientConfig *rest.Config, opts i if istio.EventDelete == e { - rc.IstioConfigStore.Delete(istioModel.DestinationRule.Type, m.Name, syncNamespace) + rc.IstioConfigStore.Delete(istioModel.DestinationRule.Type, m.Name, r.config.SyncNamespace) log.Infof(LogFormat, "Delete", istioModel.ServiceEntry.Type, m.Name, clusterId, "success") - rc.IstioConfigStore.Delete(istioModel.ServiceEntry.Type, seName, syncNamespace) + rc.IstioConfigStore.Delete(istioModel.ServiceEntry.Type, seName, r.config.SyncNamespace) log.Infof(LogFormat, "Delete", istioModel.ServiceEntry.Type, seName, clusterId, "success") for _, subset := range destinationRule.Subsets { sseName := seName + common.Dash + subset.Name - rc.IstioConfigStore.Delete(istioModel.ServiceEntry.Type, sseName, syncNamespace) + rc.IstioConfigStore.Delete(istioModel.ServiceEntry.Type, sseName, r.config.SyncNamespace) log.Infof(LogFormat, "Delete", istioModel.ServiceEntry.Type, sseName, clusterId, "success") } - rc.IstioConfigStore.Delete(istioModel.DestinationRule.Type, localDrName, syncNamespace) + rc.IstioConfigStore.Delete(istioModel.DestinationRule.Type, localDrName, r.config.SyncNamespace) log.Infof(LogFormat, "Delete", istioModel.DestinationRule.Type, localDrName, clusterId, "success") } else { - exist := rc.IstioConfigStore.Get(istioModel.DestinationRule.Type, m.Name, syncNamespace) + exist := rc.IstioConfigStore.Get(istioModel.DestinationRule.Type, m.Name, r.config.SyncNamespace) //copy destination rule only to other clusters if dependentCluster != clusterId { - addUpdateIstioResource(rc, m, exist, istioModel.DestinationRule.Type, syncNamespace) + addUpdateIstioResource(rc, m, exist, istioModel.DestinationRule.Type, r.config.SyncNamespace) } if drServiceEntries != nil { for _seName, se := range drServiceEntries { - existsServiceEntry = rc.IstioConfigStore.Get(istioModel.ServiceEntry.Type, _seName, syncNamespace) - newServiceEntry, err = createIstioConfig(istio.ServiceEntryProto, se, _seName, syncNamespace) + existsServiceEntry = rc.IstioConfigStore.Get(istioModel.ServiceEntry.Type, _seName, r.config.SyncNamespace) + newServiceEntry, err = createIstioConfig(istio.ServiceEntryProto, se, _seName, r.config.SyncNamespace) if err != nil { log.Warnf(LogErrFormat, "Create", istioModel.ServiceEntry.Type, seName, clusterId, err) } if newServiceEntry != nil { - addUpdateIstioResource(rc, *newServiceEntry, existsServiceEntry, istioModel.ServiceEntry.Type, syncNamespace) + addUpdateIstioResource(rc, *newServiceEntry, existsServiceEntry, istioModel.ServiceEntry.Type, r.config.SyncNamespace) } //cache the subset service entries for updating them later for pod events if dependentCluster == clusterId && se.Resolution == networking.ServiceEntry_STATIC { @@ -505,7 +503,7 @@ func (r *RemoteRegistry) createIstioController(clientConfig *rest.Config, opts i if dependentCluster == clusterId { //we need a destination rule with local fqdn for destination rules created with cnames to work in local cluster - createDestinationRuleForLocal(remoteController, localDrName, localIdentityId, clusterId, destinationRule) + createDestinationRuleForLocal(remoteController, localDrName, localIdentityId, clusterId, destinationRule, r.config.SyncNamespace, r.config.HostnameSuffix, r.config.LabelSet.WorkloadIdentityLabel) } } @@ -520,11 +518,8 @@ func (r *RemoteRegistry) createIstioController(clientConfig *rest.Config, opts i } func createDestinationRuleForLocal(remoteController *RemoteController, localDrName string, identityId string, clusterId string, - destinationRule *networking.DestinationRule) { + destinationRule *networking.DestinationRule, syncNamespace string, nameSuffix string, identifier string) { - syncNamespace := common.GetSyncNamespace() - nameSuffix := common.GetHostnameSuffix() - identifier := common.GetWorkloadIdentifier() deployment := remoteController.DeploymentController.Cache.Get(identityId) if deployment == nil || len(deployment.Deployments) == 0 { diff --git a/admiral/pkg/clusters/registry_test.go b/admiral/pkg/clusters/registry_test.go index f76a9dd3..d92357d9 100644 --- a/admiral/pkg/clusters/registry_test.go +++ b/admiral/pkg/clusters/registry_test.go @@ -20,7 +20,6 @@ import ( "time" ) - func TestDeleteCacheControllerThatDoesntExist(t *testing.T) { w := RemoteRegistry{ @@ -122,7 +121,7 @@ func TestCreateDestinationRuleForLocalNoDeployLabel(t *testing.T) { Host: "localhost", } - d, e := admiral.NewDeploymentController(make(chan struct{}), &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300)) + d, e := admiral.NewDeploymentController(make(chan struct{}), &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300), &common.LabelSet{}) if e != nil { t.Fail() @@ -145,7 +144,7 @@ func TestCreateDestinationRuleForLocalNoDeployLabel(t *testing.T) { }, } - createDestinationRuleForLocal(&rc, "local.name", "identity", "cluster1", &des) + createDestinationRuleForLocal(&rc, "local.name", "identity", "cluster1", &des, "sync", ".global", "identity") } @@ -170,7 +169,7 @@ func TestCreateDestinationRuleForLocal(t *testing.T) { }, } - createDestinationRuleForLocal(rc, "local.name", "bar", "cluster1", &des) + createDestinationRuleForLocal(rc, "local.name", "bar", "cluster1", &des, "sync", ".global", "identity") } @@ -179,7 +178,7 @@ func createMockRemoteController(f func(interface{})) (*RemoteController, error) Host: "localhost", } stop := make(chan struct{}) - d, e := admiral.NewDeploymentController(stop, &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300)) + d, e := admiral.NewDeploymentController(stop, &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300), &common.LabelSet{}) s, e := admiral.NewServiceController(stop, &test.MockServiceHandler{}, &config, time.Second*time.Duration(300)) n, e := admiral.NewNodeController(stop, &test.MockNodeHandler{}, &config) @@ -232,19 +231,23 @@ func createMockRemoteController(f func(interface{})) (*RemoteController, error) func TestCreateSecretController(t *testing.T) { + p := AdmiralParams{ + KubeconfigPath: "testdata/fake.config", + } + rr := RemoteRegistry{} - err := createSecretController(context.Background(), &rr) + err := createSecretController(context.Background(), &rr, p) if err != nil { t.Fail() } - common.SetKubeconfigPath("fail") + p = AdmiralParams{ + KubeconfigPath: "fail", + } rr = RemoteRegistry{} - err = createSecretController(context.Background(), &rr) - - common.SetKubeconfigPath("testdata/fake.config") + err = createSecretController(context.Background(), &rr, p) if err == nil { t.Fail() @@ -253,12 +256,10 @@ func TestCreateSecretController(t *testing.T) { func TestInitAdmiral(t *testing.T) { - p := common.AdmiralParams{ + p := AdmiralParams{ KubeconfigPath: "testdata/fake.config", - LabelSet: &common.LabelSet{}, } - rr, err := InitAdmiral(context.Background(), p) if err != nil { @@ -267,15 +268,11 @@ func TestInitAdmiral(t *testing.T) { if len(rr.remoteControllers) != 0 { t.Fail() } - - if common.GetWorkloadIdentifier() != "identity" { - t.Errorf("Workload identity label override failed. Expected \"identity\", got %v", common.GetWorkloadIdentifier()) - } } func TestAdded(t *testing.T) { - p := common.AdmiralParams{ + p := AdmiralParams{ KubeconfigPath: "testdata/fake.config", } rr, _ := InitAdmiral(context.Background(), p) @@ -310,7 +307,7 @@ func TestAdded(t *testing.T) { func TestCreateIstioController(t *testing.T) { - p := common.AdmiralParams{ + p := AdmiralParams{ KubeconfigPath: "testdata/fake.config", } rr, _ := InitAdmiral(context.Background(), p) @@ -345,7 +342,7 @@ func TestMakeVirtualService(t *testing.T) { func TestDeploymentHandler(t *testing.T) { - p := common.AdmiralParams{ + p := AdmiralParams{ KubeconfigPath: "testdata/fake.config", } @@ -390,7 +387,7 @@ func TestDeploymentHandler(t *testing.T) { func TestPodHandler(t *testing.T) { - p := common.AdmiralParams{ + p := AdmiralParams{ KubeconfigPath: "testdata/fake.config", } diff --git a/admiral/pkg/clusters/serviceentry.go b/admiral/pkg/clusters/serviceentry.go index bf77b501..4185d985 100644 --- a/admiral/pkg/clusters/serviceentry.go +++ b/admiral/pkg/clusters/serviceentry.go @@ -20,11 +20,10 @@ import ( "time" ) -func createServiceEntry(rc *RemoteController, admiralCache *AdmiralCache, +func createServiceEntry(rc *RemoteController, config AdmiralParams, admiralCache *AdmiralCache, destDeployment *k8sAppsV1.Deployment, serviceEntries map[string]*networking.ServiceEntry) *networking.ServiceEntry { - workloadIdentityKey := common.GetWorkloadIdentifier() - globalFqdn := common.GetCname(destDeployment, workloadIdentityKey, common.GetHostnameSuffix()) + globalFqdn := common.GetCname(destDeployment, config.LabelSet.WorkloadIdentityLabel, config.HostnameSuffix) //Handling retries for getting/putting service entries from/in cache @@ -64,10 +63,10 @@ func createServiceEntry(rc *RemoteController, admiralCache *AdmiralCache, } var san []string - if common.GetEnableSAN() { - tmpSan := common.GetSAN(common.GetSANPrefix(), destDeployment, workloadIdentityKey) + if config.EnableSAN { + tmpSan := common.GetSAN(config.SANPrefix, destDeployment, config.LabelSet.WorkloadIdentityLabel) if len(tmpSan) > 0 { - san = []string{common.GetSAN(common.GetSANPrefix(), destDeployment, workloadIdentityKey)} + san = []string{common.GetSAN(config.SANPrefix, destDeployment, config.LabelSet.WorkloadIdentityLabel)} } } else { san = nil @@ -128,7 +127,7 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem serviceInstance := getServiceForDeployment(rc, deploymentInstance[0]) - cname = common.GetCname(deploymentInstance[0], common.GetWorkloadIdentifier(), cname) + cname = common.GetCname(deploymentInstance[0], remoteRegistry.config.LabelSet.WorkloadIdentityLabel, cname) remoteRegistry.AdmiralCache.IdentityClusterCache.Put(sourceIdentity, rc.ClusterID, rc.ClusterID) remoteRegistry.AdmiralCache.CnameClusterCache.Put(cname, rc.ClusterID, rc.ClusterID) @@ -137,7 +136,7 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem sourceDeployments[rc.ClusterID] = deploymentInstance[0] - createServiceEntry(rc, remoteRegistry.AdmiralCache, deploymentInstance[0], serviceEntries) + createServiceEntry(rc, remoteRegistry.config, remoteRegistry.AdmiralCache, deploymentInstance[0], serviceEntries) } @@ -150,7 +149,8 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem remoteRegistry.AdmiralCache.CnameDependentClusterCache.Put(cname, clusterId, clusterId) } - AddServiceEntriesWithDr(remoteRegistry.AdmiralCache, dependentClusters, remoteRegistry.remoteControllers, serviceEntries) + AddServiceEntriesWithDr(remoteRegistry.AdmiralCache, dependentClusters, remoteRegistry.remoteControllers, serviceEntries, + remoteRegistry.config.SyncNamespace) //update the address to local fqdn for service entry in a cluster local to the service instance for sourceCluster, serviceInstance := range sourceServices { @@ -166,7 +166,7 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem oldPorts := ep.Ports ep.Ports = meshPorts AddServiceEntriesWithDr(remoteRegistry.AdmiralCache, map[string]string{sourceCluster: sourceCluster}, remoteRegistry.remoteControllers, - map[string]*networking.ServiceEntry{key: serviceEntry}) + map[string]*networking.ServiceEntry{key: serviceEntry}, remoteRegistry.config.SyncNamespace) //swap it back to use for next iteration ep.Address = clusterIngress ep.Ports = oldPorts @@ -235,8 +235,8 @@ func createSeWithDrLabels(remoteController *RemoteController, localCluster bool, return allSes } -func AddServiceEntriesWithDr(cache *AdmiralCache, sourceClusters map[string]string, rcs map[string]*RemoteController, serviceEntries map[string]*networking.ServiceEntry) { - syncNamespace := common.GetSyncNamespace() +func AddServiceEntriesWithDr(cache *AdmiralCache, sourceClusters map[string]string, rcs map[string]*RemoteController, serviceEntries map[string]*networking.ServiceEntry, + syncNamespace string) { for _, se := range serviceEntries { //add service entry @@ -259,7 +259,7 @@ func AddServiceEntriesWithDr(cache *AdmiralCache, sourceClusters map[string]stri //Add a label if identityId, ok := cache.CnameIdentityCache.Load(se.Hosts[0]); ok { - newServiceEntry.Labels = map[string]string{common.GetWorkloadIdentifier(): fmt.Sprintf("%v", identityId)} + newServiceEntry.Labels = map[string]string{common.DefaultGlobalIdentifier(): fmt.Sprintf("%v", identityId)} } if err == nil { diff --git a/admiral/pkg/clusters/serviceentry_test.go b/admiral/pkg/clusters/serviceentry_test.go index 60489032..6b008b7f 100644 --- a/admiral/pkg/clusters/serviceentry_test.go +++ b/admiral/pkg/clusters/serviceentry_test.go @@ -96,12 +96,12 @@ func TestAddServiceEntriesWithDr(t *testing.T) { } - AddServiceEntriesWithDr(&admiralCache, map[string]string{"cl1":"cl1"}, map[string]*RemoteController{"cl1":rc}, map[string]*networking.ServiceEntry{"se1": &se}) + AddServiceEntriesWithDr(&admiralCache, map[string]string{"cl1":"cl1"}, map[string]*RemoteController{"cl1":rc}, map[string]*networking.ServiceEntry{"se1": &se}, "admiral-sync") } func TestCreateServiceEntryForNewServiceOrPod(t *testing.T) { - p := common.AdmiralParams{ + p := AdmiralParams{ KubeconfigPath: "testdata/fake.config", } rr, _ := InitAdmiral(context.Background(), p) @@ -319,6 +319,13 @@ func TestCreateServiceEntry(t *testing.T) { } }) + params := AdmiralParams{ + EnableSAN: true, + SANPrefix: "prefix", + LabelSet:&common.LabelSet{WorkloadIdentityLabel:"identity"}, + HostnameSuffix: "mesh", + } + cacheWithEntry := ServiceEntryAddressStore{ EntryAddresses: map[string]string{"e2e.my-first-service.mesh": localAddress}, Addresses: []string{localAddress}, @@ -335,7 +342,7 @@ func TestCreateServiceEntry(t *testing.T) { deployment := v12.Deployment{} deployment.Spec.Template.Labels = map[string]string{"env":"e2e", "identity":"my-first-service", } - resultingEntry := createServiceEntry(rc, &admiralCache, &deployment, map[string]*networking.ServiceEntry{}) + resultingEntry := createServiceEntry(rc, params, &admiralCache, &deployment, map[string]*networking.ServiceEntry{}) if resultingEntry.Hosts[0] != "e2e.my-first-service.mesh" { t.Errorf("Host mismatch. Got: %v, expected: e2e.my-first-service.mesh", resultingEntry.Hosts[0]) diff --git a/admiral/pkg/clusters/types.go b/admiral/pkg/clusters/types.go index d00ab71d..f83d2b71 100644 --- a/admiral/pkg/clusters/types.go +++ b/admiral/pkg/clusters/types.go @@ -2,6 +2,7 @@ package clusters import ( "context" + "fmt" "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" @@ -12,8 +13,35 @@ import ( k8s "k8s.io/client-go/kubernetes" "sync" + "time" ) +type AdmiralParams struct { + KubeconfigPath string + CacheRefreshDuration time.Duration + ClusterRegistriesNamespace string + DependenciesNamespace string + SyncNamespace string + EnableSAN bool + SANPrefix string + SecretResolver string + LabelSet *common.LabelSet + HostnameSuffix string + +} + +func (b AdmiralParams) String() string { + return fmt.Sprintf("KubeconfigPath=%v ", b.KubeconfigPath) + + fmt.Sprintf("CacheRefreshDuration=%v ", b.CacheRefreshDuration) + + fmt.Sprintf("ClusterRegistriesNamespace=%v ", b.ClusterRegistriesNamespace) + + fmt.Sprintf("DependenciesNamespace=%v ", b.DependenciesNamespace) + + fmt.Sprintf("EnableSAN=%v ", b.EnableSAN) + + fmt.Sprintf("SANPrefix=%v ", b.SANPrefix) + + fmt.Sprintf("LabelSet=%v ", b.LabelSet) + + fmt.Sprintf("SecretResolver=%v ", b.SecretResolver) +} + + type RemoteController struct { ClusterID string IstioConfigStore istio.ConfigStoreCache @@ -39,6 +67,7 @@ type AdmiralCache struct { } type RemoteRegistry struct { + config AdmiralParams sync.Mutex remoteControllers map[string]*RemoteController secretClient k8s.Interface @@ -100,7 +129,7 @@ func (dh *DependencyHandler) Added(obj *v1.Dependency) { updateIdentityDependencyCache(sourceIdentity, dh.RemoteRegistry.AdmiralCache.IdentityDependencyCache, obj) - handleDependencyRecord(obj.Spec.IdentityLabel, sourceIdentity, dh.RemoteRegistry.AdmiralCache, dh.RemoteRegistry.remoteControllers, obj) + handleDependencyRecord(obj.Spec.IdentityLabel, sourceIdentity, dh.RemoteRegistry.AdmiralCache, dh.RemoteRegistry.remoteControllers, dh.RemoteRegistry.config, obj) } @@ -123,7 +152,7 @@ func (pc *DeploymentHandler) Added(obj *k8sAppsV1.Deployment) { globalIdentifier := common.GetDeploymentGlobalIdentifier(obj) if len(globalIdentifier) == 0 { - log.Infof(LogFormat, "Event", "deployment", obj.Name, "", "Skipped as '"+common.GetWorkloadIdentifier()+" was not found', namespace="+obj.Namespace) + log.Infof(LogFormat, "Event", "deployment", obj.Name, "", "Skipped as '"+common.DefaultGlobalIdentifier()+" was not found', namespace="+obj.Namespace) return } @@ -142,7 +171,7 @@ func (pc *PodHandler) Added(obj *k8sV1.Pod) { globalIdentifier := common.GetPodGlobalIdentifier(obj) if len(globalIdentifier) == 0 { - log.Infof(LogFormat, "Event", "deployment", obj.Name, "", "Skipped as '"+common.GetWorkloadIdentifier()+" was not found', namespace="+obj.Namespace) + log.Infof(LogFormat, "Event", "deployment", obj.Name, "", "Skipped as '"+common.DefaultGlobalIdentifier()+" was not found', namespace="+obj.Namespace) return } diff --git a/admiral/pkg/controller/admiral/configmap.go b/admiral/pkg/controller/admiral/configmap.go index 3ac403cf..20ae0a74 100644 --- a/admiral/pkg/controller/admiral/configmap.go +++ b/admiral/pkg/controller/admiral/configmap.go @@ -1,7 +1,6 @@ package admiral import ( - "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" "k8s.io/api/core/v1" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -24,9 +23,8 @@ type ConfigMapController struct { //todo this is a temp state, eventually changes will have to be made to give each cluster it's own configmap -func NewConfigMapController() (*ConfigMapController, error) { - kubeconfigPath := common.GetKubeconfigPath() - namespaceToUse := common.GetSyncNamespace() +func NewConfigMapController(kubeconfigPath string, namespaceToUse string) (*ConfigMapController, error) { + if kubeconfigPath == "" { config, err := rest.InClusterConfig() if err != nil { diff --git a/admiral/pkg/controller/admiral/configmap_test.go b/admiral/pkg/controller/admiral/configmap_test.go index 00bf55e3..abed187a 100644 --- a/admiral/pkg/controller/admiral/configmap_test.go +++ b/admiral/pkg/controller/admiral/configmap_test.go @@ -3,15 +3,12 @@ package admiral import ( "errors" "github.com/google/go-cmp/cmp" - "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" - _ "github.com/istio-ecosystem/admiral/admiral/pkg/test" "github.com/sirupsen/logrus" "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes/fake" "testing" ) - func TestConfigMapController_GetConfigMap(t *testing.T) { configmapController := ConfigMapController{ ConfigmapNamespace: "admiral", @@ -102,8 +99,7 @@ func TestNewConfigMapController(t *testing.T) { for _, c := range testCases { t.Run(c.name, func(t *testing.T) { - common.SetKubeconfigPath(c.kubeconfigPath) - controller, err := NewConfigMapController() + controller, err := NewConfigMapController(c.kubeconfigPath, c.namespace) if err==nil && c.expectedError==nil { //only do these in an error-less context if c.namespace != controller.ConfigmapNamespace { diff --git a/admiral/pkg/controller/admiral/deployment.go b/admiral/pkg/controller/admiral/deployment.go index 39db07d9..afefb073 100644 --- a/admiral/pkg/controller/admiral/deployment.go +++ b/admiral/pkg/controller/admiral/deployment.go @@ -126,11 +126,11 @@ func (d *DeploymentController) GetDeployments() ([]*k8sAppsV1.Deployment, error) return res, nil } -func NewDeploymentController(stopCh <-chan struct{}, handler DeploymentHandler, config *rest.Config, resyncPeriod time.Duration) (*DeploymentController, error) { +func NewDeploymentController(stopCh <-chan struct{}, handler DeploymentHandler, config *rest.Config, resyncPeriod time.Duration, labelSet *common.LabelSet) (*DeploymentController, error) { deploymentController := DeploymentController{} deploymentController.DeploymentHandler = handler - deploymentController.labelSet = common.GetLabelSet() + deploymentController.labelSet = labelSet deploymentCache := deploymentCache{} deploymentCache.cache = make(map[string]*DeploymentClusterEntry) diff --git a/admiral/pkg/controller/admiral/deployment_test.go b/admiral/pkg/controller/admiral/deployment_test.go index f2316ca2..aee75f77 100644 --- a/admiral/pkg/controller/admiral/deployment_test.go +++ b/admiral/pkg/controller/admiral/deployment_test.go @@ -154,7 +154,7 @@ func TestNewDeploymentController(t *testing.T) { stop := make(chan struct{}) depHandler := test.MockDeploymentHandler{} - depCon, err := NewDeploymentController(stop, &depHandler, config, time.Duration(1000)) + depCon, err := NewDeploymentController(stop, &depHandler, config, time.Duration(1000), &common.LabelSet{}) if depCon == nil { t.Errorf("Deployment controller should not be nil") diff --git a/admiral/pkg/controller/admiral/pod.go b/admiral/pkg/controller/admiral/pod.go index afeecae8..01ae27d7 100644 --- a/admiral/pkg/controller/admiral/pod.go +++ b/admiral/pkg/controller/admiral/pod.go @@ -127,11 +127,11 @@ func (d *PodController) GetPods() ([]*k8sV1.Pod, error) { return res, nil } -func NewPodController(stopCh <-chan struct{}, handler PodHandler, config *rest.Config, resyncPeriod time.Duration) (*PodController, error) { +func NewPodController(stopCh <-chan struct{}, handler PodHandler, config *rest.Config, resyncPeriod time.Duration, labelSet *common.LabelSet) (*PodController, error) { podController := PodController{} podController.PodHandler = handler - podController.labelSet = common.GetLabelSet() + podController.labelSet = labelSet podCache := podCache{} podCache.cache = make(map[string]*PodClusterEntry) diff --git a/admiral/pkg/controller/admiral/pod_test.go b/admiral/pkg/controller/admiral/pod_test.go index ebc68fa3..61563615 100644 --- a/admiral/pkg/controller/admiral/pod_test.go +++ b/admiral/pkg/controller/admiral/pod_test.go @@ -20,7 +20,7 @@ func TestNewPodController(t *testing.T) { stop := make(chan struct{}) handler := test.MockPodHandler{} - podController, err := NewPodController(stop, &handler, config, time.Duration(1000)) + podController, err := NewPodController(stop, &handler, config, time.Duration(1000), &common.LabelSet{}) if err != nil { t.Errorf("Unexpected err %v", err) diff --git a/admiral/pkg/controller/common/common.go b/admiral/pkg/controller/common/common.go index fe0a98b7..1dbfb795 100644 --- a/admiral/pkg/controller/common/common.go +++ b/admiral/pkg/controller/common/common.go @@ -29,28 +29,36 @@ const ( ) func GetPodGlobalIdentifier(pod *k8sV1.Pod) string { - identity := pod.Labels[GetWorkloadIdentifier()] + identity := pod.Labels[DefaultGlobalIdentifier()] if len(identity) == 0 { - identity = pod.Annotations[GetWorkloadIdentifier()] + identity = pod.Annotations[DefaultGlobalIdentifier()] } return identity } func GetDeploymentGlobalIdentifier(deployment *k8sAppsV1.Deployment) string { - identity := deployment.Spec.Template.Labels[GetWorkloadIdentifier()] + identity := deployment.Spec.Template.Labels[DefaultGlobalIdentifier()] if len(identity) == 0 { //TODO can this be removed now? This was for backward compatibility - identity = deployment.Spec.Template.Annotations[GetWorkloadIdentifier()] + identity = deployment.Spec.Template.Annotations[DefaultGlobalIdentifier()] } return identity } +func DefaultGlobalIdentifier() string { + return Identity +} + // GetCname returns cname in the format ..global, Ex: stage.Admiral.services.registry.global func GetCname(deployment *k8sAppsV1.Deployment, identifier string, nameSuffix string) string { var environment = GetEnv(deployment) - alias := GetValueForKeyFromDeployment(identifier, deployment) + alias := deployment.Spec.Template.Labels[identifier] + if len(alias) == 0 { + logrus.Warnf("%v label missing on service %v in namespace %v. Falling back to annotation to create cname.", identifier, deployment.Name, deployment.Namespace) + alias = deployment.Spec.Template.Annotations[identifier] + } if len(alias) == 0 { - logrus.Errorf("Unable to get cname for deployment with name %v in namespace %v as it doesn't have the %v annotation or label", deployment.Name, deployment.Namespace, identifier) + logrus.Errorf("Unable to get cname for service with name %v in namespace %v as it doesn't have the %v annotation", deployment.Name, deployment.Namespace, identifier) return "" } return environment + Sep + alias + Sep + nameSuffix @@ -75,9 +83,8 @@ func GetEnv(deployment *k8sAppsV1.Deployment) string { // GetSAN returns SAN for a service entry in the format spiffe:///, Ex: spiffe://subdomain.domain.com/Admiral.platform.mesh.server func GetSAN(domain string, deployment *k8sAppsV1.Deployment, identifier string) string { - identifierVal := GetValueForKeyFromDeployment(identifier, deployment) + identifierVal := deployment.Spec.Template.Labels[identifier] if len(identifierVal) == 0 { - logrus.Errorf("Unable to get SAN for deployment with name %v in namespace %v as it doesn't have the %v annotation or label", deployment.Name, deployment.Namespace, identifier) return "" } if len(domain) > 0 { @@ -91,12 +98,3 @@ func GetNodeLocality(node *k8sV1.Node) string { region, _ := node.Labels[NodeRegionLabel] return region } - -func GetValueForKeyFromDeployment(key string, deployment *k8sAppsV1.Deployment) string { - value := deployment.Spec.Template.Labels[key] - if len(value) == 0 { - logrus.Warnf("%v label missing on deployment %v in namespace %v. Falling back to annotation.", key, deployment.Name, deployment.Namespace) - value = deployment.Spec.Template.Annotations[key] - } - return value -} diff --git a/admiral/pkg/controller/common/common_test.go b/admiral/pkg/controller/common/common_test.go index e6ca55fb..304837e9 100644 --- a/admiral/pkg/controller/common/common_test.go +++ b/admiral/pkg/controller/common/common_test.go @@ -6,7 +6,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1" "reflect" "testing" - ) +) func TestGetSAN(t *testing.T) { t.Parallel() @@ -16,7 +16,6 @@ func TestGetSAN(t *testing.T) { domain := "preprd" deployment := k8sAppsV1.Deployment{Spec: k8sAppsV1.DeploymentSpec{Template: v12.PodTemplateSpec{ObjectMeta: v1.ObjectMeta{Labels: map[string]string{identifier: identifierVal}}}}} - deploymentWithAnnotation := k8sAppsV1.Deployment{Spec: k8sAppsV1.DeploymentSpec{Template: v12.PodTemplateSpec{ObjectMeta: v1.ObjectMeta{Annotations: map[string]string{identifier: identifierVal}}}}} deploymentWithNoIdentifier := k8sAppsV1.Deployment{} @@ -27,17 +26,11 @@ func TestGetSAN(t *testing.T) { wantSAN string }{ { - name: "should return valid SAN (from label)", + name: "should return valid SAN", deployment: deployment, domain: domain, wantSAN: "spiffe://" + domain + "/" + identifierVal, }, - { - name: "should return valid SAN (from annotation)", - deployment: deploymentWithAnnotation, - domain: domain, - wantSAN: "spiffe://" + domain + "/" + identifierVal, - }, { name: "should return valid SAN with no domain prefix", deployment: deployment, diff --git a/admiral/pkg/controller/common/config.go b/admiral/pkg/controller/common/config.go deleted file mode 100644 index 63493f51..00000000 --- a/admiral/pkg/controller/common/config.go +++ /dev/null @@ -1,72 +0,0 @@ -package common - -import ( - "sync" - "time" -) - -var admiralParams = AdmiralParams{ - LabelSet: &LabelSet{}, -} - -var once sync.Once - -func InitializeConfig(params AdmiralParams) { - once.Do(func() { - admiralParams = params - }) -} - -func GetAdmiralParams() AdmiralParams { - return admiralParams -} - -func GetKubeconfigPath() string { - return admiralParams.KubeconfigPath -} - -func GetCacheRefreshDuration() time.Duration { - return admiralParams.CacheRefreshDuration -} - -func GetClusterRegistriesNamespace() string { - return admiralParams.ClusterRegistriesNamespace -} - -func GetDependenciesNamespace() string { - return admiralParams.DependenciesNamespace -} - -func GetSyncNamespace() string { - return admiralParams.SyncNamespace -} - -func GetEnableSAN() bool { - return admiralParams.EnableSAN -} - -func GetSANPrefix() string { - return admiralParams.SANPrefix -} - -func GetSecretResolver() string { - return admiralParams.SecretResolver -} - -func GetLabelSet() *LabelSet { - return admiralParams.LabelSet -} - -func GetHostnameSuffix() string { - return admiralParams.HostnameSuffix -} - -func GetWorkloadIdentifier() string { - return admiralParams.LabelSet.WorkloadIdentityKey -} - -///Setters - be careful - -func SetKubeconfigPath(path string) { - admiralParams.KubeconfigPath = path -} \ No newline at end of file diff --git a/admiral/pkg/controller/common/configInitializer.go b/admiral/pkg/controller/common/configInitializer.go deleted file mode 100644 index 5080385c..00000000 --- a/admiral/pkg/controller/common/configInitializer.go +++ /dev/null @@ -1,25 +0,0 @@ -package common - -import "time" - -//Because the admiralParams are not a global singleton, we'll initialize them here and then use setters as needed throughout the tests. Import this package to ensure the initialization happens in your test -//If you're not using anything from the package (and getting an unused import error), import it with the name of _ (_ "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common") -func init() { - p := AdmiralParams{ - KubeconfigPath: "testdata/fake.config", - LabelSet: &LabelSet{}, - EnableSAN: true, - SANPrefix: "prefix", - HostnameSuffix: "mesh", - SyncNamespace: "ns", - CacheRefreshDuration: time.Minute, - ClusterRegistriesNamespace: "default", - DependenciesNamespace: "default", - SecretResolver: "", - - } - - p.LabelSet.WorkloadIdentityKey="identity" - - InitializeConfig(p) -} \ No newline at end of file diff --git a/admiral/pkg/controller/common/config_test.go b/admiral/pkg/controller/common/config_test.go deleted file mode 100644 index e7c5b39c..00000000 --- a/admiral/pkg/controller/common/config_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package common - -import ( - "testing" - "time" -) - -func TestConfigManagement(t *testing.T) { - - //Initial state comes from the init method in configInitializer.go - //p := AdmiralParams{ - // KubeconfigPath: "testdata/fake.config", - // LabelSet: &LabelSet{}, - // EnableSAN: true, - // SANPrefix: "prefix", - // HostnameSuffix: "mesh", - // SyncNamespace: "ns", - //} - // - //p.LabelSet.WorkloadIdentityKey="identity" - - //trying to initialize again. If the singleton pattern works, none of these will have changed - p := AdmiralParams{ - KubeconfigPath: "DIFFERENT", - LabelSet: &LabelSet{}, - EnableSAN: false, - SANPrefix: "BAD_PREFIX", - HostnameSuffix: "NOT_MESH", - SyncNamespace: "NOT_A_NAMESPACE", - CacheRefreshDuration: time.Hour, - ClusterRegistriesNamespace: "NOT_DEFAULT", - DependenciesNamespace: "NOT_DEFAULT", - SecretResolver: "INSECURE_RESOLVER", - - } - - p.LabelSet.WorkloadIdentityKey="BAD_LABEL" - - InitializeConfig(p) - - if GetWorkloadIdentifier() != "identity" { - t.Errorf("Workload identifier mismatch, expected identity, got %v", GetWorkloadIdentifier()) - } - if GetKubeconfigPath() != "testdata/fake.config" { - t.Errorf("Kubeconfig path mismatch, expected testdata/fake.config, got %v", GetKubeconfigPath()) - } - if GetSANPrefix() != "prefix" { - t.Errorf("San prefix mismatch, expected prefix, got %v", GetSANPrefix()) - } - if GetHostnameSuffix() != "mesh" { - t.Errorf("Hostname suffix mismatch, expected mesh, got %v", GetHostnameSuffix()) - } - if GetSyncNamespace() != "ns" { - t.Errorf("Sync namespace mismatch, expected ns, got %v", GetSyncNamespace()) - } - if GetEnableSAN() != true { - t.Errorf("Enable SAN mismatch, expected true, got %v", GetEnableSAN()) - } - if GetCacheRefreshDuration() != time.Minute { - t.Errorf("Cachee refresh duration mismatch, expected %v, got %v", time.Minute, GetCacheRefreshDuration()) - } - if GetClusterRegistriesNamespace() != "default" { - t.Errorf("Cluster registry namespace mismatch, expected default, got %v", GetClusterRegistriesNamespace()) - } - if GetDependenciesNamespace() != "default" { - t.Errorf("Dependency namespace mismatch, expected default, got %v", GetDependenciesNamespace()) - } - if GetSecretResolver() != "" { - t.Errorf("Secret resolver mismatch, expected empty string, got %v", GetSecretResolver()) - } - -} \ No newline at end of file diff --git a/admiral/pkg/controller/common/types.go b/admiral/pkg/controller/common/types.go index 93903a66..dbe2abda 100644 --- a/admiral/pkg/controller/common/types.go +++ b/admiral/pkg/controller/common/types.go @@ -1,9 +1,7 @@ package common import ( - "fmt" "sync" - "time" ) type Map struct { @@ -17,39 +15,13 @@ type MapOfMaps struct { mutex *sync.Mutex } -type AdmiralParams struct { - KubeconfigPath string - CacheRefreshDuration time.Duration - ClusterRegistriesNamespace string - DependenciesNamespace string - SyncNamespace string - EnableSAN bool - SANPrefix string - SecretResolver string - LabelSet *LabelSet - HostnameSuffix string - -} - -func (b AdmiralParams) String() string { - return fmt.Sprintf("KubeconfigPath=%v ", b.KubeconfigPath) + - fmt.Sprintf("CacheRefreshDuration=%v ", b.CacheRefreshDuration) + - fmt.Sprintf("ClusterRegistriesNamespace=%v ", b.ClusterRegistriesNamespace) + - fmt.Sprintf("DependenciesNamespace=%v ", b.DependenciesNamespace) + - fmt.Sprintf("EnableSAN=%v ", b.EnableSAN) + - fmt.Sprintf("SANPrefix=%v ", b.SANPrefix) + - fmt.Sprintf("LabelSet=%v ", b.LabelSet) + - fmt.Sprintf("SecretResolver=%v ", b.SecretResolver) -} - - type LabelSet struct { DeploymentAnnotation string SubsetLabel string NamespaceSidecarInjectionLabel string NamespaceSidecarInjectionLabelValue string 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) + WorkloadIdentityLabel string } func NewMap() *Map { diff --git a/admiral/pkg/controller/util/util.go b/admiral/pkg/controller/util/util.go index f712db23..d468840b 100644 --- a/admiral/pkg/controller/util/util.go +++ b/admiral/pkg/controller/util/util.go @@ -35,4 +35,4 @@ func Contains(vs []string, t string) bool { } } return false -} \ No newline at end of file +} From 5d47585fef8b81f7ec6bc4e3d8668aa4931efedf Mon Sep 17 00:00:00 2001 From: josephpeacock <51184065+josephpeacock@users.noreply.github.com> Date: Wed, 19 Feb 2020 14:06:03 -0800 Subject: [PATCH 18/45] Fix init (#70) * Overriding defaults for basic example Signed-off-by: Joe Peacock Signed-off-by: Madeline --- admiral/cmd/admiral/cmd/root.go | 2 +- .../admiral/overlays/demosinglecluster/envconfig_values.yaml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/admiral/cmd/admiral/cmd/root.go b/admiral/cmd/admiral/cmd/root.go index 852d9940..dd6d1708 100644 --- a/admiral/cmd/admiral/cmd/root.go +++ b/admiral/cmd/admiral/cmd/root.go @@ -62,7 +62,7 @@ func GetRootCmd(args []string) *cobra.Command { "Namespace to monitor for secrets defaults to admiral-secrets") rootCmd.PersistentFlags().StringVar(¶ms.DependenciesNamespace, "dependency_namespace", "default", "Namespace to monitor for secrets defaults to admiral-secrets") - rootCmd.PersistentFlags().StringVar(¶ms.SyncNamespace, "sync_namespace", "admiral-sync", + rootCmd.PersistentFlags().StringVar(¶ms.SyncNamespace, "sync_namespace", "default", "Namespace to monitor for secrets defaults to admiral-secrets") rootCmd.PersistentFlags().DurationVar(¶ms.CacheRefreshDuration, "sync_period", 5*time.Minute, "Interval for syncing Kubernetes resources, defaults to 5 min") diff --git a/install/admiral/overlays/demosinglecluster/envconfig_values.yaml b/install/admiral/overlays/demosinglecluster/envconfig_values.yaml index c09fd83d..95ab838b 100644 --- a/install/admiral/overlays/demosinglecluster/envconfig_values.yaml +++ b/install/admiral/overlays/demosinglecluster/envconfig_values.yaml @@ -16,4 +16,6 @@ spec: - admiral - --secret_namespace - admiral + - --sync_namespace + - admiral name: admiral \ No newline at end of file From 571687442ffc89e7079eb2b1e0dcb8aec1a5321a Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Wed, 26 Feb 2020 12:30:54 -0800 Subject: [PATCH 19/45] Add locality lb settings support on destination rules (#41) * Bootstrapping admiral * Add circle ci config * Fix the working directory for builds * Bootstrapping admiral Signed-off-by: aattuluri * Add circle ci config Signed-off-by: aattuluri * Fix the working directory for builds Signed-off-by: aattuluri * Add build status badge. Signed-off-by: aattuluri * Revert "Add build status badge." This reverts commit a9fa4095c7c3d8d157231664134ef850f0797a5c. * Working version after major refactor. * Fix test files. * Use global traffic policy to update destination rule. * Minor fixes. * Update image for admiral * Add outlier detection for locality load balancing to work * Undo tag change * Sample files for demo * Remove/comment some tests * Add the needed method * Commenting out few more tests. * Remove unwanted files. * Adding back missing tests. * Create an alias for logrus. * Fixing tests. Signed-off-by: Madeline --- Makefile | 9 +- admiral/cmd/admiral/cmd/root.go | 7 +- .../pkg/apis/admiral/model/dependency.pb.go | 21 +- .../pkg/apis/admiral/model/dependency.proto | 2 +- .../apis/admiral/model/globalrouting.pb.go | 53 +- .../apis/admiral/model/globalrouting.proto | 2 +- .../apis/admiral/v1/zz_generated.deepcopy.go | 4 +- .../typed/admiral/v1/admiral_client.go | 3 +- .../typed/admiral/v1/fake/fake_dependency.go | 2 +- .../v1/fake/fake_globaltrafficpolicy.go | 2 +- admiral/pkg/clusters/handler.go | 569 +++++ admiral/pkg/clusters/registry.go | 479 +--- admiral/pkg/clusters/registry_test.go | 112 +- admiral/pkg/clusters/serviceentry.go | 48 +- admiral/pkg/clusters/serviceentry_test.go | 663 +++--- admiral/pkg/clusters/types.go | 12 +- admiral/pkg/clusters/util.go | 40 +- .../pkg/controller/admiral/admiralclient.go | 2 +- .../pkg/controller/admiral/configmap_test.go | 4 +- admiral/pkg/controller/admiral/controller.go | 9 +- admiral/pkg/controller/admiral/dependency.go | 4 +- admiral/pkg/controller/admiral/deployment.go | 2 +- .../pkg/controller/admiral/deployment_test.go | 4 +- .../pkg/controller/admiral/globaltraffic.go | 19 +- admiral/pkg/controller/admiral/node.go | 2 +- admiral/pkg/controller/admiral/pod.go | 2 +- admiral/pkg/controller/admiral/service.go | 2 +- admiral/pkg/controller/common/common.go | 22 +- admiral/pkg/controller/istio/client.go | 632 ------ admiral/pkg/controller/istio/config.go | 258 --- admiral/pkg/controller/istio/controller.go | 297 --- admiral/pkg/controller/istio/conversion.go | 366 --- .../pkg/controller/istio/destinationrule.go | 75 + admiral/pkg/controller/istio/queue.go | 125 -- admiral/pkg/controller/istio/serviceentry.go | 75 + admiral/pkg/controller/istio/types.go | 1959 ----------------- .../pkg/controller/istio/virtualservice.go | 65 + .../pkg/controller/secret/secretcontroller.go | 8 +- admiral/pkg/test/mock.go | 32 +- go.mod | 65 +- go.sum | 403 +++- install/admiral/base/deployments.yaml | 2 +- 42 files changed, 1647 insertions(+), 4815 deletions(-) create mode 100644 admiral/pkg/clusters/handler.go delete mode 100644 admiral/pkg/controller/istio/client.go delete mode 100644 admiral/pkg/controller/istio/config.go delete mode 100644 admiral/pkg/controller/istio/controller.go delete mode 100644 admiral/pkg/controller/istio/conversion.go create mode 100644 admiral/pkg/controller/istio/destinationrule.go delete mode 100644 admiral/pkg/controller/istio/queue.go create mode 100644 admiral/pkg/controller/istio/serviceentry.go delete mode 100644 admiral/pkg/controller/istio/types.go create mode 100644 admiral/pkg/controller/istio/virtualservice.go diff --git a/Makefile b/Makefile index d0f199db..72ee04ca 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ PROTOC_ZIP=protoc-$(PROTOC_VER)-osx-x86_64.zip # ROOT_PACKAGE :: the package (relative to $GOPATH/src) that is the target for code generation -ROOT_PACKAGE=github.com/admiral/admiral +ROOT_PACKAGE=github.com/istio-ecosystem/admiral/admiral # CUSTOM_RESOURCE_NAME :: the name of the custom resource that we're generating client code for CUSTOM_RESOURCE_NAME=admiral # CUSTOM_RESOURCE_VERSION :: the version of the resource @@ -66,12 +66,11 @@ api-gen: go install github.com/golang/protobuf/protoc-gen-go $(GOCMD) generate ./... go install k8s.io/code-generator/cmd/deepcopy-gen - $(GOBIN)/deepcopy-gen --input-dirs ./admiral/pkg/apis/admiral/model --bounding-dirs ./admiral/pkg/apis/admiral/model -O zz_generated.deepcopy -o $(GOPATH)/src crd-gen: - #go get -d -u -fix k8s.io/code-generator - #go get -d -u -fix k8s.io/apimachinery - #go get -d -u -fix k8s.io/gengo + go get -d -u -fix k8s.io/code-generator + go get -d -u -fix k8s.io/apimachinery + go get -d -u -fix k8s.io/gengo $(GOPATH)/src/k8s.io/code-generator/generate-groups.sh all "$(ROOT_PACKAGE)/pkg/client" "$(ROOT_PACKAGE)/pkg/apis" "$(CUSTOM_RESOURCE_NAME):$(CUSTOM_RESOURCE_VERSION)" # Cross compilation diff --git a/admiral/cmd/admiral/cmd/root.go b/admiral/cmd/admiral/cmd/root.go index dd6d1708..9f612c37 100644 --- a/admiral/cmd/admiral/cmd/root.go +++ b/admiral/cmd/admiral/cmd/root.go @@ -5,8 +5,8 @@ import ( "flag" "fmt" "github.com/istio-ecosystem/admiral/admiral/pkg/clusters" + "github.com/prometheus/common/log" "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" - "istio.io/istio/pkg/log" "os" "os/signal" "syscall" @@ -16,7 +16,6 @@ import ( ) var ( - loggingOptions = log.DefaultOptions() ctx, cancel = context.WithCancel(context.Background()) ) @@ -36,8 +35,7 @@ func GetRootCmd(args []string) *cobra.Command { if len(args) > 0 { return fmt.Errorf("%q is an invalid argument", args[0]) } - err := log.Configure(loggingOptions) - return err + return nil }, Run: func(cmd *cobra.Command, args []string) { log.Info("Starting Admiral") @@ -86,7 +84,6 @@ func GetRootCmd(args []string) *cobra.Command { "The hostname suffix to customize the cname generated by admiral. Default suffix value will be \"global\"") rootCmd.PersistentFlags().StringVar(¶ms.LabelSet.WorkloadIdentityLabel, "workload_identity_label", "identity", "The workload identity label key, on deployment which holds identity value used to generate cname by admiral. Default label key will be \"identity\"") - loggingOptions.AttachCobraFlags(rootCmd) return rootCmd } diff --git a/admiral/pkg/apis/admiral/model/dependency.pb.go b/admiral/pkg/apis/admiral/model/dependency.pb.go index 428e95ec..9b6eab76 100644 --- a/admiral/pkg/apis/admiral/model/dependency.pb.go +++ b/admiral/pkg/apis/admiral/model/dependency.pb.go @@ -3,9 +3,11 @@ package model -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -16,7 +18,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package type Dependency struct { // REQUIRED: identifier for the source workload @@ -34,16 +36,17 @@ func (m *Dependency) Reset() { *m = Dependency{} } func (m *Dependency) String() string { return proto.CompactTextString(m) } func (*Dependency) ProtoMessage() {} func (*Dependency) Descriptor() ([]byte, []int) { - return fileDescriptor_dependency_507a6246af59cbf1, []int{0} + return fileDescriptor_394735771c16e617, []int{0} } + func (m *Dependency) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Dependency.Unmarshal(m, b) } func (m *Dependency) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_Dependency.Marshal(b, m, deterministic) } -func (dst *Dependency) XXX_Merge(src proto.Message) { - xxx_messageInfo_Dependency.Merge(dst, src) +func (m *Dependency) XXX_Merge(src proto.Message) { + xxx_messageInfo_Dependency.Merge(m, src) } func (m *Dependency) XXX_Size() int { return xxx_messageInfo_Dependency.Size(m) @@ -79,9 +82,9 @@ func init() { proto.RegisterType((*Dependency)(nil), "admiral.global.v1alpha.Dependency") } -func init() { proto.RegisterFile("dependency.proto", fileDescriptor_dependency_507a6246af59cbf1) } +func init() { proto.RegisterFile("dependency.proto", fileDescriptor_394735771c16e617) } -var fileDescriptor_dependency_507a6246af59cbf1 = []byte{ +var fileDescriptor_394735771c16e617 = []byte{ // 154 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x48, 0x49, 0x2d, 0x48, 0xcd, 0x4b, 0x49, 0xcd, 0x4b, 0xae, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x4b, 0x4c, diff --git a/admiral/pkg/apis/admiral/model/dependency.proto b/admiral/pkg/apis/admiral/model/dependency.proto index 7b31f443..81a029b9 100644 --- a/admiral/pkg/apis/admiral/model/dependency.proto +++ b/admiral/pkg/apis/admiral/model/dependency.proto @@ -5,7 +5,7 @@ package admiral.global.v1alpha; option go_package = "model"; // ``` -// apiVersion: admiral.global.traffic/v1alpha +// apiVersion: admiral.io/v1alpha1 // kind: Dependency // metadata: // name: my-dependency diff --git a/admiral/pkg/apis/admiral/model/globalrouting.pb.go b/admiral/pkg/apis/admiral/model/globalrouting.pb.go index c3bd32d6..868b8286 100644 --- a/admiral/pkg/apis/admiral/model/globalrouting.pb.go +++ b/admiral/pkg/apis/admiral/model/globalrouting.pb.go @@ -3,9 +3,11 @@ package model -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -16,14 +18,14 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package type TrafficPolicy_LbType int32 const ( - // Traffic with be routed to local locality first - // if there are no health instances in the local locality traffic will be routed to - // remote locality + //Traffic with be routed to local locality first + //if there are no health instances in the local locality traffic will be routed to + //remote locality TrafficPolicy_TOPOLOGY TrafficPolicy_LbType = 0 TrafficPolicy_FAILOVER TrafficPolicy_LbType = 1 ) @@ -32,6 +34,7 @@ var TrafficPolicy_LbType_name = map[int32]string{ 0: "TOPOLOGY", 1: "FAILOVER", } + var TrafficPolicy_LbType_value = map[string]int32{ "TOPOLOGY": 0, "FAILOVER": 1, @@ -40,8 +43,9 @@ var TrafficPolicy_LbType_value = map[string]int32{ func (x TrafficPolicy_LbType) String() string { return proto.EnumName(TrafficPolicy_LbType_name, int32(x)) } + func (TrafficPolicy_LbType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_globalrouting_80c97d19fb14f2ca, []int{1, 0} + return fileDescriptor_a5c0dc509add6f4f, []int{1, 0} } type GlobalTrafficPolicy struct { @@ -61,16 +65,17 @@ func (m *GlobalTrafficPolicy) Reset() { *m = GlobalTrafficPolicy{} } func (m *GlobalTrafficPolicy) String() string { return proto.CompactTextString(m) } func (*GlobalTrafficPolicy) ProtoMessage() {} func (*GlobalTrafficPolicy) Descriptor() ([]byte, []int) { - return fileDescriptor_globalrouting_80c97d19fb14f2ca, []int{0} + return fileDescriptor_a5c0dc509add6f4f, []int{0} } + func (m *GlobalTrafficPolicy) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_GlobalTrafficPolicy.Unmarshal(m, b) } func (m *GlobalTrafficPolicy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_GlobalTrafficPolicy.Marshal(b, m, deterministic) } -func (dst *GlobalTrafficPolicy) XXX_Merge(src proto.Message) { - xxx_messageInfo_GlobalTrafficPolicy.Merge(dst, src) +func (m *GlobalTrafficPolicy) XXX_Merge(src proto.Message) { + xxx_messageInfo_GlobalTrafficPolicy.Merge(m, src) } func (m *GlobalTrafficPolicy) XXX_Size() int { return xxx_messageInfo_GlobalTrafficPolicy.Size(m) @@ -102,7 +107,7 @@ type TrafficPolicy struct { Dns string `protobuf:"bytes,1,opt,name=dns,proto3" json:"dns,omitempty"` // REQUIRED: type of global load distrubtion LbType TrafficPolicy_LbType `protobuf:"varint,2,opt,name=lbType,proto3,enum=admiral.global.v1alpha.TrafficPolicy_LbType" json:"lbType,omitempty"` - // weigth of primary and secondary must each 100 + //weigth of primary and secondary must each 100 Target []*TrafficGroup `protobuf:"bytes,3,rep,name=target,proto3" json:"target,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -113,16 +118,17 @@ func (m *TrafficPolicy) Reset() { *m = TrafficPolicy{} } func (m *TrafficPolicy) String() string { return proto.CompactTextString(m) } func (*TrafficPolicy) ProtoMessage() {} func (*TrafficPolicy) Descriptor() ([]byte, []int) { - return fileDescriptor_globalrouting_80c97d19fb14f2ca, []int{1} + return fileDescriptor_a5c0dc509add6f4f, []int{1} } + func (m *TrafficPolicy) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_TrafficPolicy.Unmarshal(m, b) } func (m *TrafficPolicy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_TrafficPolicy.Marshal(b, m, deterministic) } -func (dst *TrafficPolicy) XXX_Merge(src proto.Message) { - xxx_messageInfo_TrafficPolicy.Merge(dst, src) +func (m *TrafficPolicy) XXX_Merge(src proto.Message) { + xxx_messageInfo_TrafficPolicy.Merge(m, src) } func (m *TrafficPolicy) XXX_Size() int { return xxx_messageInfo_TrafficPolicy.Size(m) @@ -155,9 +161,9 @@ func (m *TrafficPolicy) GetTarget() []*TrafficGroup { } type TrafficGroup struct { - // region for the traffic + //region for the traffic Region string `protobuf:"bytes,1,opt,name=region,proto3" json:"region,omitempty"` - // weight for traffic this region should get. + //weight for traffic this region should get. Weight int32 `protobuf:"varint,2,opt,name=weight,proto3" json:"weight,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -168,16 +174,17 @@ func (m *TrafficGroup) Reset() { *m = TrafficGroup{} } func (m *TrafficGroup) String() string { return proto.CompactTextString(m) } func (*TrafficGroup) ProtoMessage() {} func (*TrafficGroup) Descriptor() ([]byte, []int) { - return fileDescriptor_globalrouting_80c97d19fb14f2ca, []int{2} + return fileDescriptor_a5c0dc509add6f4f, []int{2} } + func (m *TrafficGroup) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_TrafficGroup.Unmarshal(m, b) } func (m *TrafficGroup) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_TrafficGroup.Marshal(b, m, deterministic) } -func (dst *TrafficGroup) XXX_Merge(src proto.Message) { - xxx_messageInfo_TrafficGroup.Merge(dst, src) +func (m *TrafficGroup) XXX_Merge(src proto.Message) { + xxx_messageInfo_TrafficGroup.Merge(m, src) } func (m *TrafficGroup) XXX_Size() int { return xxx_messageInfo_TrafficGroup.Size(m) @@ -203,16 +210,16 @@ func (m *TrafficGroup) GetWeight() int32 { } func init() { + proto.RegisterEnum("admiral.global.v1alpha.TrafficPolicy_LbType", TrafficPolicy_LbType_name, TrafficPolicy_LbType_value) proto.RegisterType((*GlobalTrafficPolicy)(nil), "admiral.global.v1alpha.GlobalTrafficPolicy") proto.RegisterMapType((map[string]string)(nil), "admiral.global.v1alpha.GlobalTrafficPolicy.SelectorEntry") proto.RegisterType((*TrafficPolicy)(nil), "admiral.global.v1alpha.TrafficPolicy") proto.RegisterType((*TrafficGroup)(nil), "admiral.global.v1alpha.TrafficGroup") - proto.RegisterEnum("admiral.global.v1alpha.TrafficPolicy_LbType", TrafficPolicy_LbType_name, TrafficPolicy_LbType_value) } -func init() { proto.RegisterFile("globalrouting.proto", fileDescriptor_globalrouting_80c97d19fb14f2ca) } +func init() { proto.RegisterFile("globalrouting.proto", fileDescriptor_a5c0dc509add6f4f) } -var fileDescriptor_globalrouting_80c97d19fb14f2ca = []byte{ +var fileDescriptor_a5c0dc509add6f4f = []byte{ // 323 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xdf, 0x4a, 0xc3, 0x30, 0x14, 0xc6, 0xed, 0xca, 0xea, 0x3c, 0x6e, 0x52, 0x32, 0x19, 0xc5, 0x2b, 0x29, 0x13, 0x76, 0x21, diff --git a/admiral/pkg/apis/admiral/model/globalrouting.proto b/admiral/pkg/apis/admiral/model/globalrouting.proto index 794408ef..52e1331a 100644 --- a/admiral/pkg/apis/admiral/model/globalrouting.proto +++ b/admiral/pkg/apis/admiral/model/globalrouting.proto @@ -8,7 +8,7 @@ option go_package = "model"; // ``` -// apiVersion: navarch.global.traffic/v1alpha +// apiVersion: admiral.io/v1alpha1 // kind: GlobalTrafficPolicy // metadata: // name: my-routing diff --git a/admiral/pkg/apis/admiral/v1/zz_generated.deepcopy.go b/admiral/pkg/apis/admiral/v1/zz_generated.deepcopy.go index e7b2c558..c7b0780c 100644 --- a/admiral/pkg/apis/admiral/v1/zz_generated.deepcopy.go +++ b/admiral/pkg/apis/admiral/v1/zz_generated.deepcopy.go @@ -56,7 +56,7 @@ func (in *Dependency) DeepCopyObject() runtime.Object { func (in *DependencyList) DeepCopyInto(out *DependencyList) { *out = *in out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items *out = make([]Dependency, len(*in)) @@ -133,7 +133,7 @@ func (in *GlobalTrafficPolicy) DeepCopyObject() runtime.Object { func (in *GlobalTrafficPolicyList) DeepCopyInto(out *GlobalTrafficPolicyList) { *out = *in out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items *out = make([]GlobalTrafficPolicy, len(*in)) diff --git a/admiral/pkg/client/clientset/versioned/typed/admiral/v1/admiral_client.go b/admiral/pkg/client/clientset/versioned/typed/admiral/v1/admiral_client.go index f5e22a82..465c4c33 100644 --- a/admiral/pkg/client/clientset/versioned/typed/admiral/v1/admiral_client.go +++ b/admiral/pkg/client/clientset/versioned/typed/admiral/v1/admiral_client.go @@ -21,7 +21,6 @@ package v1 import ( v1 "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" "github.com/istio-ecosystem/admiral/admiral/pkg/client/clientset/versioned/scheme" - "k8s.io/apimachinery/pkg/runtime/serializer" rest "k8s.io/client-go/rest" ) @@ -76,7 +75,7 @@ func setConfigDefaults(config *rest.Config) error { gv := v1.SchemeGroupVersion config.GroupVersion = &gv config.APIPath = "/apis" - config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() if config.UserAgent == "" { config.UserAgent = rest.DefaultKubernetesUserAgent() diff --git a/admiral/pkg/client/clientset/versioned/typed/admiral/v1/fake/fake_dependency.go b/admiral/pkg/client/clientset/versioned/typed/admiral/v1/fake/fake_dependency.go index 987aca6f..b1da8fc9 100644 --- a/admiral/pkg/client/clientset/versioned/typed/admiral/v1/fake/fake_dependency.go +++ b/admiral/pkg/client/clientset/versioned/typed/admiral/v1/fake/fake_dependency.go @@ -131,7 +131,7 @@ func (c *FakeDependencies) DeleteCollection(options *v1.DeleteOptions, listOptio // Patch applies the patch and returns the patched dependency. func (c *FakeDependencies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *admiralv1.Dependency, err error) { obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(dependenciesResource, c.ns, name, data, subresources...), &admiralv1.Dependency{}) + Invokes(testing.NewPatchSubresourceAction(dependenciesResource, c.ns, name, pt, data, subresources...), &admiralv1.Dependency{}) if obj == nil { return nil, err diff --git a/admiral/pkg/client/clientset/versioned/typed/admiral/v1/fake/fake_globaltrafficpolicy.go b/admiral/pkg/client/clientset/versioned/typed/admiral/v1/fake/fake_globaltrafficpolicy.go index a2e94d37..f70dd556 100644 --- a/admiral/pkg/client/clientset/versioned/typed/admiral/v1/fake/fake_globaltrafficpolicy.go +++ b/admiral/pkg/client/clientset/versioned/typed/admiral/v1/fake/fake_globaltrafficpolicy.go @@ -131,7 +131,7 @@ func (c *FakeGlobalTrafficPolicies) DeleteCollection(options *v1.DeleteOptions, // Patch applies the patch and returns the patched globalTrafficPolicy. func (c *FakeGlobalTrafficPolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *admiralv1.GlobalTrafficPolicy, err error) { obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(globaltrafficpoliciesResource, c.ns, name, data, subresources...), &admiralv1.GlobalTrafficPolicy{}) + Invokes(testing.NewPatchSubresourceAction(globaltrafficpoliciesResource, c.ns, name, pt, data, subresources...), &admiralv1.GlobalTrafficPolicy{}) if obj == nil { return nil, err diff --git a/admiral/pkg/clusters/handler.go b/admiral/pkg/clusters/handler.go new file mode 100644 index 00000000..6a4ce38b --- /dev/null +++ b/admiral/pkg/clusters/handler.go @@ -0,0 +1,569 @@ +package clusters + +import ( + "fmt" + "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" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/util" + log "github.com/sirupsen/logrus" + v1alpha32 "istio.io/api/networking/v1alpha3" + "istio.io/client-go/pkg/apis/networking/v1alpha3" + v12 "k8s.io/apimachinery/pkg/apis/meta/v1" + "strings" + + k8sAppsV1 "k8s.io/api/apps/v1" + k8sV1 "k8s.io/api/core/v1" + +) + +type ServiceEntryHandler struct { + RemoteRegistry *RemoteRegistry + ClusterID string +} + +type DestinationRuleHandler struct { + RemoteRegistry *RemoteRegistry + ClusterID string +} + +type VirtualServiceHandler struct { + RemoteRegistry *RemoteRegistry + ClusterID string +} + +func updateIdentityDependencyCache(sourceIdentity string, identityDependencyCache *common.MapOfMaps, dr *v1.Dependency) { + for _, dIdentity := range dr.Spec.Destinations { + identityDependencyCache.Put(dIdentity, sourceIdentity, sourceIdentity) + } + log.Infof(LogFormat, "Update", "dependency-cache", dr.Name, "", "Updated=true namespace="+dr.Namespace) +} + +func handleDependencyRecord(sourceIdentity string, r *RemoteRegistry, rcs map[string]*RemoteController, config AdmiralParams, obj *v1.Dependency) { + + destinationIdentitys := obj.Spec.Destinations + + destinationClusters := make(map[string]string) + + sourceClusters := make(map[string]string) + + var serviceEntries = make(map[string]*v1alpha32.ServiceEntry) + + for _, rc := range rcs { + + //for every cluster the source identity is running, add their istio ingress as service entry address + tempDeployment := rc.DeploymentController.Cache.Get(sourceIdentity) + if tempDeployment != nil { + sourceClusters[rc.ClusterID] = rc.ClusterID + r.AdmiralCache.IdentityClusterCache.Put(sourceIdentity, rc.ClusterID, rc.ClusterID) + } + + //create and store destination service entries + for _, destinationCluster := range destinationIdentitys { + destDeployment := rc.DeploymentController.Cache.Get(destinationCluster) + if destDeployment == nil { + continue + } + + //deployment can be in multiple clusters, create SEs for all clusters + + for _, deployment := range destDeployment.Deployments { + + r.AdmiralCache.IdentityClusterCache.Put(destinationCluster, rc.ClusterID, rc.ClusterID) + + deployments := rc.DeploymentController.Cache.Get(destinationCluster) + + if deployments == nil || len(deployments.Deployments) == 0 { + continue + } + //TODO pass deployment + tmpSe := createServiceEntry(rc, config, r.AdmiralCache, deployment[0], serviceEntries) + + if tmpSe == nil { + continue + } + + destinationClusters[rc.ClusterID] = tmpSe.Hosts[0] //Only single host supported + + r.AdmiralCache.CnameIdentityCache.Store(tmpSe.Hosts[0], destinationCluster) + + serviceEntries[tmpSe.Hosts[0]] = tmpSe + } + + } + } + + if len(sourceClusters) == 0 || len(serviceEntries) == 0 { + log.Infof(LogFormat, "Event", "dependency-record", sourceIdentity, "", "skipped") + return + } + + for dCluster, globalFqdn := range destinationClusters { + for _, sCluster := range sourceClusters { + r.AdmiralCache.CnameClusterCache.Put(globalFqdn, dCluster, dCluster) + r.AdmiralCache.CnameDependentClusterCache.Put(globalFqdn, sCluster, sCluster) + //filter out the source clusters same as destinationClusters + delete(sourceClusters, dCluster) + } + } + + //add service entries for all dependencies in source cluster + AddServiceEntriesWithDr(r, sourceClusters, rcs, serviceEntries, config.SyncNamespace) +} + +func getIstioResourceName(host string, suffix string) string { + return strings.ToLower(host) + suffix +} + +//TODO use selector on pod, instead of hardcoded identityId +func getMatchingGlobalTrafficPolicy(rc *RemoteController, identityId string) *v1.GlobalTrafficPolicy { + return rc.GlobalTraffic.Cache.Get(identityId) +} + +func makeVirtualService(host string, destination string, port uint32) *v1alpha32.VirtualService { + return &v1alpha32.VirtualService{Hosts: []string{host}, + Gateways: []string{common.MulticlusterIngressGateway}, + ExportTo: []string{"*"}, + Http: []*v1alpha32.HTTPRoute{{Route: []*v1alpha32.HTTPRouteDestination{{Destination: &v1alpha32.Destination{Host: destination, Port: &v1alpha32.PortSelector{Number: port}}}}}}} +} + +func getDestinationRule(host string, locality string, gtpWrapper *v1.GlobalTrafficPolicy) *v1alpha32.DestinationRule { + var dr = &v1alpha32.DestinationRule{} + dr.Host = host + dr.TrafficPolicy = &v1alpha32.TrafficPolicy{Tls: &v1alpha32.TLSSettings{Mode: v1alpha32.TLSSettings_ISTIO_MUTUAL}} + if gtpWrapper != nil { + var loadBalancerSettings = &v1alpha32.LoadBalancerSettings{ + LbPolicy: &v1alpha32.LoadBalancerSettings_Simple{Simple: v1alpha32.LoadBalancerSettings_ROUND_ROBIN}, + } + gtp := gtpWrapper.Spec + gtpTrafficPolicy := gtp.Policy[0] + if len(gtpTrafficPolicy.Target) > 0 { + var localityLbSettings = &v1alpha32.LocalityLoadBalancerSetting{} + if gtpTrafficPolicy.LbType == model.TrafficPolicy_FAILOVER { + distribute := make([]*v1alpha32.LocalityLoadBalancerSetting_Distribute, 0) + targetTrafficMap := make(map[string]uint32) + for _, tg := range gtpTrafficPolicy.Target { + targetTrafficMap[tg.Region] = uint32(tg.Weight) + } + distribute = append(distribute, &v1alpha32.LocalityLoadBalancerSetting_Distribute{ + From: locality + "/*", + To: targetTrafficMap, + }) + localityLbSettings.Distribute = distribute + } else { + //this will have default behavior + } + loadBalancerSettings.LocalityLbSetting = localityLbSettings + dr.TrafficPolicy.LoadBalancer = loadBalancerSettings + dr.TrafficPolicy.OutlierDetection = &v1alpha32.OutlierDetection{ + BaseEjectionTime: &types.Duration{Seconds: 120}, + ConsecutiveErrors: 10, + Interval: &types.Duration{Seconds: 60}, + } + } + } + return dr +} + +func (ic *ServiceEntryHandler) Added(obj *v1alpha3.ServiceEntry) { + //log.Infof("New Pod %s on cluster: %s in namespace: %s", obj.Name, obj.ClusterName, obj.Namespace) +} + +func (ic *ServiceEntryHandler) Updated(obj *v1alpha3.ServiceEntry) { + // log.Infof("Pod deleted %s on cluster: %s in namespace: %s", obj.Name, obj.ClusterName, obj.Namespace) +} + +func (ic *ServiceEntryHandler) Deleted(obj *v1alpha3.ServiceEntry) { + // log.Infof("Pod deleted %s on cluster: %s in namespace: %s", obj.Name, obj.ClusterName, obj.Namespace) +} + +func (dh *DestinationRuleHandler) Added(obj *v1alpha3.DestinationRule) { + handleDestinationRuleEvent(obj, dh, common.Add, common.DestinationRule) +} + +func (dh *DestinationRuleHandler) Updated(obj *v1alpha3.DestinationRule) { + handleDestinationRuleEvent(obj, dh, common.Update, common.DestinationRule) +} + +func (dh *DestinationRuleHandler) Deleted(obj *v1alpha3.DestinationRule) { + handleDestinationRuleEvent(obj, dh, common.Delete, common.DestinationRule) +} + +func (vh *VirtualServiceHandler) Added(obj *v1alpha3.VirtualService) { + handleVirtualServiceEvent(obj, vh, common.Add, common.VirtualService) +} + +func (vh *VirtualServiceHandler) Updated(obj *v1alpha3.VirtualService) { + handleVirtualServiceEvent(obj, vh, common.Update, common.VirtualService) +} + +func (vh *VirtualServiceHandler) Deleted(obj *v1alpha3.VirtualService) { + handleVirtualServiceEvent(obj, vh, common.Delete, common.VirtualService) +} + +func handleDestinationRuleEvent(obj *v1alpha3.DestinationRule, dh *DestinationRuleHandler, event common.Event, resourceType common.ResourceType) { + destinationRule := obj.Spec + + clusterId := dh.ClusterID + + localDrName := obj.Name + "-local" + + var localIdentityId string + + syncNamespace := dh.RemoteRegistry.config.SyncNamespace + + r := dh.RemoteRegistry + + if obj.Namespace == syncNamespace || obj.Namespace == common.NamespaceKubeSystem { + log.Infof(LogFormat, "Event", "DestinationRule", obj.Name, clusterId, "Skipping the namespace: "+obj.Namespace) + return + } + + dependentClusters := r.AdmiralCache.CnameDependentClusterCache.Get(destinationRule.Host) + + if dependentClusters == nil { + log.Infof("Skipping event: %s from cluster %s for %v", "DestinationRule", clusterId, destinationRule) + log.Infof(LogFormat, "Event", "DestinationRule", obj.Name, clusterId, "No dependent clusters found") + return + } + + log.Infof(LogFormat, "Event", "DestinationRule", obj.Name, clusterId, "Processing") + + //Create label based service entry in source and dependent clusters for subset routing to work + host := destinationRule.Host + + basicSEName := getIstioResourceName(host, "-se") + + seName := getIstioResourceName(obj.Name, "-se") + + allDependentClusters := make(map[string]string) + + util.MapCopy(allDependentClusters, dependentClusters.Map()) + + allDependentClusters[clusterId] = clusterId + + for _, dependentCluster := range allDependentClusters { + + rc := r.remoteControllers[dependentCluster] + + var newServiceEntry *v1alpha3.ServiceEntry + + var existsServiceEntry *v1alpha3.ServiceEntry + + var drServiceEntries = make(map[string]*v1alpha32.ServiceEntry) + + exist, err := rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(r.config.SyncNamespace).Get(basicSEName, v12.GetOptions{}) + + var identityId = "" + + if exist == nil || err != nil { + + log.Warnf(LogFormat, "Find", "ServiceEntry", basicSEName, dependentCluster, "Failed") + + } else { + + serviceEntry := exist.Spec + + identityRaw, ok := r.AdmiralCache.CnameIdentityCache.Load(serviceEntry.Hosts[0]) + + if ok { + identityId = fmt.Sprintf("%v", identityRaw) + if dependentCluster == clusterId { + localIdentityId = identityId + } + drServiceEntries = createSeWithDrLabels(rc, dependentCluster == clusterId, identityId, seName, &serviceEntry, &destinationRule, r.AdmiralCache.ServiceEntryAddressStore, r.AdmiralCache.ConfigMapController) + } + + } + + if event == common.Delete { + + rc.DestinationRuleController.IstioClient.NetworkingV1alpha3().DestinationRules(r.config.SyncNamespace).Delete(obj.Name, &v12.DeleteOptions{}) + log.Infof(LogFormat, "Delete", "DestinationRule", obj.Name, clusterId, "success") + rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(r.config.SyncNamespace).Delete(seName, &v12.DeleteOptions{}) + log.Infof(LogFormat, "Delete", "ServiceEntry", seName, clusterId, "success") + for _, subset := range destinationRule.Subsets { + sseName := seName + common.Dash + subset.Name + rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(r.config.SyncNamespace).Delete(sseName, &v12.DeleteOptions{}) + log.Infof(LogFormat, "Delete", "ServiceEntry", sseName, clusterId, "success") + } + rc.DestinationRuleController.IstioClient.NetworkingV1alpha3().DestinationRules(r.config.SyncNamespace).Delete(localDrName, &v12.DeleteOptions{}) + log.Infof(LogFormat, "Delete", "DestinationRule", localDrName, clusterId, "success") + + } else { + + exist, _ := rc.DestinationRuleController.IstioClient.NetworkingV1alpha3().DestinationRules(r.config.SyncNamespace).Get(obj.Name, v12.GetOptions{}) + + //copy destination rule only to other clusters + if dependentCluster != clusterId { + addUpdateDestinationRule(obj, exist, r.config.SyncNamespace, rc) + } + + if drServiceEntries != nil { + for _seName, se := range drServiceEntries { + existsServiceEntry, _ = rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(r.config.SyncNamespace).Get(_seName, v12.GetOptions{}) + newServiceEntry = createServiceEntrySkeletion(*se, _seName, r.config.SyncNamespace) + if err != nil { + log.Warnf(LogErrFormat, "Create", "ServiceEntry", seName, clusterId, err) + } + if newServiceEntry != nil { + addUpdateServiceEntry(newServiceEntry, existsServiceEntry, r.config.SyncNamespace, rc) + } + //cache the subset service entries for updating them later for pod events + if dependentCluster == clusterId && se.Resolution == v1alpha32.ServiceEntry_STATIC { + r.AdmiralCache.SubsetServiceEntryIdentityCache.Store(identityId, map[string]string{_seName: clusterId}) + } + } + } + + if dependentCluster == clusterId { + //we need a destination rule with local fqdn for destination rules created with cnames to work in local cluster + createDestinationRuleForLocal(rc, localDrName, localIdentityId, clusterId, &destinationRule, r.config.SyncNamespace, r.config.HostnameSuffix, r.config.LabelSet.WorkloadIdentityLabel) + } + + } + } +} + +func createDestinationRuleForLocal(remoteController *RemoteController, localDrName string, identityId string, clusterId string, + destinationRule *v1alpha32.DestinationRule, syncNamespace string, nameSuffix string, identifier string) { + + deployment := remoteController.DeploymentController.Cache.Get(identityId) + + if deployment == nil || len(deployment.Deployments) == 0 { + log.Errorf(LogFormat, "Find", "deployment", identityId, remoteController.ClusterID, "Couldn't find deployment with identity") + return + } + + //TODO this will pull a random deployment from some cluster which might not be the right deployment + var deploymentInstance *k8sAppsV1.Deployment + for _, value := range deployment.Deployments { + deploymentInstance = value[0] + break + } + + serviceInstance := getServiceForDeployment(remoteController, deploymentInstance) + + cname := common.GetCname(deploymentInstance, identifier, nameSuffix) + if cname == destinationRule.Host { + destinationRule.Host = serviceInstance.Name + common.Sep + serviceInstance.Namespace + common.DotLocalDomainSuffix + existsDestinationRule, err := remoteController.DestinationRuleController.IstioClient.NetworkingV1alpha3().DestinationRules(syncNamespace).Get(localDrName, v12.GetOptions{}) + if err != nil { + log.Warnf(LogErrFormat, "Find", "DestinationRule", localDrName, clusterId, err) + } + newDestinationRule := createDestinationRulSkeletion(*destinationRule, localDrName, syncNamespace) + + + if newDestinationRule != nil { + addUpdateDestinationRule(newDestinationRule, existsDestinationRule, syncNamespace, remoteController) + } + } +} + +func handleVirtualServiceEvent(obj *v1alpha3.VirtualService, vh *VirtualServiceHandler, event common.Event, resourceType common.ResourceType) { + + log.Infof(LogFormat, "Event", resourceType, obj.Name, vh.ClusterID, "Received event") + + virtualService := obj.Spec + + clusterId := vh.ClusterID + + r := vh.RemoteRegistry + + if obj.Namespace == r.config.SyncNamespace { + log.Infof(LogFormat, "Event", resourceType, obj.Name, clusterId, "Skipping the namespace: "+obj.Namespace) + return + } + + if len(virtualService.Hosts) > 1 { + log.Errorf(LogFormat, "Event", resourceType, obj.Name, clusterId, "Skipping as multiple hosts not supported for virtual service namespace="+obj.Namespace) + return + } + + dependentClusters := r.AdmiralCache.CnameDependentClusterCache.Get(virtualService.Hosts[0]) + + if dependentClusters == nil { + log.Infof(LogFormat, "Event", resourceType, obj.Name, clusterId, "No dependent clusters found") + return + } + + log.Infof(LogFormat, "Event", "VirtualService", obj.Name, clusterId, "Processing") + + for _, dependentCluster := range dependentClusters.Map() { + + rc := r.remoteControllers[dependentCluster] + + if clusterId != dependentCluster { + + if event == common.Delete { + log.Infof(LogFormat, "Delete", "VirtualService", obj.Name, clusterId, "Success") + rc.VirtualServiceController.IstioClient.NetworkingV1alpha3().VirtualServices(r.config.SyncNamespace).Delete(obj.Name, &v12.DeleteOptions{}) + + } else { + + exist, _ := rc.VirtualServiceController.IstioClient.NetworkingV1alpha3().VirtualServices(r.config.SyncNamespace).Get(obj.Name, v12.GetOptions{}) + + //change destination host for all http routes .. to same as host on the virtual service + for _, httpRoute := range virtualService.Http { + for _, destination := range httpRoute.Route { + //get at index 0, we do not support wildcards or multiple hosts currently + destination.Destination.Host = virtualService.Hosts[0] + } + } + + for _, tlsRoute := range virtualService.Tls { + for _, destination := range tlsRoute.Route { + //get at index 0, we do not support wildcards or multiple hosts currently + destination.Destination.Host = virtualService.Hosts[0] + } + } + + addUpdateVirtualService(obj, exist, vh.RemoteRegistry.config.SyncNamespace, rc) + } + } + + } +} + +func addUpdateVirtualService(obj *v1alpha3.VirtualService, exist *v1alpha3.VirtualService, namespace string, rc *RemoteController) { + var err error + var op string + if exist == nil { + obj.Namespace = namespace + obj.ResourceVersion = "" + _, err = rc.VirtualServiceController.IstioClient.NetworkingV1alpha3().VirtualServices(namespace).Create(obj) + op = "Add" + } else { + exist.Labels = obj.Labels + exist.Annotations = obj.Annotations + exist.Spec = obj.Spec + op = "Update" + _, err = rc.VirtualServiceController.IstioClient.NetworkingV1alpha3().VirtualServices(namespace).Update(exist) + } + + if err != nil { + log.Infof(LogErrFormat, op, "VirtualService", obj.Name, rc.ClusterID, err) + } else { + log.Infof(LogErrFormat, op, "VirtualService", obj.Name, rc.ClusterID, "Success") + } +} + +func addUpdateServiceEntry(obj *v1alpha3.ServiceEntry, exist *v1alpha3.ServiceEntry, namespace string, rc *RemoteController) { + var err error + var op string + if exist == nil { + obj.Namespace = namespace + obj.ResourceVersion = "" + _, err = rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(namespace).Create(obj) + op = "Add" + } else { + exist.Labels = obj.Labels + exist.Annotations = obj.Annotations + exist.Spec = obj.Spec + op = "Update" + _, err = rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(namespace).Update(exist) + } + + if err != nil { + log.Infof(LogErrFormat, op, "ServiceEntry", obj.Name, rc.ClusterID, err) + } else { + log.Infof(LogErrFormat, op, "ServiceEntry", obj.Name, rc.ClusterID, "Success") + } +} + +func addUpdateDestinationRule(obj *v1alpha3.DestinationRule, exist *v1alpha3.DestinationRule, namespace string, rc *RemoteController) { + var err error + var op string + if exist == nil { + obj.Namespace = namespace + obj.ResourceVersion = "" + _, err = rc.DestinationRuleController.IstioClient.NetworkingV1alpha3().DestinationRules(namespace).Create(obj) + op = "Add" + } else { + exist.Labels = obj.Labels + exist.Annotations = obj.Annotations + exist.Spec = obj.Spec + op = "Update" + _, err = rc.DestinationRuleController.IstioClient.NetworkingV1alpha3().DestinationRules(namespace).Update(exist) + } + + if err != nil { + log.Infof(LogErrFormat, op, "DestinationRule", obj.Name, rc.ClusterID, err) + } else { + log.Infof(LogErrFormat, op, "DestinationRule", obj.Name, rc.ClusterID, "Success") + } +} + +func createVirtualServiceSkeletion(vs v1alpha32.VirtualService, name string, namespace string) *v1alpha3.VirtualService { + return &v1alpha3.VirtualService{Spec:vs, ObjectMeta: v12.ObjectMeta{Name:name, Namespace: namespace}} +} + +func createServiceEntrySkeletion(se v1alpha32.ServiceEntry, name string, namespace string) *v1alpha3.ServiceEntry { + return &v1alpha3.ServiceEntry{Spec:se, ObjectMeta: v12.ObjectMeta{Name:name, Namespace: namespace}} +} + +func createDestinationRulSkeletion(dr v1alpha32.DestinationRule, name string, namespace string) *v1alpha3.DestinationRule { + return &v1alpha3.DestinationRule{Spec:dr, ObjectMeta: v12.ObjectMeta{Name:name, Namespace: namespace}} +} + +func getServiceForDeployment(rc *RemoteController, deployment *k8sAppsV1.Deployment) *k8sV1.Service { + + if deployment == nil { + return nil + } + + cachedService := rc.ServiceController.Cache.Get(deployment.Namespace) + + if cachedService == nil { + return nil + } + var matchedService *k8sV1.Service + for _, service := range cachedService.Service[deployment.Namespace] { + var match = true + for lkey, lvalue := range service.Spec.Selector { + value, ok := deployment.Spec.Selector.MatchLabels[lkey] + if !ok || value != lvalue { + match = false + break + } + } + //make sure the service matches the deployment Selector and also has a mesh port in the port spec + if match { + ports := GetMeshPorts(rc.ClusterID, service, deployment) + if len(ports) > 0 { + matchedService = service + break + } + } + } + return matchedService +} + +func getDependentClusters(dependents *common.Map, 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 + } + } + } + } + } + } + return dependentClusters +} + +func copyEndpoint(e *v1alpha32.ServiceEntry_Endpoint) *v1alpha32.ServiceEntry_Endpoint { + labels := make(map[string]string) + util.MapCopy(labels, e.Labels) + ports := make(map[string]uint32) + util.MapCopy(ports, e.Ports) + return &v1alpha32.ServiceEntry_Endpoint{Address: e.Address, Ports: ports, Locality: e.Locality, Labels: labels} +} \ No newline at end of file diff --git a/admiral/pkg/clusters/registry.go b/admiral/pkg/clusters/registry.go index 9685bfbc..749be19e 100644 --- a/admiral/pkg/clusters/registry.go +++ b/admiral/pkg/clusters/registry.go @@ -2,30 +2,17 @@ package clusters import ( "context" - "github.com/gogo/protobuf/proto" - "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" + "fmt" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/istio" + "sync" + "time" + "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/istio" "github.com/istio-ecosystem/admiral/admiral/pkg/controller/secret" - "github.com/istio-ecosystem/admiral/admiral/pkg/controller/util" + log "github.com/sirupsen/logrus" "k8s.io/client-go/rest" - "strings" - "time" - "fmt" - - "istio.io/istio/pkg/log" - "sync" - - istioModel "istio.io/istio/pilot/pkg/model" - k8sAppsV1 "k8s.io/api/apps/v1" - k8sV1 "k8s.io/api/core/v1" - - networking "istio.io/api/networking/v1alpha3" - istioKube "istio.io/istio/pilot/pkg/serviceregistry/kube" - - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const ( @@ -33,160 +20,6 @@ const ( LogErrFormat = "op=%s type=%v name=%v cluster=%s, e=%v" ) -func updateIdentityDependencyCache(sourceIdentity string, identityDependencyCache *common.MapOfMaps, dr *v1.Dependency) { - for _, dIdentity := range dr.Spec.Destinations { - identityDependencyCache.Put(dIdentity, sourceIdentity, sourceIdentity) - } - log.Infof(LogFormat, "Update", "dependency-cache", dr.Name, "", "Updated=true namespace="+dr.Namespace) -} - -func handleDependencyRecord(identifier string, sourceIdentity string, admiralCache *AdmiralCache, rcs map[string]*RemoteController, config AdmiralParams, obj *v1.Dependency) { - - destinationIdentitys := obj.Spec.Destinations - - destinationClusters := make(map[string]string) - - sourceClusters := make(map[string]string) - - var serviceEntries = make(map[string]*networking.ServiceEntry) - - for _, rc := range rcs { - - //for every cluster the source identity is running, add their istio ingress as service entry address - tempDeployment := rc.DeploymentController.Cache.Get(sourceIdentity) - if tempDeployment != nil { - sourceClusters[rc.ClusterID] = rc.ClusterID - admiralCache.IdentityClusterCache.Put(sourceIdentity, rc.ClusterID, rc.ClusterID) - } - - //create and store destination service entries - for _, destinationCluster := range destinationIdentitys { - destDeployment := rc.DeploymentController.Cache.Get(destinationCluster) - if destDeployment == nil { - continue - } - - //deployment can be in multiple clusters, create SEs for all clusters - - for _, deployment := range destDeployment.Deployments { - - admiralCache.IdentityClusterCache.Put(destinationCluster, rc.ClusterID, rc.ClusterID) - - deployments := rc.DeploymentController.Cache.Get(destinationCluster) - - if deployments == nil || len(deployments.Deployments) == 0 { - continue - } - //TODO pass deployment - - tmpSe := createServiceEntry(rc, config, admiralCache, deployment[0], serviceEntries) - - if tmpSe == nil { - continue - } - - destinationClusters[rc.ClusterID] = tmpSe.Hosts[0] //Only single host supported - - admiralCache.CnameIdentityCache.Store(tmpSe.Hosts[0], destinationCluster) - - serviceEntries[tmpSe.Hosts[0]] = tmpSe - } - - } - } - - if len(sourceClusters) == 0 || len(serviceEntries) == 0 { - log.Infof(LogFormat, "Event", "dependency-record", sourceIdentity, "", "skipped") - return - } - - for dCluster, globalFqdn := range destinationClusters { - for _, sCluster := range sourceClusters { - admiralCache.CnameClusterCache.Put(globalFqdn, dCluster, dCluster) - admiralCache.CnameDependentClusterCache.Put(globalFqdn, sCluster, sCluster) - //filter out the source clusters same as destinationClusters - delete(sourceClusters, dCluster) - } - } - - //add service entries for all dependencies in source cluster - AddServiceEntriesWithDr(admiralCache, sourceClusters, rcs, serviceEntries, config.SyncNamespace) -} - -func getIstioResourceName(host string, suffix string) string { - return strings.ToLower(host) + suffix -} - - -func createIstioConfig(schema istio.ProtoSchema, object proto.Message, name string, namespace string) (*istio.Config, error) { - return istio.ConvertIstioType(schema, object, name, namespace) -} - -func makeVirtualService(host string, destination string, port uint32) *networking.VirtualService { - return &networking.VirtualService{Hosts: []string{host}, - Gateways: []string{common.Mesh, common.MulticlusterIngressGateway}, - ExportTo: []string{"*"}, - Http: []*networking.HTTPRoute{{Route: []*networking.HTTPRouteDestination{{Destination: &networking.Destination{Host: destination, Port: &networking.PortSelector{Port: &networking.PortSelector_Number{Number: port}}}}}}}} -} - - -func getDestinationRule(host string) *networking.DestinationRule { - return &networking.DestinationRule{Host: host, - TrafficPolicy: &networking.TrafficPolicy{Tls: &networking.TLSSettings{Mode: networking.TLSSettings_ISTIO_MUTUAL}}} -} - -func getServiceForDeployment(rc *RemoteController, deployment *k8sAppsV1.Deployment) *k8sV1.Service { - - if deployment == nil { - return nil - } - cachedService := rc.ServiceController.Cache.Get(deployment.Namespace) - - if cachedService == nil { - return nil - } - var matchedService *k8sV1.Service - for _, service := range cachedService.Service[deployment.Namespace] { - var match = true - for lkey, lvalue := range service.Spec.Selector { - value, ok := deployment.Spec.Selector.MatchLabels[lkey] - if !ok || value != lvalue { - match = false - break - } - } - //make sure the service matches the deployment Selector and also has a mesh port in the port spec - if match { - ports := GetMeshPorts(rc.ClusterID, service, deployment) - if len(ports) > 0 { - matchedService = service - break - } - } - } - return matchedService -} - -func getDependentClusters(dependents *common.Map, 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 - } - } - } - } - } - } - return dependentClusters -} - func InitAdmiral(ctx context.Context, params AdmiralParams) (*RemoteRegistry, error) { log.Infof("Initializing Admiral with params: %v", params) @@ -216,7 +49,7 @@ func InitAdmiral(ctx context.Context, params AdmiralParams) (*RemoteRegistry, er IdentityDependencyCache: common.NewMapOfMaps(), CnameIdentityCache: &sync.Map{}, SubsetServiceEntryIdentityCache: &sync.Map{}, - ServiceEntryAddressStore: &ServiceEntryAddressStore{EntryAddresses: map[string]string{}, Addresses: []string{},}} + ServiceEntryAddressStore: &ServiceEntryAddressStore{EntryAddresses: map[string]string{}, Addresses: []string{}}} configMapController, err := admiral.NewConfigMapController(w.config.KubeconfigPath, w.config.SyncNamespace) if err != nil { @@ -258,12 +91,6 @@ func createSecretController(ctx context.Context, w *RemoteRegistry, params Admir func (r *RemoteRegistry) createCacheController(clientConfig *rest.Config, clusterID string, resyncPeriod time.Duration) error { - opts := istioKube.ControllerOptions{ - WatchedNamespace: meta_v1.NamespaceAll, - ResyncPeriod: resyncPeriod, - DomainSuffix: "cluster.local", - } - stop := make(chan struct{}) rc := RemoteController{ @@ -271,13 +98,9 @@ func (r *RemoteRegistry) createCacheController(clientConfig *rest.Config, cluste ClusterID: clusterID, } - err := r.createIstioController(clientConfig, opts, &rc, stop, clusterID) - - if err != nil { - return fmt.Errorf(" Error with Istio controller init: %v", err) - } + var err error - log.Infof("starting global traffic policy controller clusterID: %v", clusterID) + log.Infof("starting global traffic policy controller custerID: %v", clusterID) rc.GlobalTraffic, err = admiral.NewGlobalTrafficController(stop, &GlobalTrafficHandler{RemoteRegistry: r}, clientConfig, resyncPeriod) if err != nil { @@ -312,286 +135,34 @@ func (r *RemoteRegistry) createCacheController(clientConfig *rest.Config, cluste return fmt.Errorf(" Error with ServiceController controller init: %v", err) } - r.Lock() - defer r.Unlock() - r.remoteControllers[clusterID] = &rc - - log.Infof("Create Controller %s", clusterID) - - return nil -} + log.Infof("starting service entry controller for custerID: %v", clusterID) + rc.ServiceEntryController, err = istio.NewServiceEntryController(stop, &ServiceEntryHandler{RemoteRegistry: r, ClusterID:clusterID}, clientConfig, resyncPeriod) -func (r *RemoteRegistry) createIstioController(clientConfig *rest.Config, opts istioKube.ControllerOptions, remoteController *RemoteController, stop <-chan struct{}, clusterID string) error { - configClient, err := istio.NewClient(clientConfig, "", istio.IstioConfigTypes, opts.DomainSuffix) if err != nil { - return fmt.Errorf("error creating istio client to the remote clusterId: %s error:%v", clusterID, err) + return fmt.Errorf(" Error with ServiceEntryController init: %v", err) } - configStore := istio.NewController(configClient, opts) - configStore.RegisterEventHandler(istioModel.VirtualService.Type, func(m istio.Config, e istio.Event) { - virtualService := m.Spec.(*networking.VirtualService) + log.Infof("starting destination rule controller for custerID: %v", clusterID) + rc.DestinationRuleController, err = istio.NewDestinationRuleController(stop, &DestinationRuleHandler{RemoteRegistry: r, ClusterID:clusterID}, clientConfig, resyncPeriod) - clusterId := remoteController.ClusterID - - if m.Namespace == r.config.SyncNamespace { - log.Infof(LogFormat, "Event", e.String(), m.Name, clusterId, "Skipping the namespace: "+m.Namespace) - return - } - - if len(virtualService.Hosts) > 1 { - log.Errorf(LogFormat, "Event", e.String(), m.Name, clusterId, "Skipping as multiple hosts not supported for virtual service namespace="+m.Namespace) - return - } - - dependentClusters := r.AdmiralCache.CnameDependentClusterCache.Get(virtualService.Hosts[0]) - - if dependentClusters == nil { - log.Infof(LogFormat, "Event", e.String(), m.Name, clusterId, "No dependent clusters found") - return - } - - log.Infof(LogFormat, "Event", e.String(), m.Name, clusterId, "Processing") - - for _, dependentCluster := range dependentClusters.Map() { - - rc := r.remoteControllers[dependentCluster] - - if clusterId != dependentCluster { - - if istio.EventDelete == e { - - log.Infof(LogFormat, "Delete", istioModel.DestinationRule.Type, m.Name, clusterId, "Success") - rc.IstioConfigStore.Delete(istioModel.DestinationRule.Type, m.Name, r.config.SyncNamespace) - - } else { - - exist := rc.IstioConfigStore.Get(istioModel.VirtualService.Type, m.Name, r.config.SyncNamespace) - - //change destination host for all http routes .. to same as host on the virtual service - for _, httpRoute := range virtualService.Http { - for _, destination := range httpRoute.Route { - //get at index 0, we do not support wildcards or multiple hosts currently - destination.Destination.Host = virtualService.Hosts[0] - } - } - - for _, tlsRoute := range virtualService.Tls { - for _, destination := range tlsRoute.Route { - //get at index 0, we do not support wildcards or multiple hosts currently - destination.Destination.Host = virtualService.Hosts[0] - } - } - - addUpdateIstioResource(rc, m, exist, istioModel.VirtualService.Type, r.config.SyncNamespace) - } - } - - } - - }) - - configStore.RegisterEventHandler(istioModel.DestinationRule.Type, func(m istio.Config, e istio.Event) { - destinationRule := m.Spec.(*networking.DestinationRule) - - clusterId := remoteController.ClusterID - - localDrName := m.Name + "-local" - - var localIdentityId string - - if m.Namespace == r.config.SyncNamespace || m.Namespace == common.NamespaceKubeSystem { - log.Infof(LogFormat, "Event", e.String(), m.Name, clusterId, "Skipping the namespace: "+m.Namespace) - return - } - - dependentClusters := r.AdmiralCache.CnameDependentClusterCache.Get(destinationRule.Host) - - if dependentClusters == nil { - log.Infof("Skipping event: %s from cluster %s for %v", e.String(), clusterId, destinationRule) - log.Infof(LogFormat, "Event", e.String(), m.Name, clusterId, "No dependent clusters found") - return - } - - log.Infof(LogFormat, "Event", e.String(), m.Name, clusterId, "Processing") - - //Create label based service entry in source and dependent clusters for subset routing to work - host := destinationRule.Host - - basicSEName := getIstioResourceName(host, "-se") - - seName := getIstioResourceName(m.Name, "-se") - - allDependentClusters := make(map[string]string) - - util.MapCopy(allDependentClusters, dependentClusters.Map()) - - allDependentClusters[clusterId] = clusterId - - for _, dependentCluster := range allDependentClusters { - - rc := r.remoteControllers[dependentCluster] - - var newServiceEntry *istio.Config - - var existsServiceEntry *istio.Config - - var drServiceEntries = make(map[string]*networking.ServiceEntry) - - exist := rc.IstioConfigStore.Get(istioModel.ServiceEntry.Type, basicSEName, r.config.SyncNamespace) - - var identityId = "" - - if exist == nil { - - log.Warnf(LogFormat, "Find", istioModel.ServiceEntry.Type, basicSEName, clusterID, "Failed") - - } else { - - serviceEntry := exist.Spec.(*networking.ServiceEntry) - - identityRaw, ok := r.AdmiralCache.CnameIdentityCache.Load(serviceEntry.Hosts[0]) - - if ok { - identityId = fmt.Sprintf("%v", identityRaw) - if dependentCluster == clusterId { - localIdentityId = identityId - } - drServiceEntries = createSeWithDrLabels(remoteController, dependentCluster == clusterId, identityId, seName, serviceEntry, destinationRule, r.AdmiralCache.ServiceEntryAddressStore, r.AdmiralCache.ConfigMapController) - } - - } - - if istio.EventDelete == e { - - rc.IstioConfigStore.Delete(istioModel.DestinationRule.Type, m.Name, r.config.SyncNamespace) - log.Infof(LogFormat, "Delete", istioModel.ServiceEntry.Type, m.Name, clusterId, "success") - rc.IstioConfigStore.Delete(istioModel.ServiceEntry.Type, seName, r.config.SyncNamespace) - log.Infof(LogFormat, "Delete", istioModel.ServiceEntry.Type, seName, clusterId, "success") - for _, subset := range destinationRule.Subsets { - sseName := seName + common.Dash + subset.Name - rc.IstioConfigStore.Delete(istioModel.ServiceEntry.Type, sseName, r.config.SyncNamespace) - log.Infof(LogFormat, "Delete", istioModel.ServiceEntry.Type, sseName, clusterId, "success") - } - rc.IstioConfigStore.Delete(istioModel.DestinationRule.Type, localDrName, r.config.SyncNamespace) - log.Infof(LogFormat, "Delete", istioModel.DestinationRule.Type, localDrName, clusterId, "success") - - } else { - - exist := rc.IstioConfigStore.Get(istioModel.DestinationRule.Type, m.Name, r.config.SyncNamespace) - - //copy destination rule only to other clusters - if dependentCluster != clusterId { - addUpdateIstioResource(rc, m, exist, istioModel.DestinationRule.Type, r.config.SyncNamespace) - } - - if drServiceEntries != nil { - for _seName, se := range drServiceEntries { - existsServiceEntry = rc.IstioConfigStore.Get(istioModel.ServiceEntry.Type, _seName, r.config.SyncNamespace) - newServiceEntry, err = createIstioConfig(istio.ServiceEntryProto, se, _seName, r.config.SyncNamespace) - if err != nil { - log.Warnf(LogErrFormat, "Create", istioModel.ServiceEntry.Type, seName, clusterId, err) - } - if newServiceEntry != nil { - addUpdateIstioResource(rc, *newServiceEntry, existsServiceEntry, istioModel.ServiceEntry.Type, r.config.SyncNamespace) - } - //cache the subset service entries for updating them later for pod events - if dependentCluster == clusterId && se.Resolution == networking.ServiceEntry_STATIC { - r.AdmiralCache.SubsetServiceEntryIdentityCache.Store(identityId, map[string]string{_seName: clusterId}) - } - } - } - - if dependentCluster == clusterId { - //we need a destination rule with local fqdn for destination rules created with cnames to work in local cluster - createDestinationRuleForLocal(remoteController, localDrName, localIdentityId, clusterId, destinationRule, r.config.SyncNamespace, r.config.HostnameSuffix, r.config.LabelSet.WorkloadIdentityLabel) - } - - } - } - - }) - - remoteController.IstioConfigStore = configStore - - go configStore.Run(stop) - return nil -} - -func createDestinationRuleForLocal(remoteController *RemoteController, localDrName string, identityId string, clusterId string, - destinationRule *networking.DestinationRule, syncNamespace string, nameSuffix string, identifier string) { - - deployment := remoteController.DeploymentController.Cache.Get(identityId) - - if deployment == nil || len(deployment.Deployments) == 0 { - log.Errorf(LogFormat, "Find", "deployment", identityId, remoteController.ClusterID, "Couldn't find deployment with identity") - return - } - - //TODO this will pull a random deployment from some cluster which might not be the right deployment - var deploymentInstance *k8sAppsV1.Deployment - for _, value := range deployment.Deployments { - deploymentInstance = value[0] - break + if err != nil { + return fmt.Errorf(" Error with DestinationRuleController init: %v", err) } - serviceInstance := getServiceForDeployment(remoteController, deploymentInstance) + log.Infof("starting virtual service controller for custerID: %v", clusterID) + rc.VirtualServiceController, err = istio.NewVirtualServiceController(stop, &VirtualServiceHandler{RemoteRegistry: r, ClusterID:clusterID}, clientConfig, resyncPeriod) - cname := common.GetCname(deploymentInstance, identifier, nameSuffix) - if cname == destinationRule.Host { - destinationRule.Host = serviceInstance.Name + common.Sep + serviceInstance.Namespace + common.DotLocalDomainSuffix - existsDestinationRule := remoteController.IstioConfigStore.Get(istioModel.DestinationRule.Type, localDrName, syncNamespace) - newDestinationRule, err := createIstioConfig(istio.DestinationRuleProto, destinationRule, localDrName, syncNamespace) - - if err != nil { - log.Warnf(LogErrFormat, "Create", istioModel.DestinationRule.Type, localDrName, clusterId, err) - } - if newDestinationRule != nil { - addUpdateIstioResource(remoteController, *newDestinationRule, existsDestinationRule, istioModel.DestinationRule.Type, syncNamespace) - } + if err != nil { + return fmt.Errorf(" Error with VirtualServiceController init: %v", err) } -} -func copyEndpoint(e *networking.ServiceEntry_Endpoint) *networking.ServiceEntry_Endpoint { - labels := make(map[string]string) - util.MapCopy(labels, e.Labels) - ports := make(map[string]uint32) - util.MapCopy(ports, e.Ports) - return &networking.ServiceEntry_Endpoint{Address: e.Address, Ports: ports, Locality: e.Locality, Labels: labels} -} + r.Lock() + defer r.Unlock() + r.remoteControllers[clusterID] = &rc -func addUpdateIstioResource(rc *RemoteController, m istio.Config, exist *istio.Config, t string, syncNamespace string) { - var e error - var verb string - if exist == nil { - m.Namespace = syncNamespace - m.ResourceVersion = "" - verb = "Add" - _, e = rc.IstioConfigStore.Create(m) - - } else { - if len(exist.Labels) > 0 { - if _, ok := exist.Labels["disable-update"]; ok { - verb = "Skipped" - } - } - - if verb != "Skipped" { - exist.Labels = m.Labels - exist.Spec = m.Spec - exist.Annotations = m.Annotations - verb = "Update" - _, e = rc.IstioConfigStore.Update(*exist) - } - } + log.Infof("Create Controller %s", clusterID) - //TODO this should return an error - if e != nil { - log.Errorf(LogErrFormat, verb, t, m.Spec, rc.ClusterID, e) - } else if verb != "Skipped" { - log.Infof(LogFormat, verb, t, m.Name, rc.ClusterID, "success") - } else { - log.Infof(LogFormat, verb, t, m.Name, rc.ClusterID, "Found Skip flag") - } + return nil } func (r *RemoteRegistry) deleteCacheController(clusterID string) error { diff --git a/admiral/pkg/clusters/registry_test.go b/admiral/pkg/clusters/registry_test.go index d92357d9..705cd237 100644 --- a/admiral/pkg/clusters/registry_test.go +++ b/admiral/pkg/clusters/registry_test.go @@ -7,11 +7,9 @@ import ( "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/istio" "github.com/istio-ecosystem/admiral/admiral/pkg/test" + log "github.com/sirupsen/logrus" networking "istio.io/api/networking/v1alpha3" - istioKube "istio.io/istio/pilot/pkg/serviceregistry/kube" - "istio.io/istio/pkg/log" k8sAppsV1 "k8s.io/api/apps/v1" k8sCoreV1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -63,31 +61,6 @@ func TestDeleteCacheController(t *testing.T) { } } -func TestAddUpdateIstioResource(t *testing.T) { - - rc := RemoteController{ - IstioConfigStore: &test.MockIstioConfigStore{}, - } - - new := istio.Config{} - - existing := istio.Config{} - - configToSkipUpdate := istio.Config{ - ConfigMeta: istio.ConfigMeta{ - Labels: map[string]string{ - "disable-update": "true", - }, - }, - } - - addUpdateIstioResource(&rc, new, nil, "VirtualService", "default") - - addUpdateIstioResource(&rc, new, &existing, "VirtualService", "default") - - addUpdateIstioResource(&rc, new, &configToSkipUpdate, "VirtualService", "default") -} - func TestCopyServiceEntry(t *testing.T) { se := networking.ServiceEntry{ @@ -128,12 +101,6 @@ func TestCreateDestinationRuleForLocalNoDeployLabel(t *testing.T) { } rc := RemoteController{ - IstioConfigStore: &test.MockIstioConfigStore{ - - TestHook: func(i interface{}) { - t.Fail() - }, - }, DeploymentController: d, } @@ -152,10 +119,7 @@ func TestCreateDestinationRuleForLocal(t *testing.T) { rc, err := createMockRemoteController( func(i interface{}) { - res := i.(istio.Config) - if res.Name != "local.name" { - t.Fail() - } + }, ) @@ -218,9 +182,6 @@ func createMockRemoteController(f func(interface{})) (*RemoteController, error) s.Added(&service) rc := RemoteController{ - IstioConfigStore: &test.MockIstioConfigStore{ - TestHook: f, - }, DeploymentController: d, ServiceController: s, NodeController: n, @@ -305,31 +266,6 @@ func TestAdded(t *testing.T) { } -func TestCreateIstioController(t *testing.T) { - - p := AdmiralParams{ - KubeconfigPath: "testdata/fake.config", - } - rr, _ := InitAdmiral(context.Background(), p) - - rc, _ := createMockRemoteController(func(i interface{}) { - t.Fail() - }) - rr.remoteControllers["test.cluster"] = rc - config := rest.Config{ - Host: "localhost", - } - - opts := istioKube.ControllerOptions{ - WatchedNamespace: metav1.NamespaceAll, - ResyncPeriod: time.Second * time.Duration(300), - DomainSuffix: ".cluster", - } - - rr.createIstioController(&config, opts, rc, make(chan struct{}), "test.cluster") - -} - func TestMakeVirtualService(t *testing.T) { vs := makeVirtualService("test.local", "dest", 8080) if vs.Hosts[0] != "test.local" { @@ -349,13 +285,7 @@ func TestDeploymentHandler(t *testing.T) { rr, _ := InitAdmiral(context.Background(), p) rc, _ := createMockRemoteController(func(i interface{}) { - res := i.(istio.Config) - se, ok := res.Spec.(*networking.ServiceEntry) - if ok { - if se.Hosts[0] != "dev.bar.global" { - t.Fail() - } - } + }) rr.remoteControllers["test.cluster"] = rc @@ -394,13 +324,7 @@ func TestPodHandler(t *testing.T) { rr, _ := InitAdmiral(context.Background(), p) rc, _ := createMockRemoteController(func(i interface{}) { - res := i.(istio.Config) - se, ok := res.Spec.(*networking.ServiceEntry) - if ok { - if se.Hosts[0] != "dev.bar.global" { - t.Fail() - } - } + }) rr.remoteControllers["test.cluster"] = rc @@ -425,23 +349,23 @@ func TestPodHandler(t *testing.T) { func TestGetServiceForDeployment(t *testing.T) { baseRc, _ := createMockRemoteController(func(i interface{}) { - res := i.(istio.Config) - se, ok := res.Spec.(*networking.ServiceEntry) - if ok { - if se.Hosts[0] != "dev.bar.global" { - t.Errorf("Host mismatch. Expected dev.bar.global, got %v", se.Hosts[0]) - } - } + //res := i.(istio.Config) + //se, ok := res.Spec.(*v1alpha3.ServiceEntry) + //if ok { + // if se.Hosts[0] != "dev.bar.global" { + // t.Errorf("Host mismatch. Expected dev.bar.global, got %v", se.Hosts[0]) + // } + //} }) rcWithService, _ := createMockRemoteController(func(i interface{}) { - res := i.(istio.Config) - se, ok := res.Spec.(*networking.ServiceEntry) - if ok { - if se.Hosts[0] != "dev.bar.global" { - t.Errorf("Host mismatch. Expected dev.bar.global, got %v", se.Hosts[0]) - } - } + //res := i.(istio.Config) + //se, ok := res.Spec.(*networking.ServiceEntry) + //if ok { + // if se.Hosts[0] != "dev.bar.global" { + // t.Errorf("Host mismatch. Expected dev.bar.global, got %v", se.Hosts[0]) + // } + //} }) service := k8sCoreV1.Service{} diff --git a/admiral/pkg/clusters/serviceentry.go b/admiral/pkg/clusters/serviceentry.go index 4185d985..036ca57a 100644 --- a/admiral/pkg/clusters/serviceentry.go +++ b/admiral/pkg/clusters/serviceentry.go @@ -5,15 +5,13 @@ import ( "fmt" "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/istio" "github.com/istio-ecosystem/admiral/admiral/pkg/controller/util" - "github.com/sirupsen/logrus" + log "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" networking "istio.io/api/networking/v1alpha3" - istioModel "istio.io/istio/pilot/pkg/model" - "istio.io/istio/pkg/log" k8sAppsV1 "k8s.io/api/apps/v1" k8sV1 "k8s.io/api/core/v1" + v12 "k8s.io/apimachinery/pkg/apis/meta/v1" "math" "math/rand" "strconv" @@ -149,7 +147,7 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem remoteRegistry.AdmiralCache.CnameDependentClusterCache.Put(cname, clusterId, clusterId) } - AddServiceEntriesWithDr(remoteRegistry.AdmiralCache, dependentClusters, remoteRegistry.remoteControllers, serviceEntries, + AddServiceEntriesWithDr(remoteRegistry, dependentClusters, remoteRegistry.remoteControllers, serviceEntries, remoteRegistry.config.SyncNamespace) //update the address to local fqdn for service entry in a cluster local to the service instance @@ -165,7 +163,7 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem ep.Address = localFqdn oldPorts := ep.Ports ep.Ports = meshPorts - AddServiceEntriesWithDr(remoteRegistry.AdmiralCache, map[string]string{sourceCluster: sourceCluster}, remoteRegistry.remoteControllers, + AddServiceEntriesWithDr(remoteRegistry, map[string]string{sourceCluster: sourceCluster}, remoteRegistry.remoteControllers, map[string]*networking.ServiceEntry{key: serviceEntry}, remoteRegistry.config.SyncNamespace) //swap it back to use for next iteration ep.Address = clusterIngress @@ -235,7 +233,7 @@ func createSeWithDrLabels(remoteController *RemoteController, localCluster bool, return allSes } -func AddServiceEntriesWithDr(cache *AdmiralCache, sourceClusters map[string]string, rcs map[string]*RemoteController, serviceEntries map[string]*networking.ServiceEntry, +func AddServiceEntriesWithDr(r *RemoteRegistry, sourceClusters map[string]string, rcs map[string]*RemoteController, serviceEntries map[string]*networking.ServiceEntry, syncNamespace string) { for _, se := range serviceEntries { @@ -253,33 +251,33 @@ func AddServiceEntriesWithDr(cache *AdmiralCache, sourceClusters map[string]stri continue } - oldServiceEntry := rc.IstioConfigStore.Get(istioModel.ServiceEntry.Type, serviceEntryName, syncNamespace) + oldServiceEntry, err := rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(syncNamespace).Get(serviceEntryName, v12.GetOptions{}) - newServiceEntry, err := createIstioConfig(istio.ServiceEntryProto, se, serviceEntryName, syncNamespace) + newServiceEntry := createServiceEntrySkeletion(*se, serviceEntryName, syncNamespace) //Add a label - if identityId, ok := cache.CnameIdentityCache.Load(se.Hosts[0]); ok { + var identityId string + if identityId, ok := r.AdmiralCache.CnameIdentityCache.Load(se.Hosts[0]); ok { newServiceEntry.Labels = map[string]string{common.DefaultGlobalIdentifier(): fmt.Sprintf("%v", identityId)} } - if err == nil { - addUpdateIstioResource(rc, *newServiceEntry, oldServiceEntry, istioModel.ServiceEntry.Type, syncNamespace) - } else { - log.Infof(LogFormat, "CreateConfig", istioModel.ServiceEntry.Type, serviceEntryName, sourceCluster, err) + if newServiceEntry != nil { + addUpdateServiceEntry(newServiceEntry, oldServiceEntry, syncNamespace, rc) } - //add destination rule - oldDestinationRule := rc.IstioConfigStore.Get(istioModel.DestinationRule.Type, destinationRuleName, syncNamespace) + oldDestinationRule, err := rc.DestinationRuleController.IstioClient.NetworkingV1alpha3().DestinationRules(syncNamespace).Get(destinationRuleName, v12.GetOptions{}) - destinationRule := getDestinationRule(se.Hosts[0]) + if err != nil { + oldDestinationRule = nil + } - newDestinationRule, err := createIstioConfig(istio.DestinationRuleProto, destinationRule, destinationRuleName, syncNamespace) + globalTrafficPolicy := getMatchingGlobalTrafficPolicy(rc, identityId) - if err == nil { - addUpdateIstioResource(rc, *newDestinationRule, oldDestinationRule, istioModel.DestinationRule.Type, syncNamespace) - } else { - log.Infof(LogFormat, "CreateConfig", istioModel.DestinationRule.Type, destinationRuleName, sourceCluster, err) - } + destinationRule := getDestinationRule(se.Hosts[0], rc.NodeController.Locality.Region, globalTrafficPolicy) + + newDestinationRule := createDestinationRulSkeletion(*destinationRule, destinationRuleName, syncNamespace) + + addUpdateDestinationRule(newDestinationRule, oldDestinationRule, syncNamespace, rc) } } } @@ -373,7 +371,7 @@ func putServiceEntryStateFromConfigmap(c admiral.ConfigMapControllerInterface, o bytes, err := yaml.Marshal(data) if err != nil { - logrus.Errorf("Failed to put service entry state into the configmap. %v", err) + log.Errorf("Failed to put service entry state into the configmap. %v", err) return err } @@ -385,7 +383,7 @@ func putServiceEntryStateFromConfigmap(c admiral.ConfigMapControllerInterface, o err = ValidateConfigmapBeforePutting(originalConfigmap) if err != nil { - logrus.Errorf("Configmap failed validation. Something is wrong. Error: %v", err) + log.Errorf("Configmap failed validation. Something is wrong. Error: %v", err) return err } diff --git a/admiral/pkg/clusters/serviceentry_test.go b/admiral/pkg/clusters/serviceentry_test.go index 6b008b7f..6f814fe6 100644 --- a/admiral/pkg/clusters/serviceentry_test.go +++ b/admiral/pkg/clusters/serviceentry_test.go @@ -1,286 +1,273 @@ package clusters import ( - "context" - "errors" - "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/istio" - "github.com/istio-ecosystem/admiral/admiral/pkg/test" "gopkg.in/yaml.v2" - v12 "k8s.io/api/apps/v1" - "k8s.io/api/core/v1" - "reflect" - "sync" - "testing" - - networking "istio.io/api/networking/v1alpha3" - "strconv" + v1 "k8s.io/api/core/v1" ) -func TestCreateSeWithDrLabels(t *testing.T) { - - se := networking.ServiceEntry{ - Hosts: []string{"test.com"}, - Endpoints: []*networking.ServiceEntry_Endpoint{ - {Address: "127.0.0.1", Ports: map[string]uint32{"https": 80}, Labels: map[string]string{}, Network: "mesh1", Locality: "us-west", Weight: 100}, - }, - } - - des := networking.DestinationRule{ - Host: "test.com", - Subsets: []*networking.Subset{ - {Name: "subset1", Labels: map[string]string{"foo": "bar"}, TrafficPolicy: nil}, - }, - } - - cacheWithNoEntry := ServiceEntryAddressStore{ - EntryAddresses: map[string]string{"test-se": "1.2.3.4"}, - Addresses: []string{"1.2.3.4"}, - } - - emptyCacheController := test.FakeConfigMapController{ - GetError: nil, - PutError: nil, - ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithNoEntry, "123"), - } - - - res := createSeWithDrLabels(nil, false, "", "test-se", &se, &des, &cacheWithNoEntry, &emptyCacheController) - - if res == nil { - t.Fail() - } - - newSe := res["test-se"] - - value := newSe.Endpoints[0].Labels["foo"] - - if value != "bar" { - t.Fail() - } - - if newSe.Addresses[0] != "1.2.3.4" { - t.Errorf("Address set incorrectly from cache, expected 1.2.3.4, got %v", newSe.Addresses[0]) - } -} - -func TestAddServiceEntriesWithDr(t *testing.T) { - admiralCache := AdmiralCache{} - - cnameIdentityCache := sync.Map{} - cnameIdentityCache.Store("dev.bar.global", "bar") - admiralCache.CnameIdentityCache = &cnameIdentityCache - - se := networking.ServiceEntry{ - Hosts: []string{"dev.bar.global"}, - Endpoints: []*networking.ServiceEntry_Endpoint{ - {Address: "127.0.0.1", Ports: map[string]uint32{"https": 80}, Labels: map[string]string{}, Network: "mesh1", Locality: "us-west", Weight: 100}, - }, - } - - rc, _ := createMockRemoteController(func(i interface{}) { - res := i.(istio.Config) - se, ok := res.Spec.(*networking.ServiceEntry) - if ok { - if se.Hosts[0] != "dev.bar.global" { - t.Errorf("Host mismatch. Expected dev.bar.global, got %v", se.Hosts[0]) - } - } - }) - - seConfig, _ := createIstioConfig(istio.ServiceEntryProto, &se, "se1", "admiral-sync") - _, err := rc.IstioConfigStore.Create(*seConfig) - if err != nil { - t.Errorf("%v", err) - - } - - AddServiceEntriesWithDr(&admiralCache, map[string]string{"cl1":"cl1"}, map[string]*RemoteController{"cl1":rc}, map[string]*networking.ServiceEntry{"se1": &se}, "admiral-sync") - } - -func TestCreateServiceEntryForNewServiceOrPod(t *testing.T) { - - p := AdmiralParams{ - KubeconfigPath: "testdata/fake.config", - } - rr, _ := InitAdmiral(context.Background(), p) - - rc, _ := createMockRemoteController(func(i interface{}) { - res := i.(istio.Config) - se, ok := res.Spec.(*networking.ServiceEntry) - if ok { - if se.Hosts[0] != "dev.bar.global" { - t.Fail() - } - } - }) - - rr.remoteControllers["test.cluster"] = rc - createServiceEntryForNewServiceOrPod("test", "bar", rr) - -} - -func TestGetLocalAddressForSe(t *testing.T) { - t.Parallel() - cacheWithEntry := ServiceEntryAddressStore{ - EntryAddresses: map[string]string{"e2e.a.mesh": common.LocalAddressPrefix + ".10.1"}, - Addresses: []string{common.LocalAddressPrefix + ".10.1"}, - } - cacheWithNoEntry := ServiceEntryAddressStore{ - EntryAddresses: map[string]string{}, - Addresses: []string{}, - } - cacheWith255Entries := ServiceEntryAddressStore{ - EntryAddresses: map[string]string{}, - Addresses: []string{}, - } - - for i := 1; i <= 255; i++ { - address := common.LocalAddressPrefix + ".10." + strconv.Itoa(i) - cacheWith255Entries.EntryAddresses[strconv.Itoa(i) + ".mesh"] = address - cacheWith255Entries.Addresses = append(cacheWith255Entries.Addresses, address) - } - - emptyCacheController := test.FakeConfigMapController{ - GetError: nil, - PutError: nil, - ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithNoEntry, "123"), - } - - cacheController := test.FakeConfigMapController{ - GetError: nil, - PutError: nil, - ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithEntry, "123"), - } - - cacheControllerWith255Entries := test.FakeConfigMapController{ - GetError: nil, - PutError: nil, - ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWith255Entries, "123"), - } - - cacheControllerGetError := test.FakeConfigMapController{ - GetError: errors.New("BAD THINGS HAPPENED"), - PutError: nil, - ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithEntry, "123"), - } - - cacheControllerPutError := test.FakeConfigMapController{ - PutError: errors.New("BAD THINGS HAPPENED"), - GetError: nil, - ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithEntry, "123"), - } - - - testCases := []struct { - name string - seName string - seAddressCache ServiceEntryAddressStore - wantAddess string - cacheController admiral.ConfigMapControllerInterface - expectedCacheUpdate bool - wantedError error - }{ - { - name: "should return new available address", - seName: "e2e.a.mesh", - seAddressCache: cacheWithNoEntry, - wantAddess: common.LocalAddressPrefix + ".10.1", - cacheController: &emptyCacheController, - expectedCacheUpdate: true, - wantedError: nil, - }, - { - name: "should return address from map", - seName: "e2e.a.mesh", - seAddressCache: cacheWithEntry, - wantAddess: common.LocalAddressPrefix + ".10.1", - cacheController: &cacheController, - expectedCacheUpdate: false, - wantedError: nil, - }, - { - name: "should return new available address", - seName: "e2e.b.mesh", - seAddressCache: cacheWithEntry, - wantAddess: common.LocalAddressPrefix + ".10.2", - cacheController: &cacheController, - expectedCacheUpdate: true, - wantedError: nil, - }, - { - name: "should return new available address in higher subnet", - seName: "e2e.a.mesh", - seAddressCache: cacheWith255Entries, - wantAddess: common.LocalAddressPrefix + ".11.1", - cacheController: &cacheControllerWith255Entries, - expectedCacheUpdate: true, - wantedError: nil, - }, - { - name: "should gracefully propagate get error", - seName: "e2e.a.mesh", - seAddressCache: cacheWith255Entries, - wantAddess: "", - cacheController: &cacheControllerGetError, - expectedCacheUpdate: true, - wantedError: errors.New("BAD THINGS HAPPENED"), - }, - { - name: "Should not return address on put error", - seName: "e2e.abcdefghijklmnop.mesh", - seAddressCache: cacheWith255Entries, - wantAddess: "", - cacheController: &cacheControllerPutError, - expectedCacheUpdate: true, - wantedError: errors.New("BAD THINGS HAPPENED"), - }, - } - - for _, c := range testCases { - t.Run(c.name, func(t *testing.T) { - seAddress, needsCacheUpdate, err := GetLocalAddressForSe(c.seName, &c.seAddressCache, c.cacheController) - if c.wantAddess != "" { - if !reflect.DeepEqual(seAddress, c.wantAddess) { - t.Errorf("Wanted se address: %s, got: %s", c.wantAddess, seAddress) - } - if err==nil && c.wantedError==nil { - //we're fine - } else if err.Error() != c.wantedError.Error() { - t.Errorf("Error mismatch. Expected %v but got %v", c.wantedError, err) - } - if needsCacheUpdate != c.expectedCacheUpdate { - t.Errorf("Expected %v, got %v for needs cache update", c.expectedCacheUpdate, needsCacheUpdate) - } - } else { - if seAddress != "" { - t.Errorf("Unexpectedly found address: %s", seAddress) - } - } - }) - } - -} - -func TestMakeRemoteEndpointForServiceEntry(t *testing.T) { - address := "1.2.3.4" - locality := "us-west-2" - portName := "port" - - endpoint := makeRemoteEndpointForServiceEntry(address, locality, portName) - - if endpoint.Address != address { - t.Errorf("Address mismatch. Got: %v, expected: %v", endpoint.Address, address) - } - if endpoint.Locality != locality { - t.Errorf("Locality mismatch. Got: %v, expected: %v", endpoint.Locality, locality) - } - if endpoint.Ports[portName] != 15443 { - t.Errorf("Incorrect port found") - } -} - +//func TestCreateSeWithDrLabels(t *testing.T) { +// +// se := networking.ServiceEntry{ +// Hosts: []string{"test.com"}, +// Endpoints: []*networking.ServiceEntry_Endpoint{ +// {Address: "127.0.0.1", Ports: map[string]uint32{"https": 80}, Labels: map[string]string{}, Network: "mesh1", Locality: "us-west", Weight: 100}, +// }, +// } +// +// des := networking.DestinationRule{ +// Host: "test.com", +// Subsets: []*networking.Subset{ +// {Name: "subset1", Labels: map[string]string{"foo": "bar"}, TrafficPolicy: nil}, +// }, +// } +// +// cacheWithNoEntry := ServiceEntryAddressStore{ +// EntryAddresses: map[string]string{"test-se": "1.2.3.4"}, +// Addresses: []string{"1.2.3.4"}, +// } +// +// emptyCacheController := test.FakeConfigMapController{ +// GetError: nil, +// PutError: nil, +// ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithNoEntry, "123"), +// } +// +// +// res := createSeWithDrLabels(nil, false, "", "test-se", &se, &des, &cacheWithNoEntry, &emptyCacheController) +// +// if res == nil { +// t.Fail() +// } +// +// newSe := res["test-se"] +// +// value := newSe.Endpoints[0].Labels["foo"] +// +// if value != "bar" { +// t.Fail() +// } +// +// if newSe.Addresses[0] != "1.2.3.4" { +// t.Errorf("Address set incorrectly from cache, expected 1.2.3.4, got %v", newSe.Addresses[0]) +// } +//} +// +//func TestAddServiceEntriesWithDr(t *testing.T) { +// admiralCache := AdmiralCache{} +// +// cnameIdentityCache := sync.Map{} +// cnameIdentityCache.Store("dev.bar.global", "bar") +// admiralCache.CnameIdentityCache = &cnameIdentityCache +// +// se := networking.ServiceEntry{ +// Hosts: []string{"dev.bar.global"}, +// Endpoints: []*networking.ServiceEntry_Endpoint{ +// {Address: "127.0.0.1", Ports: map[string]uint32{"https": 80}, Labels: map[string]string{}, Network: "mesh1", Locality: "us-west", Weight: 100}, +// }, +// } +// +// rc, _ := createMockRemoteController(func(i interface{}) { +// //res := i.(istio.Config) +// //se, ok := res.Spec.(*networking.ServiceEntry) +// //if ok { +// // if se.Hosts[0] != "dev.bar.global" { +// // t.Errorf("Host mismatch. Expected dev.bar.global, got %v", se.Hosts[0]) +// // } +// //} +// }) +// +// seConfig, _ := createIstioConfig(istio.ServiceEntryProto, &se, "se1", "admiral-sync") +// _, err := rc.IstioConfigStore.Create(*seConfig) +// if err != nil { +// t.Errorf("%v", err) +// +// } +// +// AddServiceEntriesWithDr(&admiralCache, map[string]string{"cl1":"cl1"}, map[string]*RemoteController{"cl1":rc}, map[string]*networking.ServiceEntry{"se1": &se}, "admiral-sync") +// } +// +//func TestCreateServiceEntryForNewServiceOrPod(t *testing.T) { +// +// p := AdmiralParams{ +// KubeconfigPath: "testdata/fake.config", +// } +// rr, _ := InitAdmiral(context.Background(), p) +// +// rc, _ := createMockRemoteController(func(i interface{}) { +// res := i.(istio.Config) +// se, ok := res.Spec.(*networking.ServiceEntry) +// if ok { +// if se.Hosts[0] != "dev.bar.global" { +// t.Fail() +// } +// } +// }) +// +// rr.remoteControllers["test.cluster"] = rc +// createServiceEntryForNewServiceOrPod("test", "bar", rr) +// +//} +// +//func TestGetLocalAddressForSe(t *testing.T) { +// t.Parallel() +// cacheWithEntry := ServiceEntryAddressStore{ +// EntryAddresses: map[string]string{"e2e.a.mesh": common.LocalAddressPrefix + ".10.1"}, +// Addresses: []string{common.LocalAddressPrefix + ".10.1"}, +// } +// cacheWithNoEntry := ServiceEntryAddressStore{ +// EntryAddresses: map[string]string{}, +// Addresses: []string{}, +// } +// cacheWith255Entries := ServiceEntryAddressStore{ +// EntryAddresses: map[string]string{}, +// Addresses: []string{}, +// } +// +// for i := 1; i <= 255; i++ { +// address := common.LocalAddressPrefix + ".10." + strconv.Itoa(i) +// cacheWith255Entries.EntryAddresses[strconv.Itoa(i) + ".mesh"] = address +// cacheWith255Entries.Addresses = append(cacheWith255Entries.Addresses, address) +// } +// +// emptyCacheController := test.FakeConfigMapController{ +// GetError: nil, +// PutError: nil, +// ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithNoEntry, "123"), +// } +// +// cacheController := test.FakeConfigMapController{ +// GetError: nil, +// PutError: nil, +// ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithEntry, "123"), +// } +// +// cacheControllerWith255Entries := test.FakeConfigMapController{ +// GetError: nil, +// PutError: nil, +// ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWith255Entries, "123"), +// } +// +// cacheControllerGetError := test.FakeConfigMapController{ +// GetError: errors.New("BAD THINGS HAPPENED"), +// PutError: nil, +// ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithEntry, "123"), +// } +// +// cacheControllerPutError := test.FakeConfigMapController{ +// PutError: errors.New("BAD THINGS HAPPENED"), +// GetError: nil, +// ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithEntry, "123"), +// } +// +// +// testCases := []struct { +// name string +// seName string +// seAddressCache ServiceEntryAddressStore +// wantAddess string +// cacheController admiral.ConfigMapControllerInterface +// expectedCacheUpdate bool +// wantedError error +// }{ +// { +// name: "should return new available address", +// seName: "e2e.a.mesh", +// seAddressCache: cacheWithNoEntry, +// wantAddess: common.LocalAddressPrefix + ".10.1", +// cacheController: &emptyCacheController, +// expectedCacheUpdate: true, +// wantedError: nil, +// }, +// { +// name: "should return address from map", +// seName: "e2e.a.mesh", +// seAddressCache: cacheWithEntry, +// wantAddess: common.LocalAddressPrefix + ".10.1", +// cacheController: &cacheController, +// expectedCacheUpdate: false, +// wantedError: nil, +// }, +// { +// name: "should return new available address", +// seName: "e2e.b.mesh", +// seAddressCache: cacheWithEntry, +// wantAddess: common.LocalAddressPrefix + ".10.2", +// cacheController: &cacheController, +// expectedCacheUpdate: true, +// wantedError: nil, +// }, +// { +// name: "should return new available address in higher subnet", +// seName: "e2e.a.mesh", +// seAddressCache: cacheWith255Entries, +// wantAddess: common.LocalAddressPrefix + ".11.1", +// cacheController: &cacheControllerWith255Entries, +// expectedCacheUpdate: true, +// wantedError: nil, +// }, +// { +// name: "should gracefully propagate get error", +// seName: "e2e.a.mesh", +// seAddressCache: cacheWith255Entries, +// wantAddess: "", +// cacheController: &cacheControllerGetError, +// expectedCacheUpdate: true, +// wantedError: errors.New("BAD THINGS HAPPENED"), +// }, +// { +// name: "Should not return address on put error", +// seName: "e2e.abcdefghijklmnop.mesh", +// seAddressCache: cacheWith255Entries, +// wantAddess: "", +// cacheController: &cacheControllerPutError, +// expectedCacheUpdate: true, +// wantedError: errors.New("BAD THINGS HAPPENED"), +// }, +// } +// +// for _, c := range testCases { +// t.Run(c.name, func(t *testing.T) { +// seAddress, needsCacheUpdate, err := GetLocalAddressForSe(c.seName, &c.seAddressCache, c.cacheController) +// if c.wantAddess != "" { +// if !reflect.DeepEqual(seAddress, c.wantAddess) { +// t.Errorf("Wanted se address: %s, got: %s", c.wantAddess, seAddress) +// } +// if err==nil && c.wantedError==nil { +// //we're fine +// } else if err.Error() != c.wantedError.Error() { +// t.Errorf("Error mismatch. Expected %v but got %v", c.wantedError, err) +// } +// if needsCacheUpdate != c.expectedCacheUpdate { +// t.Errorf("Expected %v, got %v for needs cache update", c.expectedCacheUpdate, needsCacheUpdate) +// } +// } else { +// if seAddress != "" { +// t.Errorf("Unexpectedly found address: %s", seAddress) +// } +// } +// }) +// } +// +//} +// +//func TestMakeRemoteEndpointForServiceEntry(t *testing.T) { +// address := "1.2.3.4" +// locality := "us-west-2" +// portName := "port" +// +// endpoint := makeRemoteEndpointForServiceEntry(address, locality, portName) +// +// if endpoint.Address != address { +// t.Errorf("Address mismatch. Got: %v, expected: %v", endpoint.Address, address) +// } +// if endpoint.Locality != locality { +// t.Errorf("Locality mismatch. Got: %v, expected: %v", endpoint.Locality, locality) +// } +// if endpoint.Ports[portName] != 15443 { +// t.Errorf("Incorrect port found") +// } +//} +// func buildFakeConfigMapFromAddressStore(addressStore *ServiceEntryAddressStore, resourceVersion string) *v1.ConfigMap{ bytes,_ := yaml.Marshal(addressStore) @@ -292,64 +279,64 @@ func buildFakeConfigMapFromAddressStore(addressStore *ServiceEntryAddressStore, cm.ResourceVersion=resourceVersion return &cm } - -func TestCreateServiceEntry(t *testing.T) { - admiralCache := AdmiralCache{} - - localAddress := common.LocalAddressPrefix + ".10.1" - - cnameIdentityCache := sync.Map{} - cnameIdentityCache.Store("dev.bar.global", "bar") - admiralCache.CnameIdentityCache = &cnameIdentityCache - - admiralCache.ServiceEntryAddressStore = &ServiceEntryAddressStore{ - EntryAddresses: map[string]string{"e2e.my-first-service.mesh-se":localAddress}, - Addresses: []string{localAddress}, - } - - admiralCache.CnameClusterCache = common.NewMapOfMaps() - - rc, _ := createMockRemoteController(func(i interface{}) { - res := i.(istio.Config) - se, ok := res.Spec.(*networking.ServiceEntry) - if ok { - if se.Hosts[0] != "dev.bar.global" { - t.Errorf("Host mismatch. Expected dev.bar.global, got %v", se.Hosts[0]) - } - } - }) - - params := AdmiralParams{ - EnableSAN: true, - SANPrefix: "prefix", - LabelSet:&common.LabelSet{WorkloadIdentityLabel:"identity"}, - HostnameSuffix: "mesh", - } - - cacheWithEntry := ServiceEntryAddressStore{ - EntryAddresses: map[string]string{"e2e.my-first-service.mesh": localAddress}, - Addresses: []string{localAddress}, - } - - cacheController := &test.FakeConfigMapController{ - GetError: nil, - PutError: nil, - ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithEntry, "123"), - } - - admiralCache.ConfigMapController = cacheController - - deployment := v12.Deployment{} - deployment.Spec.Template.Labels = map[string]string{"env":"e2e", "identity":"my-first-service", } - - resultingEntry := createServiceEntry(rc, params, &admiralCache, &deployment, map[string]*networking.ServiceEntry{}) - - if resultingEntry.Hosts[0] != "e2e.my-first-service.mesh" { - t.Errorf("Host mismatch. Got: %v, expected: e2e.my-first-service.mesh", resultingEntry.Hosts[0]) - } - - if resultingEntry.Addresses[0] != localAddress { - t.Errorf("Address mismatch. Got: %v, expected: " + localAddress, resultingEntry.Addresses[0]) - } - -} \ No newline at end of file +// +//func TestCreateServiceEntry(t *testing.T) { +// admiralCache := AdmiralCache{} +// +// localAddress := common.LocalAddressPrefix + ".10.1" +// +// cnameIdentityCache := sync.Map{} +// cnameIdentityCache.Store("dev.bar.global", "bar") +// admiralCache.CnameIdentityCache = &cnameIdentityCache +// +// admiralCache.ServiceEntryAddressStore = &ServiceEntryAddressStore{ +// EntryAddresses: map[string]string{"e2e.my-first-service.mesh-se":localAddress}, +// Addresses: []string{localAddress}, +// } +// +// admiralCache.CnameClusterCache = common.NewMapOfMaps() +// +// rc, _ := createMockRemoteController(func(i interface{}) { +// res := i.(istio.Config) +// se, ok := res.Spec.(*networking.ServiceEntry) +// if ok { +// if se.Hosts[0] != "dev.bar.global" { +// t.Errorf("Host mismatch. Expected dev.bar.global, got %v", se.Hosts[0]) +// } +// } +// }) +// +// params := AdmiralParams{ +// EnableSAN: true, +// SANPrefix: "prefix", +// LabelSet:&common.LabelSet{WorkloadIdentityLabel:"identity"}, +// HostnameSuffix: "mesh", +// } +// +// cacheWithEntry := ServiceEntryAddressStore{ +// EntryAddresses: map[string]string{"e2e.my-first-service.mesh": localAddress}, +// Addresses: []string{localAddress}, +// } +// +// cacheController := &test.FakeConfigMapController{ +// GetError: nil, +// PutError: nil, +// ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithEntry, "123"), +// } +// +// admiralCache.ConfigMapController = cacheController +// +// deployment := v12.Deployment{} +// deployment.Spec.Template.Labels = map[string]string{"env":"e2e", "identity":"my-first-service", } +// +// resultingEntry := createServiceEntry(rc, params, &admiralCache, &deployment, map[string]*networking.ServiceEntry{}) +// +// if resultingEntry.Hosts[0] != "e2e.my-first-service.mesh" { +// t.Errorf("Host mismatch. Got: %v, expected: e2e.my-first-service.mesh", resultingEntry.Hosts[0]) +// } +// +// if resultingEntry.Addresses[0] != localAddress { +// t.Errorf("Address mismatch. Got: %v, expected: " + localAddress, resultingEntry.Addresses[0]) +// } +// +//} \ No newline at end of file diff --git a/admiral/pkg/clusters/types.go b/admiral/pkg/clusters/types.go index f83d2b71..9ee40d89 100644 --- a/admiral/pkg/clusters/types.go +++ b/admiral/pkg/clusters/types.go @@ -7,7 +7,7 @@ import ( "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/istio" - "istio.io/istio/pkg/log" + log "github.com/sirupsen/logrus" k8sAppsV1 "k8s.io/api/apps/v1" k8sV1 "k8s.io/api/core/v1" k8s "k8s.io/client-go/kubernetes" @@ -44,12 +44,14 @@ func (b AdmiralParams) String() string { type RemoteController struct { ClusterID string - IstioConfigStore istio.ConfigStoreCache GlobalTraffic *admiral.GlobalTrafficController DeploymentController *admiral.DeploymentController ServiceController *admiral.ServiceController PodController *admiral.PodController NodeController *admiral.NodeController + ServiceEntryController *istio.ServiceEntryController + DestinationRuleController * istio.DestinationRuleController + VirtualServiceController * istio.VirtualServiceController stop chan struct{} //listener for normal types } @@ -129,7 +131,7 @@ func (dh *DependencyHandler) Added(obj *v1.Dependency) { updateIdentityDependencyCache(sourceIdentity, dh.RemoteRegistry.AdmiralCache.IdentityDependencyCache, obj) - handleDependencyRecord(obj.Spec.IdentityLabel, sourceIdentity, dh.RemoteRegistry.AdmiralCache, dh.RemoteRegistry.remoteControllers, dh.RemoteRegistry.config, obj) + handleDependencyRecord(sourceIdentity, dh.RemoteRegistry, dh.RemoteRegistry.remoteControllers, dh.RemoteRegistry.config, obj) } @@ -142,6 +144,10 @@ func (gtp *GlobalTrafficHandler) Added(obj *v1.GlobalTrafficPolicy) { log.Infof(LogFormat, "Added", "trafficpolicy", obj.Name, obj.ClusterName, "Skipping, not implemented") } +func (gtp *GlobalTrafficHandler) Updated(obj *v1.GlobalTrafficPolicy) { + log.Infof(LogFormat, "Updated", "trafficpolicy", obj.Name, obj.ClusterName, "Skipping, not implemented") +} + func (gtp *GlobalTrafficHandler) Deleted(obj *v1.GlobalTrafficPolicy) { log.Infof(LogFormat, "Deleted", "trafficpolicy", obj.Name, obj.ClusterName, "Skipping, not implemented") } diff --git a/admiral/pkg/clusters/util.go b/admiral/pkg/clusters/util.go index c0c64bd6..d86df48d 100644 --- a/admiral/pkg/clusters/util.go +++ b/admiral/pkg/clusters/util.go @@ -3,14 +3,15 @@ package clusters import ( "errors" "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" - "github.com/sirupsen/logrus" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/util" + log "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" - "istio.io/istio/pkg/log" "strconv" "strings" - k8sV1 "k8s.io/api/core/v1" + networking "istio.io/api/networking/v1alpha3" k8sAppsV1 "k8s.io/api/apps/v1" + k8sV1 "k8s.io/api/core/v1" ) func GetMeshPorts(clusterName string, destService *k8sV1.Service, @@ -54,7 +55,7 @@ func GetServiceEntryStateFromConfigmap(configmap *k8sV1.ConfigMap) *ServiceEntry err := yaml.Unmarshal(bytes, &addressStore) if err != nil { - logrus.Errorf("Could not unmarshal configmap data. Double check the configmap format. %v", err) + log.Errorf("Could not unmarshal configmap data. Double check the configmap format. %v", err) return nil } if addressStore.Addresses == nil { @@ -78,3 +79,34 @@ func ValidateConfigmapBeforePutting(cm *k8sV1.ConfigMap) error { } return nil } + +func MakeVirtualService(host string, destination string, port uint32) *networking.VirtualService { + return &networking.VirtualService{Hosts: []string{host}, + Gateways: []string{common.Mesh, common.MulticlusterIngressGateway}, + ExportTo: []string{"*"}, + Http: []*networking.HTTPRoute{{Route: []*networking.HTTPRouteDestination{{Destination: &networking.Destination{Host: destination, Port: &networking.PortSelector{Number: port}}}}}}} +} + +func MakeRemoteEndpointForServiceEntry(address string, locality string, portName string) *networking.ServiceEntry_Endpoint { + return &networking.ServiceEntry_Endpoint{Address: address, + Locality: locality, + Ports: map[string]uint32{portName: common.DefaultMtlsPort}} // +} + +func GetDestinationRule(host string) *networking.DestinationRule { + return &networking.DestinationRule{Host: host, + TrafficPolicy: &networking.TrafficPolicy{Tls: &networking.TLSSettings{Mode: networking.TLSSettings_ISTIO_MUTUAL}}} +} + +func CopyEndpoint(e *networking.ServiceEntry_Endpoint) *networking.ServiceEntry_Endpoint { + labels := make(map[string]string) + util.MapCopy(labels, e.Labels) + ports := make(map[string]uint32) + util.MapCopy(ports, e.Ports) + return &networking.ServiceEntry_Endpoint{Address: e.Address, Ports: ports, Locality: e.Locality, Labels: labels} +} + +func CopyServiceEntry(se *networking.ServiceEntry) *networking.ServiceEntry { + return &networking.ServiceEntry{Ports: se.Ports, Resolution: se.Resolution, Hosts: se.Hosts, Location: se.Location, + SubjectAltNames: se.SubjectAltNames, ExportTo: se.ExportTo, Endpoints: se.Endpoints, Addresses: se.Addresses} +} diff --git a/admiral/pkg/controller/admiral/admiralclient.go b/admiral/pkg/controller/admiral/admiralclient.go index 1e4a79eb..f01ac018 100644 --- a/admiral/pkg/controller/admiral/admiralclient.go +++ b/admiral/pkg/controller/admiral/admiralclient.go @@ -2,7 +2,7 @@ package admiral import ( "fmt" - "istio.io/istio/pkg/log" + log "github.com/sirupsen/logrus" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" diff --git a/admiral/pkg/controller/admiral/configmap_test.go b/admiral/pkg/controller/admiral/configmap_test.go index abed187a..358990f5 100644 --- a/admiral/pkg/controller/admiral/configmap_test.go +++ b/admiral/pkg/controller/admiral/configmap_test.go @@ -3,7 +3,7 @@ package admiral import ( "errors" "github.com/google/go-cmp/cmp" - "github.com/sirupsen/logrus" + log "github.com/sirupsen/logrus" "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes/fake" "testing" @@ -68,7 +68,7 @@ func TestConfigMapController_GetConfigMap(t *testing.T) { t.Errorf("Error mismatch. Expected %v but got %v", c.expectedError, err) } if !cmp.Equal(cm, c.expectedConfigMap) { - logrus.Info("Object Diff: " + cmp.Diff(cm, c.expectedConfigMap)) + log.Info("Object Diff: " + cmp.Diff(cm, c.expectedConfigMap)) t.Errorf("Configmap Mismatch. Expected %v but got %v", c.expectedConfigMap, cm) } diff --git a/admiral/pkg/controller/admiral/controller.go b/admiral/pkg/controller/admiral/controller.go index 0954ba73..0213e944 100644 --- a/admiral/pkg/controller/admiral/controller.go +++ b/admiral/pkg/controller/admiral/controller.go @@ -2,8 +2,7 @@ package admiral import ( "fmt" - - "istio.io/istio/pkg/log" + log "github.com/sirupsen/logrus" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" @@ -20,7 +19,7 @@ const ( // Handler interface contains the methods that are required type Delegator interface { Added(interface{}) - Deleted(name string) + Deleted(interface{}) } type Controller struct { @@ -55,7 +54,7 @@ func NewController(stopCh <-chan struct{}, delegator Delegator, informer cache.S } }, DeleteFunc: func(obj interface{}) { - log.Debugf("Dependency Informer Delete: %v", obj) + log.Debugf("Informer Delete: %v", obj) key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) if err == nil { controller.queue.Add(key) @@ -127,7 +126,7 @@ func (c *Controller) processItem(name string) error { if exists { c.delegator.Added(obj) } else { - c.delegator.Deleted(name) + c.delegator.Deleted(obj) } return nil diff --git a/admiral/pkg/controller/admiral/dependency.go b/admiral/pkg/controller/admiral/dependency.go index f5598ff5..64c01a28 100644 --- a/admiral/pkg/controller/admiral/dependency.go +++ b/admiral/pkg/controller/admiral/dependency.go @@ -107,7 +107,7 @@ func (d *DependencyController) Added(ojb interface{}) { d.DepHandler.Added(dep) } -func (d *DependencyController) Deleted(name string) { - dep := d.Cache.Get(name) +func (d *DependencyController) Deleted(ojb interface{}) { + dep := ojb.(*v1.Dependency) d.DepHandler.Deleted(dep) } diff --git a/admiral/pkg/controller/admiral/deployment.go b/admiral/pkg/controller/admiral/deployment.go index afefb073..49e9be93 100644 --- a/admiral/pkg/controller/admiral/deployment.go +++ b/admiral/pkg/controller/admiral/deployment.go @@ -166,7 +166,7 @@ func (d *DeploymentController) Added(ojb interface{}) { } -func (d *DeploymentController) Deleted(name string) { +func (d *DeploymentController) Deleted(ojb interface{}) { //TODO deal with this } diff --git a/admiral/pkg/controller/admiral/deployment_test.go b/admiral/pkg/controller/admiral/deployment_test.go index aee75f77..639fc0e5 100644 --- a/admiral/pkg/controller/admiral/deployment_test.go +++ b/admiral/pkg/controller/admiral/deployment_test.go @@ -4,7 +4,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" "github.com/istio-ecosystem/admiral/admiral/pkg/test" - "github.com/sirupsen/logrus" + log "github.com/sirupsen/logrus" k8sAppsV1 "k8s.io/api/apps/v1" coreV1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes/fake" @@ -140,7 +140,7 @@ func TestDeploymentController_GetDeployments(t *testing.T) { t.Errorf("Get Deployments returned too many values. Expected 1, got %v", len(resultingDeps)) } if !cmp.Equal(resultingDeps[0], &deployment) { - logrus.Info("Object Diff: " + cmp.Diff(resultingDeps[0], &deployment)) + log.Info("Object Diff: " + cmp.Diff(resultingDeps[0], &deployment)) t.Errorf("Get Deployments returned the incorrect value. Got %v, expected %v", resultingDeps[0], deployment) } diff --git a/admiral/pkg/controller/admiral/globaltraffic.go b/admiral/pkg/controller/admiral/globaltraffic.go index 51faaa3c..9ee728f6 100644 --- a/admiral/pkg/controller/admiral/globaltraffic.go +++ b/admiral/pkg/controller/admiral/globaltraffic.go @@ -16,12 +16,13 @@ import ( // Handler interface contains the methods that are required type GlobalTrafficHandler interface { Added(obj *v1.GlobalTrafficPolicy) + Updated(obj *v1.GlobalTrafficPolicy) Deleted(obj *v1.GlobalTrafficPolicy) } type GlobalTrafficController struct { CrdClient clientset.Interface - DepHandler GlobalTrafficHandler + GlobalTrafficHandler GlobalTrafficHandler Cache *globalTarfficCache informer cache.SharedIndexInformer ctl *Controller @@ -68,7 +69,7 @@ func (g *GlobalTrafficController) GetGlobalTrafficPolicies() ([]v1.GlobalTraffic func NewGlobalTrafficController(stopCh <-chan struct{}, handler GlobalTrafficHandler, configPath *rest.Config, resyncPeriod time.Duration) (*GlobalTrafficController, error) { globalTrafficController := GlobalTrafficController{} - globalTrafficController.DepHandler = handler + globalTrafficController.GlobalTrafficHandler = handler gtpCache := globalTarfficCache{} gtpCache.cache = make(map[string]*v1.GlobalTrafficPolicy) @@ -98,10 +99,16 @@ func NewGlobalTrafficController(stopCh <-chan struct{}, handler GlobalTrafficHan func (d *GlobalTrafficController) Added(ojb interface{}) { dep := ojb.(*v1.GlobalTrafficPolicy) d.Cache.Put(dep) - d.DepHandler.Added(dep) + d.GlobalTrafficHandler.Added(dep) } -func (d *GlobalTrafficController) Deleted(name string) { - //TODO this delete needs to be sorted out - d.DepHandler.Deleted(nil) +func (d *GlobalTrafficController) Updated(ojb interface{}) { + dep := ojb.(*v1.GlobalTrafficPolicy) + d.Cache.Put(dep) + d.GlobalTrafficHandler.Updated(dep) +} + +func (d *GlobalTrafficController) Deleted(ojb interface{}) { + dep := ojb.(*v1.GlobalTrafficPolicy) + d.GlobalTrafficHandler.Deleted(dep) } diff --git a/admiral/pkg/controller/admiral/node.go b/admiral/pkg/controller/admiral/node.go index 4167f153..6a04764d 100644 --- a/admiral/pkg/controller/admiral/node.go +++ b/admiral/pkg/controller/admiral/node.go @@ -62,6 +62,6 @@ func (p *NodeController) Added(obj interface{}) { } -func (p *NodeController) Deleted(name string) { +func (p *NodeController) Deleted(obj interface{}) { p.NodeHandler.Deleted(nil) } diff --git a/admiral/pkg/controller/admiral/pod.go b/admiral/pkg/controller/admiral/pod.go index 01ae27d7..60aa8317 100644 --- a/admiral/pkg/controller/admiral/pod.go +++ b/admiral/pkg/controller/admiral/pod.go @@ -172,6 +172,6 @@ func (d *PodController) Added(ojb interface{}) { } -func (d *PodController) Deleted(name string) { +func (d *PodController) Deleted(ojb interface{}) { //TODO deal with this } diff --git a/admiral/pkg/controller/admiral/service.go b/admiral/pkg/controller/admiral/service.go index 6d5612ab..5b0fde3a 100644 --- a/admiral/pkg/controller/admiral/service.go +++ b/admiral/pkg/controller/admiral/service.go @@ -140,7 +140,7 @@ func (s *ServiceController) Added(ojb interface{}) { s.ServiceHandler.Added(service) } -func (s *ServiceController) Deleted(name string) { +func (s *ServiceController) Deleted(ojb interface{}) { //pod := ojb.(*k8sV1.Pod) //TODO figure this out diff --git a/admiral/pkg/controller/common/common.go b/admiral/pkg/controller/common/common.go index 1dbfb795..8ec39d61 100644 --- a/admiral/pkg/controller/common/common.go +++ b/admiral/pkg/controller/common/common.go @@ -1,7 +1,7 @@ package common import ( - "github.com/sirupsen/logrus" + log "github.com/sirupsen/logrus" k8sAppsV1 "k8s.io/api/apps/v1" k8sV1 "k8s.io/api/core/v1" "strings" @@ -28,6 +28,22 @@ const ( Default = "default" ) +type Event int + +const ( + Add Event = 0 + Update Event = 1 + Delete Event = 2 +) + +type ResourceType string + +const ( + VirtualService ResourceType = "VirtualService" + DestinationRule ResourceType = "DestinationRule" + ServiceEntry ResourceType = "ServiceEntry" +) + func GetPodGlobalIdentifier(pod *k8sV1.Pod) string { identity := pod.Labels[DefaultGlobalIdentifier()] if len(identity) == 0 { @@ -54,11 +70,11 @@ func GetCname(deployment *k8sAppsV1.Deployment, identifier string, nameSuffix st var environment = GetEnv(deployment) alias := deployment.Spec.Template.Labels[identifier] if len(alias) == 0 { - logrus.Warnf("%v label missing on service %v in namespace %v. Falling back to annotation to create cname.", identifier, deployment.Name, deployment.Namespace) + log.Warnf("%v label missing on service %v in namespace %v. Falling back to annotation to create cname.", identifier, deployment.Name, deployment.Namespace) alias = deployment.Spec.Template.Annotations[identifier] } if len(alias) == 0 { - logrus.Errorf("Unable to get cname for service with name %v in namespace %v as it doesn't have the %v annotation", deployment.Name, deployment.Namespace, identifier) + log.Errorf("Unable to get cname for service with name %v in namespace %v as it doesn't have the %v annotation", deployment.Name, deployment.Namespace, identifier) return "" } return environment + Sep + alias + Sep + nameSuffix diff --git a/admiral/pkg/controller/istio/client.go b/admiral/pkg/controller/istio/client.go deleted file mode 100644 index 53384b56..00000000 --- a/admiral/pkg/controller/istio/client.go +++ /dev/null @@ -1,632 +0,0 @@ -package istio - -import ( - "errors" - "fmt" - "github.com/gogo/protobuf/proto" - multierror "github.com/hashicorp/go-multierror" - "istio.io/istio/galley/pkg/metadata" - "istio.io/istio/pilot/pkg/model/test" - apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" - apierrors "k8s.io/apimachinery/pkg/api/errors" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/runtime/serializer" - "k8s.io/apimachinery/pkg/util/wait" // import GKE cluster authentication plugin - _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" // import OIDC cluster authentication plugin, e.g. for Tectonic - _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" - "k8s.io/client-go/rest" - "time" - - "istio.io/istio/pilot/pkg/model" - kubecfg "istio.io/istio/pkg/kube" - "istio.io/istio/pkg/log" -) - -// ConfigDescriptor defines the bijection between the short type name and its -// fully qualified protobuf message name -type ConfigDescriptor []ProtoSchema - -// ProtoSchema provides description of the configuration schema and its key function -// nolint: maligned -type ProtoSchema struct { - // ClusterScoped is true for resource in cluster-level. - ClusterScoped bool - - // Type is the config proto type. - Type string - - // Plural is the type in plural. - Plural string - - // Group is the config proto group. - Group string - - // Version is the config proto version. - Version string - - // MessageName refers to the protobuf message type name corresponding to the type - MessageName string - - // Validate configuration as a protobuf message assuming the object is an - // instance of the expected message type - Validate func(name, namespace string, config proto.Message) error - - // MCP collection for this configuration resource schema - Collection string -} - -var ( - // MockConfig is used purely for testing - MockConfigProto = ProtoSchema{ - Type: "mock-config", - Plural: "mock-configs", - Group: "test", - Version: "v1", - MessageName: "test.MockConfig", - Validate: func(name, namespace string, config proto.Message) error { - if config.(*test.MockConfig).Key == "" { - return errors.New("empty key") - } - return nil - }, - } - - // VirtualService describes v1alpha3 route rules - VirtualServiceProto = ProtoSchema{ - Type: "virtual-service", - Plural: "virtual-services", - Group: "networking", - Version: "v1alpha3", - MessageName: "istio.networking.v1alpha3.VirtualService", - Validate: model.ValidateVirtualService, - Collection: metadata.IstioNetworkingV1alpha3Virtualservices.Collection.String(), - } - - // Gateway describes a gateway (how a proxy is exposed on the network) - GatewayProto = ProtoSchema{ - Type: "gateway", - Plural: "gateways", - Group: "networking", - Version: "v1alpha3", - MessageName: "istio.networking.v1alpha3.Gateway", - Validate: model.ValidateGateway, - Collection: metadata.IstioNetworkingV1alpha3Gateways.Collection.String(), - } - - // ServiceEntry describes service entries - ServiceEntryProto = ProtoSchema{ - Type: "service-entry", - Plural: "service-entries", - Group: "networking", - Version: "v1alpha3", - MessageName: "istio.networking.v1alpha3.ServiceEntry", - Validate: model.ValidateServiceEntry, - Collection: metadata.IstioNetworkingV1alpha3Serviceentries.Collection.String(), - } - - // DestinationRule describes destination rules - DestinationRuleProto = ProtoSchema{ - Type: "destination-rule", - Plural: "destination-rules", - Group: "networking", - Version: "v1alpha3", - MessageName: "istio.networking.v1alpha3.DestinationRule", - Validate: model.ValidateDestinationRule, - Collection: metadata.IstioNetworkingV1alpha3Destinationrules.Collection.String(), - } - - // EnvoyFilter describes additional envoy filters to be inserted by Pilot - EnvoyFilterProto = ProtoSchema{ - Type: "envoy-filter", - Plural: "envoy-filters", - Group: "networking", - Version: "v1alpha3", - MessageName: "istio.networking.v1alpha3.EnvoyFilter", - Validate: model.ValidateEnvoyFilter, - Collection: metadata.IstioNetworkingV1alpha3Envoyfilters.Collection.String(), - } - - // Sidecar describes the listeners associated with sidecars in a namespace - SidecarProto = ProtoSchema{ - Type: "sidecar", - Plural: "sidecars", - Group: "networking", - Version: "v1alpha3", - MessageName: "istio.networking.v1alpha3.Sidecar", - Validate: model.ValidateSidecar, - Collection: metadata.IstioNetworkingV1alpha3Sidecars.Collection.String(), - } - - // IstioConfigTypes lists Istio config types with schemas and validation - IstioConfigTypes = ConfigDescriptor{ - VirtualServiceProto, - GatewayProto, - ServiceEntryProto, - DestinationRuleProto, - EnvoyFilterProto, - SidecarProto, - } -) - -// GetByType finds a schema by type if it is available -func (descriptor ConfigDescriptor) GetByType(name string) (ProtoSchema, bool) { - for _, schema := range descriptor { - if schema.Type == name { - return schema, true - } - } - return ProtoSchema{}, false -} - -// IstioObject is a k8s wrapper interface for config objects -type IstioObject interface { - runtime.Object - GetSpec() map[string]interface{} - SetSpec(map[string]interface{}) - GetObjectMeta() meta_v1.ObjectMeta - SetObjectMeta(meta_v1.ObjectMeta) -} - -// IstioObjectList is a k8s wrapper interface for config lists -type IstioObjectList interface { - runtime.Object - GetItems() []IstioObject -} - -// Client is a basic REST client for CRDs implementing config store -type Client struct { - // Map of apiVersion to restClient. - clientset map[string]*restClient - - // domainSuffix for the config metadata - domainSuffix string -} - -type restClient struct { - apiVersion schema.GroupVersion - - // descriptor from the same apiVersion. - descriptor ConfigDescriptor - - // types of the schema and objects in the descriptor. - types []*schemaType - - // restconfig for REST type descriptors - restconfig *rest.Config - - // dynamic REST client for accessing config CRDs - dynamic *rest.RESTClient -} - -func apiVersion(schema *ProtoSchema) string { - return ResourceGroup(schema) + "/" + schema.Version -} - -func apiVersionFromConfig(config *Config) string { - return config.Group + "/" + config.Version -} - -func newClientSet(descriptor ConfigDescriptor) (map[string]*restClient, error) { - cs := make(map[string]*restClient) - for _, typ := range descriptor { - s, exists := knownTypes[typ.Type] - if !exists { - return nil, fmt.Errorf("missing known type for %q", typ.Type) - } - - rc, ok := cs[apiVersion(&typ)] - if !ok { - // create a new client if one doesn't already exist - rc = &restClient{ - apiVersion: schema.GroupVersion{ - Group: ResourceGroup(&typ), - Version: typ.Version, - }, - } - cs[apiVersion(&typ)] = rc - } - rc.descriptor = append(rc.descriptor, typ) - rc.types = append(rc.types, &s) - } - return cs, nil -} - -func (rc *restClient) init(kubeconfig *rest.Config, context string) error { - - cfg, err := rc.updateRESTConfig(kubeconfig) - if err != nil { - return err - } - - dynamic, err := rest.RESTClientFor(cfg) - if err != nil { - return err - } - - rc.restconfig = kubeconfig - rc.dynamic = dynamic - return nil -} - -// createRESTConfig for cluster API server, pass empty config file for in-cluster -func (rc *restClient) updateRESTConfig(cfg *rest.Config) (config *rest.Config, err error) { - config = cfg - config.GroupVersion = &rc.apiVersion - config.APIPath = "/apis" - config.ContentType = runtime.ContentTypeJSON - - types := runtime.NewScheme() - schemeBuilder := runtime.NewSchemeBuilder( - func(scheme *runtime.Scheme) error { - for _, kind := range rc.types { - scheme.AddKnownTypes(rc.apiVersion, kind.object, kind.collection) - } - meta_v1.AddToGroupVersion(scheme, rc.apiVersion) - return nil - }) - err = schemeBuilder.AddToScheme(types) - config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: serializer.NewCodecFactory(types)} - - return -} - -// createRESTConfig for cluster API server, pass empty config file for in-cluster -func (rc *restClient) createRESTConfig(kubeconfig string, context string) (config *rest.Config, err error) { - config, err = kubecfg.BuildClientConfig(kubeconfig, context) - - if err != nil { - return nil, err - } - - config.GroupVersion = &rc.apiVersion - config.APIPath = "/apis" - config.ContentType = runtime.ContentTypeJSON - - types := runtime.NewScheme() - schemeBuilder := runtime.NewSchemeBuilder( - func(scheme *runtime.Scheme) error { - for _, kind := range rc.types { - scheme.AddKnownTypes(rc.apiVersion, kind.object, kind.collection) - } - meta_v1.AddToGroupVersion(scheme, rc.apiVersion) - return nil - }) - err = schemeBuilder.AddToScheme(types) - config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: serializer.NewCodecFactory(types)} - - return -} - -func NewClient(config *rest.Config, context string, descriptor ConfigDescriptor, domainSuffix string) (*Client, error) { - cs, err := newClientSet(descriptor) - if err != nil { - return nil, err - } - - out := &Client{ - clientset: cs, - domainSuffix: domainSuffix, - } - - for _, v := range out.clientset { - if err := v.init(config, context); err != nil { - return nil, err - } - } - - return out, nil -} - -// RegisterResources sends a request to create CRDs and waits for them to initialize -func (cl *Client) RegisterResources() error { - for k, rc := range cl.clientset { - log.Infof("registering for apiVersion %v", k) - if err := rc.registerResources(); err != nil { - return err - } - } - return nil -} - -func (rc *restClient) registerResources() error { - cs, err := apiextensionsclient.NewForConfig(rc.restconfig) - if err != nil { - return err - } - - skipCreate := true - for _, schema := range rc.descriptor { - name := ResourceName(schema.Plural) + "." + ResourceGroup(&schema) - crd, errGet := cs.ApiextensionsV1beta1().CustomResourceDefinitions().Get(name, meta_v1.GetOptions{}) - if errGet != nil { - skipCreate = false - break // create the resources - } - for _, cond := range crd.Status.Conditions { - if cond.Type == apiextensionsv1beta1.Established && - cond.Status == apiextensionsv1beta1.ConditionTrue { - continue - } - - if cond.Type == apiextensionsv1beta1.NamesAccepted && - cond.Status == apiextensionsv1beta1.ConditionTrue { - continue - } - - log.Warnf("Not established: %v", name) - skipCreate = false - break - } - } - - if skipCreate { - return nil - } - - for _, schema := range rc.descriptor { - g := ResourceGroup(&schema) - name := ResourceName(schema.Plural) + "." + g - crdScope := apiextensionsv1beta1.NamespaceScoped - if schema.ClusterScoped { - crdScope = apiextensionsv1beta1.ClusterScoped - } - crd := &apiextensionsv1beta1.CustomResourceDefinition{ - ObjectMeta: meta_v1.ObjectMeta{ - Name: name, - }, - Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ - Group: g, - Version: schema.Version, - Scope: crdScope, - Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ - Plural: ResourceName(schema.Plural), - Kind: KebabCaseToCamelCase(schema.Type), - }, - }, - } - log.Infof("registering CRD %q", name) - _, err = cs.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd) - if err != nil && !apierrors.IsAlreadyExists(err) { - return err - } - } - - // wait for CRD being established - errPoll := wait.Poll(500*time.Millisecond, 60*time.Second, func() (bool, error) { - descriptor: - for _, schema := range rc.descriptor { - name := ResourceName(schema.Plural) + "." + ResourceGroup(&schema) - crd, errGet := cs.ApiextensionsV1beta1().CustomResourceDefinitions().Get(name, meta_v1.GetOptions{}) - if errGet != nil { - return false, errGet - } - for _, cond := range crd.Status.Conditions { - switch cond.Type { - case apiextensionsv1beta1.Established: - if cond.Status == apiextensionsv1beta1.ConditionTrue { - log.Infof("established CRD %q", name) - continue descriptor - } - case apiextensionsv1beta1.NamesAccepted: - if cond.Status == apiextensionsv1beta1.ConditionFalse { - log.Warnf("name conflict: %v", cond.Reason) - } - } - } - log.Infof("missing status condition for %q", name) - return false, nil - } - return true, nil - }) - - if errPoll != nil { - log.Error("failed to verify CRD creation") - return errPoll - } - - return nil -} - -// DeregisterResources removes third party resources -func (cl *Client) DeregisterResources() error { - for k, rc := range cl.clientset { - log.Infof("deregistering for apiVersion %s", k) - if err := rc.deregisterResources(); err != nil { - return err - } - } - return nil -} - -func (rc *restClient) deregisterResources() error { - cs, err := apiextensionsclient.NewForConfig(rc.restconfig) - if err != nil { - return err - } - - var errs error - for _, schema := range rc.descriptor { - name := ResourceName(schema.Plural) + "." + ResourceGroup(&schema) - err := cs.ApiextensionsV1beta1().CustomResourceDefinitions().Delete(name, nil) - errs = multierror.Append(errs, err) - } - return errs -} - -// ConfigDescriptor for the store -func (cl *Client) ConfigDescriptor() ConfigDescriptor { - d := make(ConfigDescriptor, 0, len(cl.clientset)) - for _, rc := range cl.clientset { - d = append(d, rc.descriptor...) - } - return d -} - -// Get implements store interface -func (cl *Client) Get(typ, name, namespace string) *Config { - s, ok := knownTypes[typ] - if !ok { - log.Warn("unknown type " + typ) - return nil - } - rc, ok := cl.clientset[apiVersion(&s.schema)] - if !ok { - log.Warn("cannot find client for type " + typ) - return nil - } - - schema, exists := rc.descriptor.GetByType(typ) - if !exists { - log.Warn("cannot find proto schema for type " + typ) - return nil - } - - config := s.object.DeepCopyObject().(IstioObject) - err := rc.dynamic.Get(). - Namespace(namespace). - Resource(ResourceName(schema.Plural)). - Name(name). - Do().Into(config) - - if err != nil { - log.Warna(err) - return nil - } - - out, err := ConvertObject(schema, config, cl.domainSuffix) - if err != nil { - log.Warna(err) - return nil - } - return out -} - -// Create implements store interface -func (cl *Client) Create(config Config) (string, error) { - rc, ok := cl.clientset[apiVersionFromConfig(&config)] - if !ok { - return "", fmt.Errorf("unrecognized apiVersion %q", config) - } - - schema, exists := rc.descriptor.GetByType(config.Type) - if !exists { - return "", fmt.Errorf("unrecognized type %q", config.Type) - } - - if err := schema.Validate(config.Name, config.Namespace, config.Spec); err != nil { - return "", multierror.Prefix(err, "validation error:") - } - - out, err := ConvertConfig(schema, config) - if err != nil { - return "", err - } - - obj := knownTypes[schema.Type].object.DeepCopyObject().(IstioObject) - err = rc.dynamic.Post(). - Namespace(out.GetObjectMeta().Namespace). - Resource(ResourceName(schema.Plural)). - Body(out). - Do().Into(obj) - if err != nil { - return "", err - } - - return obj.GetObjectMeta().ResourceVersion, nil -} - -// Update implements store interface -func (cl *Client) Update(config Config) (string, error) { - rc, ok := cl.clientset[apiVersionFromConfig(&config)] - if !ok { - return "", fmt.Errorf("unrecognized apiVersion %q", config) - } - schema, exists := rc.descriptor.GetByType(config.Type) - if !exists { - return "", fmt.Errorf("unrecognized type %q", config.Type) - } - - if err := schema.Validate(config.Name, config.Namespace, config.Spec); err != nil { - return "", multierror.Prefix(err, "validation error:") - } - - if config.ResourceVersion == "" { - return "", fmt.Errorf("revision is required") - } - - out, err := ConvertConfig(schema, config) - if err != nil { - return "", err - } - - obj := knownTypes[schema.Type].object.DeepCopyObject().(IstioObject) - err = rc.dynamic.Put(). - Namespace(out.GetObjectMeta().Namespace). - Resource(ResourceName(schema.Plural)). - Name(out.GetObjectMeta().Name). - Body(out). - Do().Into(obj) - if err != nil { - return "", err - } - - return obj.GetObjectMeta().ResourceVersion, nil -} - -// Delete implements store interface -func (cl *Client) Delete(typ, name, namespace string) error { - s, ok := knownTypes[typ] - if !ok { - return fmt.Errorf("unrecognized type %q", typ) - } - rc, ok := cl.clientset[apiVersion(&s.schema)] - if !ok { - return fmt.Errorf("unrecognized apiVersion %v", s.schema) - } - schema, exists := rc.descriptor.GetByType(typ) - if !exists { - return fmt.Errorf("missing type %q", typ) - } - - return rc.dynamic.Delete(). - Namespace(namespace). - Resource(ResourceName(schema.Plural)). - Name(name). - Do().Error() -} - -// List implements store interface -func (cl *Client) List(typ, namespace string) ([]Config, error) { - s, ok := knownTypes[typ] - if !ok { - return nil, fmt.Errorf("unrecognized type %q", typ) - } - rc, ok := cl.clientset[apiVersion(&s.schema)] - if !ok { - return nil, fmt.Errorf("unrecognized apiVersion %v", s.schema) - } - schema, exists := rc.descriptor.GetByType(typ) - if !exists { - return nil, fmt.Errorf("missing type %q", typ) - } - - list := knownTypes[schema.Type].collection.DeepCopyObject().(IstioObjectList) - errs := rc.dynamic.Get(). - Namespace(namespace). - Resource(ResourceName(schema.Plural)). - Do().Into(list) - - out := make([]Config, 0) - for _, item := range list.GetItems() { - obj, err := ConvertObject(schema, item, cl.domainSuffix) - if err != nil { - errs = multierror.Append(errs, err) - } else { - out = append(out, *obj) - } - } - return out, errs -} diff --git a/admiral/pkg/controller/istio/config.go b/admiral/pkg/controller/istio/config.go deleted file mode 100644 index 81b15287..00000000 --- a/admiral/pkg/controller/istio/config.go +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2017 Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istio - -import ( - "github.com/gogo/protobuf/proto" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "time" -) - -// IstioKind is the generic Kubernetes API object wrapper -type IstioKind struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec map[string]interface{} `json:"spec"` -} - -// GetSpec from a wrapper -func (in *IstioKind) GetSpec() map[string]interface{} { - return in.Spec -} - -// SetSpec for a wrapper -func (in *IstioKind) SetSpec(spec map[string]interface{}) { - in.Spec = spec -} - -// GetObjectMeta from a wrapper -func (in *IstioKind) GetObjectMeta() meta_v1.ObjectMeta { - return in.ObjectMeta -} - -// SetObjectMeta for a wrapper -func (in *IstioKind) SetObjectMeta(metadata meta_v1.ObjectMeta) { - in.ObjectMeta = metadata -} - -// IstioKindList is the generic Kubernetes API list wrapper -type IstioKindList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []IstioKind `json:"items"` -} - -// GetItems from a wrapper -func (in *IstioKindList) GetItems() []IstioObject { - out := make([]IstioObject, len(in.Items)) - for i := range in.Items { - out[i] = &in.Items[i] - } - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *IstioKind) DeepCopyInto(out *IstioKind) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IstioKind. -func (in *IstioKind) DeepCopy() *IstioKind { - if in == nil { - return nil - } - out := new(IstioKind) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *IstioKind) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *IstioKindList) DeepCopyInto(out *IstioKindList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]IstioKind, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IstioKindList. -func (in *IstioKindList) DeepCopy() *IstioKindList { - if in == nil { - return nil - } - out := new(IstioKindList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *IstioKindList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// Event represents a registry update event -type Event int - -const ( - // EventAdd is sent when an object is added - EventAdd Event = iota - - // EventUpdate is sent when an object is modified - // Captures the modified object - EventUpdate - - // EventDelete is sent when an object is deleted - // Captures the object at the last known state - EventDelete -) - -func (event Event) String() string { - out := "unknown" - switch event { - case EventAdd: - out = "add" - case EventUpdate: - out = "update" - case EventDelete: - out = "delete" - } - return out -} - -// ConfigMeta is metadata attached to each configuration unit. -// The revision is optional, and if provided, identifies the -// last update operation on the object. -type ConfigMeta struct { - // Type is a short configuration name that matches the content message type - // (e.g. "route-rule") - Type string `json:"type,omitempty"` - - // Group is the API group of the config. - Group string `json:"group,omitempty"` - - // Version is the API version of the Config. - Version string `json:"version,omitempty"` - - // Name is a unique immutable identifier in a namespace - Name string `json:"name,omitempty"` - - // Namespace defines the space for names (optional for some types), - // applications may choose to use namespaces for a variety of purposes - // (security domains, fault domains, organizational domains) - Namespace string `json:"namespace,omitempty"` - - // Domain defines the suffix of the fully qualified name past the namespace. - // Domain is not a part of the unique key unlike name and namespace. - Domain string `json:"domain,omitempty"` - - // Map of string keys and values that can be used to organize and categorize - // (scope and select) objects. - Labels map[string]string `json:"labels,omitempty"` - - // Annotations is an unstructured key value map stored with a resource that may be - // set by external tools to store and retrieve arbitrary metadata. They are not - // queryable and should be preserved when modifying objects. - Annotations map[string]string `json:"annotations,omitempty"` - - // ResourceVersion is an opaque identifier for tracking updates to the config registry. - // The implementation may use a change index or a commit log for the revision. - // The config client should not make any assumptions about revisions and rely only on - // exact equality to implement optimistic concurrency of read-write operations. - // - // The lifetime of an object of a particular revision depends on the underlying data store. - // The data store may compactify old revisions in the interest of storage optimization. - // - // An empty revision carries a special meaning that the associated object has - // not been stored and assigned a revision. - ResourceVersion string `json:"resourceVersion,omitempty"` - - // CreationTimestamp records the creation time - CreationTimestamp time.Time `json:"creationTimestamp,omitempty"` -} - -// Config is a configuration unit consisting of the type of configuration, the -// key identifier that is unique per type, and the content represented as a -// protobuf message. -type Config struct { - ConfigMeta - - // Spec holds the configuration object as a protobuf message - Spec proto.Message -} - -type ConfigStore interface { - // ConfigDescriptor exposes the configuration type schema known by the config store. - // The type schema defines the bidrectional mapping between configuration - // types and the protobuf encoding schema. - ConfigDescriptor() ConfigDescriptor - - // Get retrieves a configuration element by a type and a key - Get(typ, name, namespace string) *Config - - // List returns objects by type and namespace. - // Use "" for the namespace to list across namespaces. - List(typ, namespace string) ([]Config, error) - - // Create adds a new configuration object to the store. If an object with the - // same name and namespace for the type already exists, the operation fails - // with no side effects. - Create(config Config) (revision string, err error) - - // Update modifies an existing configuration object in the store. Update - // requires that the object has been created. Resource version prevents - // overriding a value that has been changed between prior _Get_ and _Put_ - // operation to achieve optimistic concurrency. This method returns a new - // revision if the operation succeeds. - Update(config Config) (newRevision string, err error) - - // Delete removes an object from the store by key - Delete(typ, name, namespace string) error -} - -type ConfigStoreCache interface { - ConfigStore - - // RegisterEventHandler adds a handler to receive config update events for a - // configuration type - RegisterEventHandler(typ string, handler func(Config, Event)) - - // Run until a signal is received - Run(stop <-chan struct{}) - - // HasSynced returns true after initial cache synchronization is complete - HasSynced() bool -} diff --git a/admiral/pkg/controller/istio/controller.go b/admiral/pkg/controller/istio/controller.go deleted file mode 100644 index 29cdb598..00000000 --- a/admiral/pkg/controller/istio/controller.go +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright 2017 Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istio - -import ( - "errors" - "fmt" - "reflect" - "sync" - "sync/atomic" - "time" - - "github.com/prometheus/client_golang/prometheus" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/tools/cache" - - "istio.io/istio/pilot/pkg/serviceregistry/kube" - "istio.io/istio/pkg/log" -) - -// controller is a collection of synchronized resource watchers. -// Caches are thread-safe -type controller struct { - client *Client - queue Queue - kinds map[string]cacheHandler -} - -type cacheHandler struct { - informer cache.SharedIndexInformer - handler *ChainHandler -} - -var ( - // experiment on getting some monitoring on config errors. - k8sEvents = prometheus.NewCounterVec(prometheus.CounterOpts{ - Name: "pilot_k8s_cfg_events", - Help: "Events from k8s config.", - }, []string{"type", "event"}) - - k8sErrors = prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Name: "pilot_k8s_object_errors", - Help: "Errors converting k8s CRDs", - }, []string{"name"}) - - // InvalidCRDs contains a sync.Map keyed by the namespace/name of the entry, and has the error as value. - // It can be used by tools like ctrlz to display the errors. - InvalidCRDs atomic.Value -) - -// NewController creates a new Kubernetes controller for CRDs -// Use "" for namespace to listen for all namespace changes -func NewController(client *Client, options kube.ControllerOptions) ConfigStoreCache { - log.Infof("CRD controller watching namespaces %q", options.WatchedNamespace) - - // Queue requires a time duration for a retry delay after a handler error - out := &controller{ - client: client, - queue: NewQueue(1 * time.Second), - kinds: make(map[string]cacheHandler), - } - - // add stores for CRD kinds - for _, schema := range client.ConfigDescriptor() { - out.addInformer(schema, options.WatchedNamespace, options.ResyncPeriod) - } - - return out -} - -func (c *controller) addInformer(schema ProtoSchema, namespace string, resyncPeriod time.Duration) { - c.kinds[schema.Type] = c.createInformer(knownTypes[schema.Type].object.DeepCopyObject(), schema.Type, resyncPeriod, - func(opts meta_v1.ListOptions) (result runtime.Object, err error) { - result = knownTypes[schema.Type].collection.DeepCopyObject() - rc, ok := c.client.clientset[apiVersion(&schema)] - if !ok { - return nil, fmt.Errorf("client not initialized %s", schema.Type) - } - req := rc.dynamic.Get(). - Resource(ResourceName(schema.Plural)). - VersionedParams(&opts, meta_v1.ParameterCodec) - - if !schema.ClusterScoped { - req = req.Namespace(namespace) - } - err = req.Do().Into(result) - return - }, - func(opts meta_v1.ListOptions) (watch.Interface, error) { - rc, ok := c.client.clientset[apiVersion(&schema)] - if !ok { - return nil, fmt.Errorf("client not initialized %s", schema.Type) - } - opts.Watch = true - req := rc.dynamic.Get(). - Resource(ResourceName(schema.Plural)). - VersionedParams(&opts, meta_v1.ParameterCodec) - if !schema.ClusterScoped { - req = req.Namespace(namespace) - } - return req.Watch() - }) -} - -// notify is the first handler in the handler chain. -// Returning an error causes repeated execution of the entire chain. -func (c *controller) notify(obj interface{}, event Event) error { - if !c.HasSynced() { - return errors.New("waiting till full synchronization") - } - _, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) - if err != nil { - log.Infof("Error retrieving key: %v", err) - } - return nil -} - -func (c *controller) createInformer( - o runtime.Object, - otype string, - resyncPeriod time.Duration, - lf cache.ListFunc, - wf cache.WatchFunc) cacheHandler { - handler := &ChainHandler{} - handler.Append(c.notify) - - // TODO: finer-grained index (perf) - informer := cache.NewSharedIndexInformer( - &cache.ListWatch{ListFunc: lf, WatchFunc: wf}, o, - resyncPeriod, cache.Indexers{}) - - informer.AddEventHandler( - cache.ResourceEventHandlerFuncs{ - // TODO: filtering functions to skip over un-referenced resources (perf) - AddFunc: func(obj interface{}) { - k8sEvents.With(prometheus.Labels{"type": otype, "event": "add"}).Add(1) - c.queue.Push(NewTask(handler.Apply, obj, EventAdd)) - }, - UpdateFunc: func(old, cur interface{}) { - if !reflect.DeepEqual(old, cur) { - k8sEvents.With(prometheus.Labels{"type": otype, "event": "update"}).Add(1) - c.queue.Push(NewTask(handler.Apply, cur, EventUpdate)) - } else { - k8sEvents.With(prometheus.Labels{"type": otype, "event": "updateSame"}).Add(1) - } - }, - DeleteFunc: func(obj interface{}) { - k8sEvents.With(prometheus.Labels{"type": otype, "event": "delete"}).Add(1) - c.queue.Push(NewTask(handler.Apply, obj, EventDelete)) - }, - }) - - return cacheHandler{informer: informer, handler: handler} -} - -func (c *controller) RegisterEventHandler(typ string, f func(Config, Event)) { - schema, exists := c.ConfigDescriptor().GetByType(typ) - if !exists { - return - } - c.kinds[typ].handler.Append(func(object interface{}, ev Event) error { - item, ok := object.(IstioObject) - if ok { - config, err := ConvertObject(schema, item, c.client.domainSuffix) - if err != nil { - log.Warnf("error translating object for schema %#v : %v\n Object:\n%#v", schema, err, object) - } else { - f(*config, ev) - } - } - return nil - }) -} - -func (c *controller) HasSynced() bool { - for kind, ctl := range c.kinds { - if !ctl.informer.HasSynced() { - log.Infof("controller %q is syncing...", kind) - return false - } - } - return true -} - -func (c *controller) Run(stop <-chan struct{}) { - go c.queue.Run(stop) - - for _, ctl := range c.kinds { - go ctl.informer.Run(stop) - } - - <-stop - log.Info("controller terminated") -} - -func (c *controller) ConfigDescriptor() ConfigDescriptor { - return c.client.ConfigDescriptor() -} - -func (c *controller) Get(typ, name, namespace string) *Config { - schema, exists := c.client.ConfigDescriptor().GetByType(typ) - if !exists { - return nil - } - - store := c.kinds[typ].informer.GetStore() - data, exists, err := store.GetByKey(kube.KeyFunc(name, namespace)) - if !exists { - return nil - } - if err != nil { - log.Warna(err) - return nil - } - - obj, ok := data.(IstioObject) - if !ok { - log.Warn("Cannot convert to config from store") - return nil - } - - config, err := ConvertObject(schema, obj, c.client.domainSuffix) - if err != nil { - return nil - } - - return config -} - -func (c *controller) Create(config Config) (string, error) { - return c.client.Create(config) -} - -func (c *controller) Update(config Config) (string, error) { - return c.client.Update(config) -} - -func (c *controller) Delete(typ, name, namespace string) error { - return c.client.Delete(typ, name, namespace) -} - -func (c *controller) List(typ, namespace string) ([]Config, error) { - schema, ok := c.client.ConfigDescriptor().GetByType(typ) - if !ok { - return nil, fmt.Errorf("missing type %q", typ) - } - - var newErrors sync.Map - var errs error - out := make([]Config, 0) - oldMap := InvalidCRDs.Load() - if oldMap != nil { - oldMap.(*sync.Map).Range(func(key, value interface{}) bool { - k8sErrors.With(prometheus.Labels{"name": key.(string)}).Set(1) - return true - }) - } - for _, data := range c.kinds[typ].informer.GetStore().List() { - item, ok := data.(IstioObject) - if !ok { - continue - } - - if namespace != "" && namespace != item.GetObjectMeta().Namespace { - continue - } - - config, err := ConvertObject(schema, item, c.client.domainSuffix) - if err != nil { - key := item.GetObjectMeta().Namespace + "/" + item.GetObjectMeta().Name - log.Errorf("Failed to convert %s object, ignoring: %s %v %v", typ, key, err, item.GetSpec()) - // DO NOT RETURN ERROR: if a single object is bad, it'll be ignored (with a log message), but - // the rest should still be processed. - // TODO: find a way to reset and represent the error !! - newErrors.Store(key, err) - k8sErrors.With(prometheus.Labels{"name": key}).Set(1) - } else { - out = append(out, *config) - } - } - InvalidCRDs.Store(&newErrors) - return out, errs -} diff --git a/admiral/pkg/controller/istio/conversion.go b/admiral/pkg/controller/istio/conversion.go deleted file mode 100644 index d1884e48..00000000 --- a/admiral/pkg/controller/istio/conversion.go +++ /dev/null @@ -1,366 +0,0 @@ -// Copyright 2017 Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istio - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "github.com/ghodss/yaml" - "github.com/gogo/protobuf/jsonpb" - "github.com/gogo/protobuf/proto" - multierror "github.com/hashicorp/go-multierror" - yaml2 "gopkg.in/yaml.v2" - - "io" - "reflect" - "strings" - - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - kubeyaml "k8s.io/apimachinery/pkg/util/yaml" - - "istio.io/istio/pilot/pkg/model" - "istio.io/istio/pkg/log" -) - -// ConvertObject converts an IstioObject k8s-style object to the -// internal configuration model. -func ConvertObject(schema ProtoSchema, object IstioObject, domain string) (*Config, error) { - data, err := schema.FromJSONMap(object.GetSpec()) - if err != nil { - return nil, err - } - meta := object.GetObjectMeta() - - return &Config{ - ConfigMeta: ConfigMeta{ - Type: schema.Type, - Group: ResourceGroup(&schema), - Version: schema.Version, - Name: meta.Name, - Namespace: meta.Namespace, - Domain: domain, - Labels: meta.Labels, - Annotations: meta.Annotations, - ResourceVersion: meta.ResourceVersion, - CreationTimestamp: meta.CreationTimestamp.Time, - }, - Spec: data, - }, nil -} - -// ConvertObject converts an k8s-style object to the -// internal configuration model. -func ConvertIstioType(schema ProtoSchema, object proto.Message, name string, namespace string) (*Config, error) { - - return &Config{ - ConfigMeta: ConfigMeta{ - Type: schema.Type, - Group: ResourceGroup(&schema), - Version: schema.Version, - Name: name, - Namespace: namespace, - }, - Spec: object, - }, nil -} - -// ConvertObjectFromUnstructured converts an IstioObject k8s-style object to the -// internal configuration model. -func ConvertObjectFromUnstructured(schema ProtoSchema, un *unstructured.Unstructured, domain string) (*model.Config, error) { - data, err := schema.FromJSONMap(un.Object["spec"]) - if err != nil { - return nil, err - } - - return &model.Config{ - ConfigMeta: model.ConfigMeta{ - Type: schema.Type, - Group: ResourceGroup(&schema), - Version: schema.Version, - Name: un.GetName(), - Namespace: un.GetNamespace(), - Domain: domain, - Labels: un.GetLabels(), - Annotations: un.GetAnnotations(), - ResourceVersion: un.GetResourceVersion(), - CreationTimestamp: un.GetCreationTimestamp().Time, - }, - Spec: data, - }, nil -} - -// ConvertConfig translates Istio config to k8s config JSON -func ConvertConfig(schema ProtoSchema, config Config) (IstioObject, error) { - spec, err := model.ToJSONMap(config.Spec) - if err != nil { - return nil, err - } - namespace := config.Namespace - if namespace == "" { - namespace = meta_v1.NamespaceDefault - } - out := knownTypes[schema.Type].object.DeepCopyObject().(IstioObject) - out.SetObjectMeta(meta_v1.ObjectMeta{ - Name: config.Name, - Namespace: namespace, - ResourceVersion: config.ResourceVersion, - Labels: config.Labels, - Annotations: config.Annotations, - }) - out.SetSpec(spec) - - return out, nil -} - -// ResourceName converts "my-name" to "myname". -// This is needed by k8s API server as dashes prevent kubectl from accessing CRDs -func ResourceName(s string) string { - return strings.Replace(s, "-", "", -1) -} - -// ResourceGroup generates the k8s API group for each schema. -func ResourceGroup(schema *ProtoSchema) string { - return schema.Group + model.IstioAPIGroupDomain -} - -// TODO - add special cases for type-to-kind and kind-to-type -// conversions with initial-isms. Consider adding additional type -// information to the abstract model and/or elevating k8s -// representation to first-class type to avoid extra conversions. - -// KebabCaseToCamelCase converts "my-name" to "MyName" -func KebabCaseToCamelCase(s string) string { - switch s { - case "http-api-spec": - return "HTTPAPISpec" - case "http-api-spec-binding": - return "HTTPAPISpecBinding" - default: - words := strings.Split(s, "-") - out := "" - for _, word := range words { - out = out + strings.Title(word) - } - return out - } -} - -// CamelCaseToKebabCase converts "MyName" to "my-name" -func CamelCaseToKebabCase(s string) string { - switch s { - case "HTTPAPISpec": - return "http-api-spec" - case "HTTPAPISpecBinding": - return "http-api-spec-binding" - default: - var out bytes.Buffer - for i := range s { - if 'A' <= s[i] && s[i] <= 'Z' { - if i > 0 { - out.WriteByte('-') - } - out.WriteByte(s[i] - 'A' + 'a') - } else { - out.WriteByte(s[i]) - } - } - return out.String() - } -} - -func parseInputsImpl(inputs string, withValidate bool) ([]Config, []IstioKind, error) { - var varr []Config - var others []IstioKind - reader := bytes.NewReader([]byte(inputs)) - var empty = IstioKind{} - - // We store configs as a YaML stream; there may be more than one decoder. - yamlDecoder := kubeyaml.NewYAMLOrJSONDecoder(reader, 512*1024) - for { - obj := IstioKind{} - err := yamlDecoder.Decode(&obj) - if err == io.EOF { - break - } - if err != nil { - return nil, nil, fmt.Errorf("cannot parse proto message: %v", err) - } - if reflect.DeepEqual(obj, empty) { - continue - } - - schema, exists := IstioConfigTypes.GetByType(CamelCaseToKebabCase(obj.Kind)) - if !exists { - log.Debugf("unrecognized type %v", obj.Kind) - others = append(others, obj) - continue - } - - config, err := ConvertObject(schema, &obj, "") - if err != nil { - return nil, nil, fmt.Errorf("cannot parse proto message: %v", err) - } - - if withValidate { - if err := schema.Validate(config.Name, config.Namespace, config.Spec); err != nil { - return nil, nil, fmt.Errorf("configuration is invalid: %v", err) - } - } - - varr = append(varr, *config) - } - - return varr, others, nil -} - -// ParseInputs reads multiple documents from `kubectl` output and checks with -// the schema. It also returns the list of unrecognized kinds as the second -// response. -// -// NOTE: This function only decodes a subset of the complete k8s -// ObjectMeta as identified by the fields in model.ConfigMeta. This -// would typically only be a problem if a user dumps an configuration -// object with kubectl and then re-ingests it. -func ParseInputs(inputs string) ([]Config, []IstioKind, error) { - return parseInputsImpl(inputs, true) -} - -// ParseInputsWithoutValidation same as ParseInputs, but do not apply schema validation. -func ParseInputsWithoutValidation(inputs string) ([]Config, []IstioKind, error) { - return parseInputsImpl(inputs, false) -} - -// Make creates a new instance of the proto message -func (ps *ProtoSchema) Make() (proto.Message, error) { - pbt := proto.MessageType(ps.MessageName) - if pbt == nil { - return nil, fmt.Errorf("unknown type %q", ps.MessageName) - } - return reflect.New(pbt.Elem()).Interface().(proto.Message), nil -} - -// ToJSON marshals a proto to canonical JSON -func ToJSON(msg proto.Message) (string, error) { - return ToJSONWithIndent(msg, "") -} - -// ToJSONWithIndent marshals a proto to canonical JSON with pretty printed string -func ToJSONWithIndent(msg proto.Message, indent string) (string, error) { - if msg == nil { - return "", errors.New("unexpected nil message") - } - - // Marshal from proto to json bytes - m := jsonpb.Marshaler{Indent: indent} - return m.MarshalToString(msg) -} - -// ToYAML marshals a proto to canonical YAML -func ToYAML(msg proto.Message) (string, error) { - js, err := ToJSON(msg) - if err != nil { - return "", err - } - yml, err := yaml.JSONToYAML([]byte(js)) - return string(yml), err -} - -// ToJSONMap converts a proto message to a generic map using canonical JSON encoding -// JSON encoding is specified here: https://developers.google.com/protocol-buffers/docs/proto3#json -func ToJSONMap(msg proto.Message) (map[string]interface{}, error) { - js, err := ToJSON(msg) - if err != nil { - return nil, err - } - - // Unmarshal from json bytes to go map - var data map[string]interface{} - err = json.Unmarshal([]byte(js), &data) - if err != nil { - return nil, err - } - - return data, nil -} - -// FromJSON converts a canonical JSON to a proto message -func (ps *ProtoSchema) FromJSON(js string) (proto.Message, error) { - pb, err := ps.Make() - if err != nil { - return nil, err - } - if err = ApplyJSON(js, pb, true); err != nil { - return nil, err - } - return pb, nil -} - -// ApplyJSON unmarshals a JSON string into a proto message. Unknown fields will produce an -//// error unless strict is set to false. -func ApplyJSON(js string, pb proto.Message, strict bool) error { - reader := strings.NewReader(js) - m := jsonpb.Unmarshaler{} - if err := m.Unmarshal(reader, pb); err != nil { - if strict { - return err - } - - log.Warnf("Failed to decode proto: %q. Trying decode with AllowUnknownFields=true", err) - m.AllowUnknownFields = true - reader.Reset(js) - return m.Unmarshal(reader, pb) - } - return nil -} - -// FromYAML converts a canonical YAML to a proto message -func (ps *ProtoSchema) FromYAML(yml string) (proto.Message, error) { - pb, err := ps.Make() - if err != nil { - return nil, err - } - if err = ApplyYAML(yml, pb, true); err != nil { - return nil, err - } - return pb, nil -} - -// ApplyYAML unmarshals a YAML string into a proto message. Unknown fields will produce an -// error unless strict is set to false. -func ApplyYAML(yml string, pb proto.Message, strict bool) error { - js, err := yaml.YAMLToJSON([]byte(yml)) - if err != nil { - return err - } - return ApplyJSON(string(js), pb, strict) -} - -// FromJSONMap converts from a generic map to a proto message using canonical JSON encoding -// JSON encoding is specified here: https://developers.google.com/protocol-buffers/docs/proto3#json -func (ps *ProtoSchema) FromJSONMap(data interface{}) (proto.Message, error) { - // Marshal to YAML bytes - str, err := yaml2.Marshal(data) - if err != nil { - return nil, err - } - out, err := ps.FromYAML(string(str)) - if err != nil { - return nil, multierror.Prefix(err, fmt.Sprintf("YAML decoding error: %v", string(str))) - } - return out, nil -} diff --git a/admiral/pkg/controller/istio/destinationrule.go b/admiral/pkg/controller/istio/destinationrule.go new file mode 100644 index 00000000..457aaddd --- /dev/null +++ b/admiral/pkg/controller/istio/destinationrule.go @@ -0,0 +1,75 @@ +package istio + +import ( + "fmt" + "time" + + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/admiral" + networking "istio.io/client-go/pkg/apis/networking/v1alpha3" + versioned "istio.io/client-go/pkg/clientset/versioned" + informers "istio.io/client-go/pkg/informers/externalversions/networking/v1alpha3" + k8sV1 "k8s.io/api/core/v1" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/cache" +) + +// Handler interface contains the methods that are required +type DestinationRuleHandler interface { + Added(obj *networking.DestinationRule) + Updated(obj *networking.DestinationRule) + Deleted(obj *networking.DestinationRule) +} + +type DestinationRuleEntry struct { + Identity string + DestinationRule *networking.DestinationRule +} + +type DestinationRuleController struct { + IstioClient *versioned.Clientset + DestinationRuleHandler DestinationRuleHandler + informer cache.SharedIndexInformer + ctl *admiral.Controller +} + +func (d *ServiceEntryController) GetDestinationRules() ([]*networking.DestinationRule, error) { + //TODO + return nil, nil +} + +func NewDestinationRuleController(stopCh <-chan struct{}, handler DestinationRuleHandler, config *rest.Config, resyncPeriod time.Duration) (*DestinationRuleController, error) { + + drController := DestinationRuleController{} + drController.DestinationRuleHandler = handler + + var err error + + ic, err := versioned.NewForConfig(config) + if err != nil { + return nil, fmt.Errorf("failed to create destination rule controller k8s client: %v", err) + } + + drController.IstioClient = ic + + drController.informer = informers.NewDestinationRuleInformer(ic, k8sV1.NamespaceAll, resyncPeriod, cache.Indexers{}) + + admiral.NewController(stopCh, &drController, drController.informer) + + return &drController, nil +} + +func (sec *DestinationRuleController) Added(ojb interface{}) { + dr := ojb.(*networking.DestinationRule) + sec.DestinationRuleHandler.Added(dr) +} + +func (sec *DestinationRuleController) Updated(ojb interface{}) { + dr := ojb.(*networking.DestinationRule) + sec.DestinationRuleHandler.Added(dr) +} + +func (sec *DestinationRuleController) Deleted(ojb interface{}) { + dr := ojb.(*networking.DestinationRule) + sec.DestinationRuleHandler.Added(dr) + +} diff --git a/admiral/pkg/controller/istio/queue.go b/admiral/pkg/controller/istio/queue.go deleted file mode 100644 index 5b902c1e..00000000 --- a/admiral/pkg/controller/istio/queue.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2017 Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package istio - -import ( - "sync" - "time" - - "istio.io/istio/pkg/log" -) - -// Queue of work tickets processed using a rate-limiting loop -type Queue interface { - // Push a ticket - Push(Task) - // Run the loop until a signal on the channel - Run(<-chan struct{}) -} - -// Handler specifies a function to apply on an object for a given event type -type Handler func(obj interface{}, event Event) error - -// Task object for the event watchers; processes until handler succeeds -type Task struct { - handler Handler - obj interface{} - event Event -} - -// NewTask creates a task from a work item -func NewTask(handler Handler, obj interface{}, event Event) Task { - return Task{handler: handler, obj: obj, event: event} -} - -type queueImpl struct { - delay time.Duration - queue []Task - cond *sync.Cond - closing bool -} - -// NewQueue instantiates a queue with a processing function -func NewQueue(errorDelay time.Duration) Queue { - return &queueImpl{ - delay: errorDelay, - queue: make([]Task, 0), - closing: false, - cond: sync.NewCond(&sync.Mutex{}), - } -} - -func (q *queueImpl) Push(item Task) { - q.cond.L.Lock() - defer q.cond.L.Unlock() - if !q.closing { - q.queue = append(q.queue, item) - } - q.cond.Signal() -} - -func (q *queueImpl) Run(stop <-chan struct{}) { - go func() { - <-stop - q.cond.L.Lock() - q.closing = true - q.cond.L.Unlock() - }() - - for { - q.cond.L.Lock() - for !q.closing && len(q.queue) == 0 { - q.cond.Wait() - } - - if len(q.queue) == 0 { - q.cond.L.Unlock() - // We must be shutting down. - return - } - - var item Task - item, q.queue = q.queue[0], q.queue[1:] - q.cond.L.Unlock() - - if err := item.handler(item.obj, item.event); err != nil { - log.Infof("Work item handle failed (%v), retry after delay %v", err, q.delay) - time.AfterFunc(q.delay, func() { - q.Push(item) - }) - } - - } -} - -// ChainHandler applies handlers in a sequence -type ChainHandler struct { - funcs []Handler -} - -// Apply is the handler function -func (ch *ChainHandler) Apply(obj interface{}, event Event) error { - for _, f := range ch.funcs { - if err := f(obj, event); err != nil { - return err - } - } - return nil -} - -// Append a handler as the last handler in the chain -func (ch *ChainHandler) Append(h Handler) { - ch.funcs = append(ch.funcs, h) -} diff --git a/admiral/pkg/controller/istio/serviceentry.go b/admiral/pkg/controller/istio/serviceentry.go new file mode 100644 index 00000000..db769dbb --- /dev/null +++ b/admiral/pkg/controller/istio/serviceentry.go @@ -0,0 +1,75 @@ +package istio + +import ( + "fmt" + "time" + + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/admiral" + networking "istio.io/client-go/pkg/apis/networking/v1alpha3" + versioned "istio.io/client-go/pkg/clientset/versioned" + informers "istio.io/client-go/pkg/informers/externalversions/networking/v1alpha3" + k8sV1 "k8s.io/api/core/v1" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/cache" +) + +// Handler interface contains the methods that are required +type ServiceEntryHandler interface { + Added(obj *networking.ServiceEntry) + Updated(obj *networking.ServiceEntry) + Deleted(obj *networking.ServiceEntry) +} + +type ServiceEntryEntry struct { + Identity string + ServiceEntry *networking.ServiceEntry +} + +type ServiceEntryController struct { + IstioClient *versioned.Clientset + ServiceEntryHandler ServiceEntryHandler + informer cache.SharedIndexInformer + ctl *admiral.Controller +} + +func (d *ServiceEntryController) GetServiceEntries() ([]*networking.ServiceEntry, error) { + //TODO + return nil, nil +} + +func NewServiceEntryController(stopCh <-chan struct{}, handler ServiceEntryHandler, config *rest.Config, resyncPeriod time.Duration) (*ServiceEntryController, error) { + + seController := ServiceEntryController{} + seController.ServiceEntryHandler = handler + + var err error + + ic, err := versioned.NewForConfig(config) + if err != nil { + return nil, fmt.Errorf("failed to create service entry k8s client: %v", err) + } + + seController.IstioClient = ic + + seController.informer = informers.NewServiceEntryInformer(ic, k8sV1.NamespaceAll, resyncPeriod, cache.Indexers{}) + + admiral.NewController(stopCh, &seController, seController.informer) + + return &seController, nil +} + +func (sec *ServiceEntryController) Added(ojb interface{}) { + se := ojb.(*networking.ServiceEntry) + sec.ServiceEntryHandler.Added(se) +} + +func (sec *ServiceEntryController) Updated(ojb interface{}) { + se := ojb.(*networking.ServiceEntry) + sec.ServiceEntryHandler.Added(se) +} + +func (sec *ServiceEntryController) Deleted(ojb interface{}) { + se := ojb.(*networking.ServiceEntry) + sec.ServiceEntryHandler.Added(se) + +} diff --git a/admiral/pkg/controller/istio/types.go b/admiral/pkg/controller/istio/types.go deleted file mode 100644 index f16ec97b..00000000 --- a/admiral/pkg/controller/istio/types.go +++ /dev/null @@ -1,1959 +0,0 @@ -// Copyright 2018 Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by pilot/tools/generate_config_crd_types.go. DO NOT EDIT! - -package istio - -// This file contains Go definitions for Custom Resource Definition kinds -// to adhere to the idiomatic use of k8s API machinery. -// These definitions are synthesized from Istio configuration type descriptors -// as declared in the Pilot config model. - -import ( - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - - "istio.io/istio/pilot/pkg/model" -) - -type schemaType struct { - schema ProtoSchema - object IstioObject - collection IstioObjectList -} - -var knownTypes = map[string]schemaType{ - model.MockConfig.Type: { - schema: MockConfigProto, - object: &MockConfig{ - TypeMeta: meta_v1.TypeMeta{ - Kind: "MockConfig", - APIVersion: apiVersion(&MockConfigProto), - }, - }, - collection: &MockConfigList{}, - }, - model.VirtualService.Type: { - schema: VirtualServiceProto, - object: &VirtualService{ - TypeMeta: meta_v1.TypeMeta{ - Kind: "VirtualService", - APIVersion: apiVersion(&VirtualServiceProto), - }, - }, - collection: &VirtualServiceList{}, - }, - model.Gateway.Type: { - schema: GatewayProto, - object: &Gateway{ - TypeMeta: meta_v1.TypeMeta{ - Kind: "Gateway", - APIVersion: apiVersion(&GatewayProto), - }, - }, - collection: &GatewayList{}, - }, - model.ServiceEntry.Type: { - schema: ServiceEntryProto, - object: &ServiceEntry{ - TypeMeta: meta_v1.TypeMeta{ - Kind: "ServiceEntry", - APIVersion: apiVersion(&ServiceEntryProto), - }, - }, - collection: &ServiceEntryList{}, - }, - model.DestinationRule.Type: { - schema: DestinationRuleProto, - object: &DestinationRule{ - TypeMeta: meta_v1.TypeMeta{ - Kind: "DestinationRule", - APIVersion: apiVersion(&DestinationRuleProto), - }, - }, - collection: &DestinationRuleList{}, - }, - model.EnvoyFilter.Type: { - schema: EnvoyFilterProto, - object: &EnvoyFilter{ - TypeMeta: meta_v1.TypeMeta{ - Kind: "EnvoyFilter", - APIVersion: apiVersion(&EnvoyFilterProto), - }, - }, - collection: &EnvoyFilterList{}, - }, - model.Sidecar.Type: { - schema: SidecarProto, - object: &Sidecar{ - TypeMeta: meta_v1.TypeMeta{ - Kind: "Sidecar", - APIVersion: apiVersion(&SidecarProto), - }, - }, - collection: &SidecarList{}, - }, - //model.HTTPAPISpec.Type: { - // schema: model.HTTPAPISpec, - // object: &HTTPAPISpec{ - // TypeMeta: meta_v1.TypeMeta{ - // Kind: "HTTPAPISpec", - // APIVersion: apiVersion(&model.HTTPAPISpec), - // }, - // }, - // collection: &HTTPAPISpecList{}, - //}, - //model.HTTPAPISpecBinding.Type: { - // schema: model.HTTPAPISpecBinding, - // object: &HTTPAPISpecBinding{ - // TypeMeta: meta_v1.TypeMeta{ - // Kind: "HTTPAPISpecBinding", - // APIVersion: apiVersion(&model.HTTPAPISpecBinding), - // }, - // }, - // collection: &HTTPAPISpecBindingList{}, - //}, - //model.QuotaSpec.Type: { - // schema: model.QuotaSpec, - // object: &QuotaSpec{ - // TypeMeta: meta_v1.TypeMeta{ - // Kind: "QuotaSpec", - // APIVersion: apiVersion(&model.QuotaSpec), - // }, - // }, - // collection: &QuotaSpecList{}, - //}, - //model.QuotaSpecBinding.Type: { - // schema: model.QuotaSpecBinding, - // object: &QuotaSpecBinding{ - // TypeMeta: meta_v1.TypeMeta{ - // Kind: "QuotaSpecBinding", - // APIVersion: apiVersion(&model.QuotaSpecBinding), - // }, - // }, - // collection: &QuotaSpecBindingList{}, - //}, - //model.AuthenticationPolicy.Type: { - // schema: model.AuthenticationPolicy, - // object: &Policy{ - // TypeMeta: meta_v1.TypeMeta{ - // Kind: "Policy", - // APIVersion: apiVersion(&model.AuthenticationPolicy), - // }, - // }, - // collection: &PolicyList{}, - //}, - //model.AuthenticationMeshPolicy.Type: { - // schema: model.AuthenticationMeshPolicy, - // object: &MeshPolicy{ - // TypeMeta: meta_v1.TypeMeta{ - // Kind: "MeshPolicy", - // APIVersion: apiVersion(&model.AuthenticationMeshPolicy), - // }, - // }, - // collection: &MeshPolicyList{}, - //}, - //model.ServiceRole.Type: { - // schema: model.ServiceRole, - // object: &ServiceRole{ - // TypeMeta: meta_v1.TypeMeta{ - // Kind: "ServiceRole", - // APIVersion: apiVersion(&model.ServiceRole), - // }, - // }, - // collection: &ServiceRoleList{}, - //}, - //model.ServiceRoleBinding.Type: { - // schema: model.ServiceRoleBinding, - // object: &ServiceRoleBinding{ - // TypeMeta: meta_v1.TypeMeta{ - // Kind: "ServiceRoleBinding", - // APIVersion: apiVersion(&model.ServiceRoleBinding), - // }, - // }, - // collection: &ServiceRoleBindingList{}, - //}, - //model.RbacConfig.Type: { - // schema: model.RbacConfig, - // object: &RbacConfig{ - // TypeMeta: meta_v1.TypeMeta{ - // Kind: "RbacConfig", - // APIVersion: apiVersion(&model.RbacConfig), - // }, - // }, - // collection: &RbacConfigList{}, - //}, - //model.ClusterRbacConfig.Type: { - // schema: model.ClusterRbacConfig, - // object: &ClusterRbacConfig{ - // TypeMeta: meta_v1.TypeMeta{ - // Kind: "ClusterRbacConfig", - // APIVersion: apiVersion(&model.ClusterRbacConfig), - // }, - // }, - // collection: &ClusterRbacConfigList{}, - //}, -} - -// MockConfig is the generic Kubernetes API object wrapper -type MockConfig struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec map[string]interface{} `json:"spec"` -} - -// GetSpec from a wrapper -func (in *MockConfig) GetSpec() map[string]interface{} { - return in.Spec -} - -// SetSpec for a wrapper -func (in *MockConfig) SetSpec(spec map[string]interface{}) { - in.Spec = spec -} - -// GetObjectMeta from a wrapper -func (in *MockConfig) GetObjectMeta() meta_v1.ObjectMeta { - return in.ObjectMeta -} - -// SetObjectMeta for a wrapper -func (in *MockConfig) SetObjectMeta(metadata meta_v1.ObjectMeta) { - in.ObjectMeta = metadata -} - -// MockConfigList is the generic Kubernetes API list wrapper -type MockConfigList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []MockConfig `json:"items"` -} - -// GetItems from a wrapper -func (in *MockConfigList) GetItems() []IstioObject { - out := make([]IstioObject, len(in.Items)) - for i := range in.Items { - out[i] = &in.Items[i] - } - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MockConfig) DeepCopyInto(out *MockConfig) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MockConfig. -func (in *MockConfig) DeepCopy() *MockConfig { - if in == nil { - return nil - } - out := new(MockConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *MockConfig) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MockConfigList) DeepCopyInto(out *MockConfigList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]MockConfig, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MockConfigList. -func (in *MockConfigList) DeepCopy() *MockConfigList { - if in == nil { - return nil - } - out := new(MockConfigList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *MockConfigList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// VirtualService is the generic Kubernetes API object wrapper -type VirtualService struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec map[string]interface{} `json:"spec"` -} - -// GetSpec from a wrapper -func (in *VirtualService) GetSpec() map[string]interface{} { - return in.Spec -} - -// SetSpec for a wrapper -func (in *VirtualService) SetSpec(spec map[string]interface{}) { - in.Spec = spec -} - -// GetObjectMeta from a wrapper -func (in *VirtualService) GetObjectMeta() meta_v1.ObjectMeta { - return in.ObjectMeta -} - -// SetObjectMeta for a wrapper -func (in *VirtualService) SetObjectMeta(metadata meta_v1.ObjectMeta) { - in.ObjectMeta = metadata -} - -// VirtualServiceList is the generic Kubernetes API list wrapper -type VirtualServiceList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []VirtualService `json:"items"` -} - -// GetItems from a wrapper -func (in *VirtualServiceList) GetItems() []IstioObject { - out := make([]IstioObject, len(in.Items)) - for i := range in.Items { - out[i] = &in.Items[i] - } - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VirtualService) DeepCopyInto(out *VirtualService) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualService. -func (in *VirtualService) DeepCopy() *VirtualService { - if in == nil { - return nil - } - out := new(VirtualService) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *VirtualService) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VirtualServiceList) DeepCopyInto(out *VirtualServiceList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]VirtualService, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualServiceList. -func (in *VirtualServiceList) DeepCopy() *VirtualServiceList { - if in == nil { - return nil - } - out := new(VirtualServiceList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *VirtualServiceList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// Gateway is the generic Kubernetes API object wrapper -type Gateway struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec map[string]interface{} `json:"spec"` -} - -// GetSpec from a wrapper -func (in *Gateway) GetSpec() map[string]interface{} { - return in.Spec -} - -// SetSpec for a wrapper -func (in *Gateway) SetSpec(spec map[string]interface{}) { - in.Spec = spec -} - -// GetObjectMeta from a wrapper -func (in *Gateway) GetObjectMeta() meta_v1.ObjectMeta { - return in.ObjectMeta -} - -// SetObjectMeta for a wrapper -func (in *Gateway) SetObjectMeta(metadata meta_v1.ObjectMeta) { - in.ObjectMeta = metadata -} - -// GatewayList is the generic Kubernetes API list wrapper -type GatewayList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []Gateway `json:"items"` -} - -// GetItems from a wrapper -func (in *GatewayList) GetItems() []IstioObject { - out := make([]IstioObject, len(in.Items)) - for i := range in.Items { - out[i] = &in.Items[i] - } - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Gateway) DeepCopyInto(out *Gateway) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Gateway. -func (in *Gateway) DeepCopy() *Gateway { - if in == nil { - return nil - } - out := new(Gateway) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Gateway) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *GatewayList) DeepCopyInto(out *GatewayList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Gateway, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayList. -func (in *GatewayList) DeepCopy() *GatewayList { - if in == nil { - return nil - } - out := new(GatewayList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *GatewayList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// ServiceEntry is the generic Kubernetes API object wrapper -type ServiceEntry struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec map[string]interface{} `json:"spec"` -} - -// GetSpec from a wrapper -func (in *ServiceEntry) GetSpec() map[string]interface{} { - return in.Spec -} - -// SetSpec for a wrapper -func (in *ServiceEntry) SetSpec(spec map[string]interface{}) { - in.Spec = spec -} - -// GetObjectMeta from a wrapper -func (in *ServiceEntry) GetObjectMeta() meta_v1.ObjectMeta { - return in.ObjectMeta -} - -// SetObjectMeta for a wrapper -func (in *ServiceEntry) SetObjectMeta(metadata meta_v1.ObjectMeta) { - in.ObjectMeta = metadata -} - -// ServiceEntryList is the generic Kubernetes API list wrapper -type ServiceEntryList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []ServiceEntry `json:"items"` -} - -// GetItems from a wrapper -func (in *ServiceEntryList) GetItems() []IstioObject { - out := make([]IstioObject, len(in.Items)) - for i := range in.Items { - out[i] = &in.Items[i] - } - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ServiceEntry) DeepCopyInto(out *ServiceEntry) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceEntry. -func (in *ServiceEntry) DeepCopy() *ServiceEntry { - if in == nil { - return nil - } - out := new(ServiceEntry) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ServiceEntry) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ServiceEntryList) DeepCopyInto(out *ServiceEntryList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]ServiceEntry, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceEntryList. -func (in *ServiceEntryList) DeepCopy() *ServiceEntryList { - if in == nil { - return nil - } - out := new(ServiceEntryList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ServiceEntryList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// DestinationRule is the generic Kubernetes API object wrapper -type DestinationRule struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec map[string]interface{} `json:"spec"` -} - -// GetSpec from a wrapper -func (in *DestinationRule) GetSpec() map[string]interface{} { - return in.Spec -} - -// SetSpec for a wrapper -func (in *DestinationRule) SetSpec(spec map[string]interface{}) { - in.Spec = spec -} - -// GetObjectMeta from a wrapper -func (in *DestinationRule) GetObjectMeta() meta_v1.ObjectMeta { - return in.ObjectMeta -} - -// SetObjectMeta for a wrapper -func (in *DestinationRule) SetObjectMeta(metadata meta_v1.ObjectMeta) { - in.ObjectMeta = metadata -} - -// DestinationRuleList is the generic Kubernetes API list wrapper -type DestinationRuleList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []DestinationRule `json:"items"` -} - -// GetItems from a wrapper -func (in *DestinationRuleList) GetItems() []IstioObject { - out := make([]IstioObject, len(in.Items)) - for i := range in.Items { - out[i] = &in.Items[i] - } - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DestinationRule) DeepCopyInto(out *DestinationRule) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DestinationRule. -func (in *DestinationRule) DeepCopy() *DestinationRule { - if in == nil { - return nil - } - out := new(DestinationRule) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DestinationRule) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DestinationRuleList) DeepCopyInto(out *DestinationRuleList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]DestinationRule, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DestinationRuleList. -func (in *DestinationRuleList) DeepCopy() *DestinationRuleList { - if in == nil { - return nil - } - out := new(DestinationRuleList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DestinationRuleList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// EnvoyFilter is the generic Kubernetes API object wrapper -type EnvoyFilter struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec map[string]interface{} `json:"spec"` -} - -// GetSpec from a wrapper -func (in *EnvoyFilter) GetSpec() map[string]interface{} { - return in.Spec -} - -// SetSpec for a wrapper -func (in *EnvoyFilter) SetSpec(spec map[string]interface{}) { - in.Spec = spec -} - -// GetObjectMeta from a wrapper -func (in *EnvoyFilter) GetObjectMeta() meta_v1.ObjectMeta { - return in.ObjectMeta -} - -// SetObjectMeta for a wrapper -func (in *EnvoyFilter) SetObjectMeta(metadata meta_v1.ObjectMeta) { - in.ObjectMeta = metadata -} - -// EnvoyFilterList is the generic Kubernetes API list wrapper -type EnvoyFilterList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []EnvoyFilter `json:"items"` -} - -// GetItems from a wrapper -func (in *EnvoyFilterList) GetItems() []IstioObject { - out := make([]IstioObject, len(in.Items)) - for i := range in.Items { - out[i] = &in.Items[i] - } - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyFilter) DeepCopyInto(out *EnvoyFilter) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyFilter. -func (in *EnvoyFilter) DeepCopy() *EnvoyFilter { - if in == nil { - return nil - } - out := new(EnvoyFilter) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *EnvoyFilter) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyFilterList) DeepCopyInto(out *EnvoyFilterList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]EnvoyFilter, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyFilterList. -func (in *EnvoyFilterList) DeepCopy() *EnvoyFilterList { - if in == nil { - return nil - } - out := new(EnvoyFilterList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *EnvoyFilterList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// Sidecar is the generic Kubernetes API object wrapper -type Sidecar struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec map[string]interface{} `json:"spec"` -} - -// GetSpec from a wrapper -func (in *Sidecar) GetSpec() map[string]interface{} { - return in.Spec -} - -// SetSpec for a wrapper -func (in *Sidecar) SetSpec(spec map[string]interface{}) { - in.Spec = spec -} - -// GetObjectMeta from a wrapper -func (in *Sidecar) GetObjectMeta() meta_v1.ObjectMeta { - return in.ObjectMeta -} - -// SetObjectMeta for a wrapper -func (in *Sidecar) SetObjectMeta(metadata meta_v1.ObjectMeta) { - in.ObjectMeta = metadata -} - -// SidecarList is the generic Kubernetes API list wrapper -type SidecarList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []Sidecar `json:"items"` -} - -// GetItems from a wrapper -func (in *SidecarList) GetItems() []IstioObject { - out := make([]IstioObject, len(in.Items)) - for i := range in.Items { - out[i] = &in.Items[i] - } - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Sidecar) DeepCopyInto(out *Sidecar) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Sidecar. -func (in *Sidecar) DeepCopy() *Sidecar { - if in == nil { - return nil - } - out := new(Sidecar) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Sidecar) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SidecarList) DeepCopyInto(out *SidecarList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Sidecar, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SidecarList. -func (in *SidecarList) DeepCopy() *SidecarList { - if in == nil { - return nil - } - out := new(SidecarList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *SidecarList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// HTTPAPISpec is the generic Kubernetes API object wrapper -type HTTPAPISpec struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec map[string]interface{} `json:"spec"` -} - -// GetSpec from a wrapper -func (in *HTTPAPISpec) GetSpec() map[string]interface{} { - return in.Spec -} - -// SetSpec for a wrapper -func (in *HTTPAPISpec) SetSpec(spec map[string]interface{}) { - in.Spec = spec -} - -// GetObjectMeta from a wrapper -func (in *HTTPAPISpec) GetObjectMeta() meta_v1.ObjectMeta { - return in.ObjectMeta -} - -// SetObjectMeta for a wrapper -func (in *HTTPAPISpec) SetObjectMeta(metadata meta_v1.ObjectMeta) { - in.ObjectMeta = metadata -} - -// HTTPAPISpecList is the generic Kubernetes API list wrapper -type HTTPAPISpecList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []HTTPAPISpec `json:"items"` -} - -// GetItems from a wrapper -func (in *HTTPAPISpecList) GetItems() []IstioObject { - out := make([]IstioObject, len(in.Items)) - for i := range in.Items { - out[i] = &in.Items[i] - } - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPAPISpec) DeepCopyInto(out *HTTPAPISpec) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPAPISpec. -func (in *HTTPAPISpec) DeepCopy() *HTTPAPISpec { - if in == nil { - return nil - } - out := new(HTTPAPISpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *HTTPAPISpec) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPAPISpecList) DeepCopyInto(out *HTTPAPISpecList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]HTTPAPISpec, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPAPISpecList. -func (in *HTTPAPISpecList) DeepCopy() *HTTPAPISpecList { - if in == nil { - return nil - } - out := new(HTTPAPISpecList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *HTTPAPISpecList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// HTTPAPISpecBinding is the generic Kubernetes API object wrapper -type HTTPAPISpecBinding struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec map[string]interface{} `json:"spec"` -} - -// GetSpec from a wrapper -func (in *HTTPAPISpecBinding) GetSpec() map[string]interface{} { - return in.Spec -} - -// SetSpec for a wrapper -func (in *HTTPAPISpecBinding) SetSpec(spec map[string]interface{}) { - in.Spec = spec -} - -// GetObjectMeta from a wrapper -func (in *HTTPAPISpecBinding) GetObjectMeta() meta_v1.ObjectMeta { - return in.ObjectMeta -} - -// SetObjectMeta for a wrapper -func (in *HTTPAPISpecBinding) SetObjectMeta(metadata meta_v1.ObjectMeta) { - in.ObjectMeta = metadata -} - -// HTTPAPISpecBindingList is the generic Kubernetes API list wrapper -type HTTPAPISpecBindingList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []HTTPAPISpecBinding `json:"items"` -} - -// GetItems from a wrapper -func (in *HTTPAPISpecBindingList) GetItems() []IstioObject { - out := make([]IstioObject, len(in.Items)) - for i := range in.Items { - out[i] = &in.Items[i] - } - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPAPISpecBinding) DeepCopyInto(out *HTTPAPISpecBinding) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPAPISpecBinding. -func (in *HTTPAPISpecBinding) DeepCopy() *HTTPAPISpecBinding { - if in == nil { - return nil - } - out := new(HTTPAPISpecBinding) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *HTTPAPISpecBinding) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPAPISpecBindingList) DeepCopyInto(out *HTTPAPISpecBindingList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]HTTPAPISpecBinding, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPAPISpecBindingList. -func (in *HTTPAPISpecBindingList) DeepCopy() *HTTPAPISpecBindingList { - if in == nil { - return nil - } - out := new(HTTPAPISpecBindingList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *HTTPAPISpecBindingList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// QuotaSpec is the generic Kubernetes API object wrapper -type QuotaSpec struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec map[string]interface{} `json:"spec"` -} - -// GetSpec from a wrapper -func (in *QuotaSpec) GetSpec() map[string]interface{} { - return in.Spec -} - -// SetSpec for a wrapper -func (in *QuotaSpec) SetSpec(spec map[string]interface{}) { - in.Spec = spec -} - -// GetObjectMeta from a wrapper -func (in *QuotaSpec) GetObjectMeta() meta_v1.ObjectMeta { - return in.ObjectMeta -} - -// SetObjectMeta for a wrapper -func (in *QuotaSpec) SetObjectMeta(metadata meta_v1.ObjectMeta) { - in.ObjectMeta = metadata -} - -// QuotaSpecList is the generic Kubernetes API list wrapper -type QuotaSpecList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []QuotaSpec `json:"items"` -} - -// GetItems from a wrapper -func (in *QuotaSpecList) GetItems() []IstioObject { - out := make([]IstioObject, len(in.Items)) - for i := range in.Items { - out[i] = &in.Items[i] - } - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *QuotaSpec) DeepCopyInto(out *QuotaSpec) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new QuotaSpec. -func (in *QuotaSpec) DeepCopy() *QuotaSpec { - if in == nil { - return nil - } - out := new(QuotaSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *QuotaSpec) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *QuotaSpecList) DeepCopyInto(out *QuotaSpecList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]QuotaSpec, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new QuotaSpecList. -func (in *QuotaSpecList) DeepCopy() *QuotaSpecList { - if in == nil { - return nil - } - out := new(QuotaSpecList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *QuotaSpecList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// QuotaSpecBinding is the generic Kubernetes API object wrapper -type QuotaSpecBinding struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec map[string]interface{} `json:"spec"` -} - -// GetSpec from a wrapper -func (in *QuotaSpecBinding) GetSpec() map[string]interface{} { - return in.Spec -} - -// SetSpec for a wrapper -func (in *QuotaSpecBinding) SetSpec(spec map[string]interface{}) { - in.Spec = spec -} - -// GetObjectMeta from a wrapper -func (in *QuotaSpecBinding) GetObjectMeta() meta_v1.ObjectMeta { - return in.ObjectMeta -} - -// SetObjectMeta for a wrapper -func (in *QuotaSpecBinding) SetObjectMeta(metadata meta_v1.ObjectMeta) { - in.ObjectMeta = metadata -} - -// QuotaSpecBindingList is the generic Kubernetes API list wrapper -type QuotaSpecBindingList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []QuotaSpecBinding `json:"items"` -} - -// GetItems from a wrapper -func (in *QuotaSpecBindingList) GetItems() []IstioObject { - out := make([]IstioObject, len(in.Items)) - for i := range in.Items { - out[i] = &in.Items[i] - } - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *QuotaSpecBinding) DeepCopyInto(out *QuotaSpecBinding) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new QuotaSpecBinding. -func (in *QuotaSpecBinding) DeepCopy() *QuotaSpecBinding { - if in == nil { - return nil - } - out := new(QuotaSpecBinding) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *QuotaSpecBinding) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *QuotaSpecBindingList) DeepCopyInto(out *QuotaSpecBindingList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]QuotaSpecBinding, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new QuotaSpecBindingList. -func (in *QuotaSpecBindingList) DeepCopy() *QuotaSpecBindingList { - if in == nil { - return nil - } - out := new(QuotaSpecBindingList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *QuotaSpecBindingList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// Policy is the generic Kubernetes API object wrapper -type Policy struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec map[string]interface{} `json:"spec"` -} - -// GetSpec from a wrapper -func (in *Policy) GetSpec() map[string]interface{} { - return in.Spec -} - -// SetSpec for a wrapper -func (in *Policy) SetSpec(spec map[string]interface{}) { - in.Spec = spec -} - -// GetObjectMeta from a wrapper -func (in *Policy) GetObjectMeta() meta_v1.ObjectMeta { - return in.ObjectMeta -} - -// SetObjectMeta for a wrapper -func (in *Policy) SetObjectMeta(metadata meta_v1.ObjectMeta) { - in.ObjectMeta = metadata -} - -// PolicyList is the generic Kubernetes API list wrapper -type PolicyList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []Policy `json:"items"` -} - -// GetItems from a wrapper -func (in *PolicyList) GetItems() []IstioObject { - out := make([]IstioObject, len(in.Items)) - for i := range in.Items { - out[i] = &in.Items[i] - } - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Policy) DeepCopyInto(out *Policy) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Policy. -func (in *Policy) DeepCopy() *Policy { - if in == nil { - return nil - } - out := new(Policy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Policy) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PolicyList) DeepCopyInto(out *PolicyList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Policy, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyList. -func (in *PolicyList) DeepCopy() *PolicyList { - if in == nil { - return nil - } - out := new(PolicyList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PolicyList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// MeshPolicy is the generic Kubernetes API object wrapper -type MeshPolicy struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec map[string]interface{} `json:"spec"` -} - -// GetSpec from a wrapper -func (in *MeshPolicy) GetSpec() map[string]interface{} { - return in.Spec -} - -// SetSpec for a wrapper -func (in *MeshPolicy) SetSpec(spec map[string]interface{}) { - in.Spec = spec -} - -// GetObjectMeta from a wrapper -func (in *MeshPolicy) GetObjectMeta() meta_v1.ObjectMeta { - return in.ObjectMeta -} - -// SetObjectMeta for a wrapper -func (in *MeshPolicy) SetObjectMeta(metadata meta_v1.ObjectMeta) { - in.ObjectMeta = metadata -} - -// MeshPolicyList is the generic Kubernetes API list wrapper -type MeshPolicyList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []MeshPolicy `json:"items"` -} - -// GetItems from a wrapper -func (in *MeshPolicyList) GetItems() []IstioObject { - out := make([]IstioObject, len(in.Items)) - for i := range in.Items { - out[i] = &in.Items[i] - } - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MeshPolicy) DeepCopyInto(out *MeshPolicy) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshPolicy. -func (in *MeshPolicy) DeepCopy() *MeshPolicy { - if in == nil { - return nil - } - out := new(MeshPolicy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *MeshPolicy) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MeshPolicyList) DeepCopyInto(out *MeshPolicyList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]MeshPolicy, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MeshPolicyList. -func (in *MeshPolicyList) DeepCopy() *MeshPolicyList { - if in == nil { - return nil - } - out := new(MeshPolicyList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *MeshPolicyList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// ServiceRole is the generic Kubernetes API object wrapper -type ServiceRole struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec map[string]interface{} `json:"spec"` -} - -// GetSpec from a wrapper -func (in *ServiceRole) GetSpec() map[string]interface{} { - return in.Spec -} - -// SetSpec for a wrapper -func (in *ServiceRole) SetSpec(spec map[string]interface{}) { - in.Spec = spec -} - -// GetObjectMeta from a wrapper -func (in *ServiceRole) GetObjectMeta() meta_v1.ObjectMeta { - return in.ObjectMeta -} - -// SetObjectMeta for a wrapper -func (in *ServiceRole) SetObjectMeta(metadata meta_v1.ObjectMeta) { - in.ObjectMeta = metadata -} - -// ServiceRoleList is the generic Kubernetes API list wrapper -type ServiceRoleList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []ServiceRole `json:"items"` -} - -// GetItems from a wrapper -func (in *ServiceRoleList) GetItems() []IstioObject { - out := make([]IstioObject, len(in.Items)) - for i := range in.Items { - out[i] = &in.Items[i] - } - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ServiceRole) DeepCopyInto(out *ServiceRole) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceRole. -func (in *ServiceRole) DeepCopy() *ServiceRole { - if in == nil { - return nil - } - out := new(ServiceRole) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ServiceRole) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ServiceRoleList) DeepCopyInto(out *ServiceRoleList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]ServiceRole, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceRoleList. -func (in *ServiceRoleList) DeepCopy() *ServiceRoleList { - if in == nil { - return nil - } - out := new(ServiceRoleList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ServiceRoleList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// ServiceRoleBinding is the generic Kubernetes API object wrapper -type ServiceRoleBinding struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec map[string]interface{} `json:"spec"` -} - -// GetSpec from a wrapper -func (in *ServiceRoleBinding) GetSpec() map[string]interface{} { - return in.Spec -} - -// SetSpec for a wrapper -func (in *ServiceRoleBinding) SetSpec(spec map[string]interface{}) { - in.Spec = spec -} - -// GetObjectMeta from a wrapper -func (in *ServiceRoleBinding) GetObjectMeta() meta_v1.ObjectMeta { - return in.ObjectMeta -} - -// SetObjectMeta for a wrapper -func (in *ServiceRoleBinding) SetObjectMeta(metadata meta_v1.ObjectMeta) { - in.ObjectMeta = metadata -} - -// ServiceRoleBindingList is the generic Kubernetes API list wrapper -type ServiceRoleBindingList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []ServiceRoleBinding `json:"items"` -} - -// GetItems from a wrapper -func (in *ServiceRoleBindingList) GetItems() []IstioObject { - out := make([]IstioObject, len(in.Items)) - for i := range in.Items { - out[i] = &in.Items[i] - } - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ServiceRoleBinding) DeepCopyInto(out *ServiceRoleBinding) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceRoleBinding. -func (in *ServiceRoleBinding) DeepCopy() *ServiceRoleBinding { - if in == nil { - return nil - } - out := new(ServiceRoleBinding) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ServiceRoleBinding) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ServiceRoleBindingList) DeepCopyInto(out *ServiceRoleBindingList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]ServiceRoleBinding, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceRoleBindingList. -func (in *ServiceRoleBindingList) DeepCopy() *ServiceRoleBindingList { - if in == nil { - return nil - } - out := new(ServiceRoleBindingList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ServiceRoleBindingList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// RbacConfig is the generic Kubernetes API object wrapper -type RbacConfig struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec map[string]interface{} `json:"spec"` -} - -// GetSpec from a wrapper -func (in *RbacConfig) GetSpec() map[string]interface{} { - return in.Spec -} - -// SetSpec for a wrapper -func (in *RbacConfig) SetSpec(spec map[string]interface{}) { - in.Spec = spec -} - -// GetObjectMeta from a wrapper -func (in *RbacConfig) GetObjectMeta() meta_v1.ObjectMeta { - return in.ObjectMeta -} - -// SetObjectMeta for a wrapper -func (in *RbacConfig) SetObjectMeta(metadata meta_v1.ObjectMeta) { - in.ObjectMeta = metadata -} - -// RbacConfigList is the generic Kubernetes API list wrapper -type RbacConfigList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []RbacConfig `json:"items"` -} - -// GetItems from a wrapper -func (in *RbacConfigList) GetItems() []IstioObject { - out := make([]IstioObject, len(in.Items)) - for i := range in.Items { - out[i] = &in.Items[i] - } - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RbacConfig) DeepCopyInto(out *RbacConfig) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RbacConfig. -func (in *RbacConfig) DeepCopy() *RbacConfig { - if in == nil { - return nil - } - out := new(RbacConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RbacConfig) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RbacConfigList) DeepCopyInto(out *RbacConfigList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]RbacConfig, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RbacConfigList. -func (in *RbacConfigList) DeepCopy() *RbacConfigList { - if in == nil { - return nil - } - out := new(RbacConfigList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RbacConfigList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// ClusterRbacConfig is the generic Kubernetes API object wrapper -type ClusterRbacConfig struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec map[string]interface{} `json:"spec"` -} - -// GetSpec from a wrapper -func (in *ClusterRbacConfig) GetSpec() map[string]interface{} { - return in.Spec -} - -// SetSpec for a wrapper -func (in *ClusterRbacConfig) SetSpec(spec map[string]interface{}) { - in.Spec = spec -} - -// GetObjectMeta from a wrapper -func (in *ClusterRbacConfig) GetObjectMeta() meta_v1.ObjectMeta { - return in.ObjectMeta -} - -// SetObjectMeta for a wrapper -func (in *ClusterRbacConfig) SetObjectMeta(metadata meta_v1.ObjectMeta) { - in.ObjectMeta = metadata -} - -// ClusterRbacConfigList is the generic Kubernetes API list wrapper -type ClusterRbacConfigList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []ClusterRbacConfig `json:"items"` -} - -// GetItems from a wrapper -func (in *ClusterRbacConfigList) GetItems() []IstioObject { - out := make([]IstioObject, len(in.Items)) - for i := range in.Items { - out[i] = &in.Items[i] - } - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterRbacConfig) DeepCopyInto(out *ClusterRbacConfig) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterRbacConfig. -func (in *ClusterRbacConfig) DeepCopy() *ClusterRbacConfig { - if in == nil { - return nil - } - out := new(ClusterRbacConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ClusterRbacConfig) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterRbacConfigList) DeepCopyInto(out *ClusterRbacConfigList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]ClusterRbacConfig, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterRbacConfigList. -func (in *ClusterRbacConfigList) DeepCopy() *ClusterRbacConfigList { - if in == nil { - return nil - } - out := new(ClusterRbacConfigList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ClusterRbacConfigList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - - return nil -} diff --git a/admiral/pkg/controller/istio/virtualservice.go b/admiral/pkg/controller/istio/virtualservice.go new file mode 100644 index 00000000..21c7f1b3 --- /dev/null +++ b/admiral/pkg/controller/istio/virtualservice.go @@ -0,0 +1,65 @@ +package istio + +import ( + "fmt" + "time" + + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/admiral" + networking "istio.io/client-go/pkg/apis/networking/v1alpha3" + "istio.io/client-go/pkg/clientset/versioned" + informers "istio.io/client-go/pkg/informers/externalversions/networking/v1alpha3" + k8sV1 "k8s.io/api/core/v1" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/cache" +) + +// Handler interface contains the methods that are required +type VirtualServiceHandler interface { + Added(obj *networking.VirtualService) + Updated(obj *networking.VirtualService) + Deleted(obj *networking.VirtualService) +} + +type VirtualServiceController struct { + IstioClient *versioned.Clientset + VirtualServiceHandler VirtualServiceHandler + informer cache.SharedIndexInformer + ctl *admiral.Controller +} + +func NewVirtualServiceController(stopCh <-chan struct{}, handler VirtualServiceHandler, config *rest.Config, resyncPeriod time.Duration) (*VirtualServiceController, error) { + + drController := VirtualServiceController{} + drController.VirtualServiceHandler = handler + + var err error + + ic, err := versioned.NewForConfig(config) + if err != nil { + return nil, fmt.Errorf("failed to create virtual service controller k8s client: %v", err) + } + + drController.IstioClient = ic + + drController.informer = informers.NewVirtualServiceInformer(ic, k8sV1.NamespaceAll, resyncPeriod, cache.Indexers{}) + + admiral.NewController(stopCh, &drController, drController.informer) + + return &drController, nil +} + +func (sec *VirtualServiceController) Added(ojb interface{}) { + dr := ojb.(*networking.VirtualService) + sec.VirtualServiceHandler.Added(dr) +} + +func (sec *VirtualServiceController) Updated(ojb interface{}) { + dr := ojb.(*networking.VirtualService) + sec.VirtualServiceHandler.Added(dr) +} + +func (sec *VirtualServiceController) Deleted(ojb interface{}) { + dr := ojb.(*networking.VirtualService) + sec.VirtualServiceHandler.Added(dr) + +} diff --git a/admiral/pkg/controller/secret/secretcontroller.go b/admiral/pkg/controller/secret/secretcontroller.go index 56e0913d..43c0ab99 100644 --- a/admiral/pkg/controller/secret/secretcontroller.go +++ b/admiral/pkg/controller/secret/secretcontroller.go @@ -19,6 +19,7 @@ import ( "errors" "fmt" "github.com/istio-ecosystem/admiral/admiral/pkg/controller/secret/resolver" + log "github.com/sirupsen/logrus" "k8s.io/client-go/rest" "time" @@ -32,9 +33,6 @@ import ( "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/workqueue" - - "istio.io/istio/pkg/kube" - "istio.io/istio/pkg/log" ) const ( @@ -46,10 +44,6 @@ const ( // DO NOT USE - TEST ONLY. var LoadKubeConfig = clientcmd.Load -// CreateInterfaceFromClusterConfig is a unit test override variable for interface create. -// DO NOT USE - TEST ONLY. -var CreateInterfaceFromClusterConfig = kube.CreateInterfaceFromClusterConfig - // addSecretCallback prototype for the add secret callback function. type addSecretCallback func(config *rest.Config, dataKey string, resyncPeriod time.Duration) error diff --git a/admiral/pkg/test/mock.go b/admiral/pkg/test/mock.go index ead07723..1e489e07 100644 --- a/admiral/pkg/test/mock.go +++ b/admiral/pkg/test/mock.go @@ -2,7 +2,6 @@ package test import ( "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" - "github.com/istio-ecosystem/admiral/admiral/pkg/controller/istio" k8sAppsV1 "k8s.io/api/apps/v1" k8sCoreV1 "k8s.io/api/core/v1" ) @@ -11,9 +10,6 @@ type MockIstioConfigStore struct { TestHook func(interface{}) } -func (m *MockIstioConfigStore) RegisterEventHandler(typ string, handler func(istio.Config, istio.Event)) { - -} func (m *MockIstioConfigStore) HasSynced() bool { return false @@ -21,31 +17,7 @@ func (m *MockIstioConfigStore) HasSynced() bool { func (m *MockIstioConfigStore) Run(stop <-chan struct{}) { } -func (m *MockIstioConfigStore) ConfigDescriptor() istio.ConfigDescriptor { - return nil -} -func (m *MockIstioConfigStore) Get(typ, name, namespace string) *istio.Config { - return nil -} - -func (m *MockIstioConfigStore) List(typ, namespace string) ([]istio.Config, error) { - return nil, nil -} -func (m *MockIstioConfigStore) Create(config istio.Config) (revision string, err error) { - - if m.TestHook != nil { - m.TestHook(config) - } - return "", nil -} -func (m *MockIstioConfigStore) Update(config istio.Config) (newRevision string, err error) { - - if m.TestHook != nil { - m.TestHook(config) - } - return "", nil -} func (m *MockIstioConfigStore) Delete(typ, name, namespace string) error { return nil @@ -113,6 +85,10 @@ func (m *MockGlobalTrafficHandler) Added(obj *v1.GlobalTrafficPolicy) { } +func (m *MockGlobalTrafficHandler) Updated(obj *v1.GlobalTrafficPolicy) { + +} + func (m *MockGlobalTrafficHandler) Deleted(obj *v1.GlobalTrafficPolicy) { } diff --git a/go.mod b/go.mod index 18c74507..af757104 100644 --- a/go.mod +++ b/go.mod @@ -3,55 +3,54 @@ module github.com/istio-ecosystem/admiral go 1.12 require ( - cloud.google.com/go v0.36.0 // indirect - github.com/cenkalti/backoff v2.1.1+incompatible - github.com/envoyproxy/go-control-plane v0.6.7 // indirect + github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect + github.com/cenkalti/backoff v2.2.1+incompatible + github.com/emicklei/go-restful v2.11.1+incompatible // indirect github.com/ghodss/yaml v1.0.0 + github.com/go-openapi/jsonreference v0.19.3 // indirect + github.com/go-openapi/spec v0.19.4 // indirect github.com/gogo/googleapis v1.1.0 // indirect - github.com/gogo/protobuf v1.2.0 + github.com/gogo/protobuf v1.3.1 github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc // indirect - github.com/golang/protobuf v1.2.0 + github.com/golang/protobuf v1.3.2 github.com/google/btree v1.0.0 // indirect - github.com/google/go-cmp v0.2.0 - github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect - github.com/googleapis/gnostic v0.2.0 // indirect + github.com/google/go-cmp v0.3.1 // indirect github.com/gorilla/mux v1.7.0 // indirect github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc // indirect github.com/hashicorp/go-multierror v1.0.0 - github.com/hashicorp/golang-lru v0.5.0 // indirect - github.com/imdario/mergo v0.3.7 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/json-iterator/go v1.1.5 // indirect + github.com/imdario/mergo v0.3.8 // indirect github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/lyft/protoc-gen-validate v0.0.13 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect + github.com/mailru/easyjson v0.7.0 // indirect github.com/natefinch/lumberjack v0.0.0-20170531160350-a96e63847dc3 // indirect github.com/onsi/ginkgo v1.10.2 // indirect - github.com/onsi/gomega v1.7.0 // indirect - github.com/peterbourgon/diskv v2.0.1+incompatible // indirect - github.com/prometheus/client_golang v0.9.2 - github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect - github.com/prometheus/common v0.0.0-20190124163007-cfeb6f9992ff // indirect - github.com/prometheus/procfs v0.0.0-20190209105433-f8d8b3f739bd // indirect + github.com/prometheus/client_golang v1.0.0 + github.com/prometheus/common v0.7.0 github.com/sirupsen/logrus v1.4.2 - github.com/spf13/cobra v0.0.3 - github.com/spf13/pflag v1.0.3 // indirect + github.com/spf13/cobra v0.0.5 github.com/yl2chen/cidranger v0.0.0-20180214081945-928b519e5268 // indirect go.uber.org/atomic v1.3.2 // indirect go.uber.org/multierr v1.1.0 // indirect go.uber.org/zap v1.9.1 // indirect - golang.org/x/oauth2 v0.0.0-20190212230446-3e8b2be13635 // indirect - google.golang.org/grpc v1.18.0 // indirect + golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d // indirect + golang.org/x/crypto v0.0.0-20191108234033-bd318be0434a // indirect + golang.org/x/net v0.0.0-20191109021931-daa7c04131f5 // indirect + golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd // indirect + golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect + golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101 // indirect + gonum.org/v1/gonum v0.6.0 // indirect + google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a // indirect + google.golang.org/grpc v1.25.1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect - gopkg.in/yaml.v2 v2.2.2 - gotest.tools v2.2.0+incompatible // indirect - istio.io/api v0.0.0-20190213184321-d817a1a3e29a - istio.io/istio v0.0.0-20190214070811-6113e155ac85 - k8s.io/api v0.0.0-20190118113203-912cbe2bfef3 + gopkg.in/yaml.v2 v2.2.5 + istio.io/api v0.0.0-20191107224652-6818c03d25b2 + istio.io/client-go v0.0.0-20191104174404-7b65e62d85b0 + istio.io/gogo-genproto v0.0.0-20191024203824-d079cc8b1d55 // indirect + k8s.io/api v0.0.0-20191108065827-59e77acf588f k8s.io/apiextensions-apiserver v0.0.0-20181204003618-e419c5771cdc - k8s.io/apimachinery v0.0.0-20190208202428-1a579f8a7b42 - k8s.io/client-go v0.0.0-20190117233410-4022682532b3 - k8s.io/code-generator v0.15.8-beta.1 // indirect - k8s.io/kube-openapi v0.0.0-20190208205540-d7c86cdc46e3 // indirect + k8s.io/apimachinery v0.0.0-20191111054156-6eb29fdf75dc + k8s.io/client-go v0.0.0-20191016111102-bec269661e48 + k8s.io/code-generator v0.0.0-20191109100332-a9a0d9c0b3aa // indirect + k8s.io/gengo v0.0.0-20191108084044-e500ee069b5c // indirect + k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d // indirect ) diff --git a/go.sum b/go.sum index 7b82ff08..c7d25b9e 100644 --- a/go.sum +++ b/go.sum @@ -3,62 +3,83 @@ cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.36.0 h1:+aCSj7tOo2LODWVEuZDZeGCckdt6MlSF+X/rB3wUiS8= cloud.google.com/go v0.36.0/go.mod h1:RUoy9p/M4ge0HzT8L+SDZ8jg+Q6fth0CiBuhFJpSV40= +cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 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 h1:lsxEuwrXEAokXB9qhlbKWPpo3KMLZQ5WB5WLQRW1uq0= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +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-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco= -github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY= github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-oidc v0.0.0-20180117170138-065b426bd416/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.0.0-20180108230905-e214231b295a/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d h1:t5Wuyh53qYyg9eqn4BbnlIT+vmhyww0TatL+zT3uWgI= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.11.1+incompatible h1:CjKsv3uWcCMvySPQYKxO8XX3f9zD4FeZRsW4G0B4ffE= +github.com/emicklei/go-restful v2.11.1+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.6.7 h1:fyr1xdpt6v/HvZL70++avYT0HggJQD6/dHgNk0+uJjM= github.com/envoyproxy/go-control-plane v0.6.7/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= -github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac= -github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= +github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= @@ -66,37 +87,28 @@ github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0 github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= -github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.17.2 h1:eYp14J1o8TTSCzndHBtsNuckikV1PfZOSnx4BcBeu0c= -github.com/go-openapi/analysis v0.17.2/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.17.2 h1:azEQ8Fnx0jmtFF2fxsnmd6I0x6rsweUF63qqSO1NmKk= -github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.19.0 h1:FTUMcX77w5rQkClIzDtTxvn6Bsa894CcrzNj2MMfeg8= -github.com/go-openapi/jsonpointer v0.19.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.19.0 h1:BqWKpV1dFd+AuiKlgtddwVIFQsuMpxfBDBHGfM2yNpk= -github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.17.2 h1:tEXYu6Xc0pevpzzQx5ghrMN9F7IVpN/+u4iD3rkYE5o= -github.com/go-openapi/loads v0.17.2/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= -github.com/go-openapi/runtime v0.17.2 h1:/ZK67ikFhQAMFFH/aPu2MaGH7QjP4wHBvHYOVIzDAw0= -github.com/go-openapi/runtime v0.17.2/go.mod h1:QO936ZXeisByFmZEO1IS1Dqhtf4QV1sYYFtIq6Ld86Q= -github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.17.2 h1:eb2NbuCnoe8cWAxhtK6CfMWUYmiFEZJ9Hx3Z2WRwJ5M= -github.com/go-openapi/spec v0.17.2/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/strfmt v0.17.0 h1:1isAxYf//QDTnVzbLAMrUK++0k1EjeLJU/gTOR0o3Mc= -github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.17.2 h1:K/ycE/XTUDFltNHSO32cGRUhrVGJD64o8WgAIZNyc3k= -github.com/go-openapi/swag v0.17.2/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= -github.com/go-openapi/validate v0.18.0 h1:PVXYcP1GkTl+XIAJnyJxOmK6CSG5Q1UcvoCvNO++5Kg= -github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.19.2 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo= +github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/googleapis v1.1.0 h1:kFkMAZBNAn4j7K0GiZr8cRYzejq68VbheufiV3YuyFI= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= @@ -104,17 +116,27 @@ github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc h1:55rEp52jU6bkyslZ1+C/7NGfpQsEc6pxGLAGDOctqbw= github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= 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/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -122,24 +144,30 @@ github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= +github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= +github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U= github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc h1:f8eY6cV/x1x+HLjOp4r72s/31/V2aTUtg5oKRRPf8/Q= @@ -155,19 +183,32 @@ github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uP github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= +github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= -github.com/jonboulle/clockwork v0.0.0-20141017032234-72f9bd7c4e0c/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= @@ -176,23 +217,30 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lyft/protoc-gen-validate v0.0.13 h1:KNt/RhmQTOLr7Aj8PsJ7mTronaFyx80mRTT9qF261dA= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +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-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d h1:7PxY7LVfSZm7PEeBTyK1rj1gABdCO2mbri6GKO1cMDs= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= @@ -202,19 +250,25 @@ github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4 github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.2 h1:uqH7bpe+ERSiDa34FDOF7RikN6RzXgduUF8yarlZp94= github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= -github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= @@ -222,18 +276,24 @@ github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20190124163007-cfeb6f9992ff h1:uAzOK0mVIKjz4TXAHcAs/ZBitRqIXnbXrPUxThluLzg= github.com/prometheus/common v0.0.0-20190124163007-cfeb6f9992ff/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +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/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190209105433-f8d8b3f739bd h1:pi7bGw6n4tfgHQtWDxJBBLYVdFr1GlfQEsDOyCDDFMM= github.com/prometheus/procfs v0.0.0-20190209105433-f8d8b3f739bd/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -265,22 +325,35 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= -github.com/spf13/cobra v0.0.0-20180319062004-c439c4fa0937/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= 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/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yl2chen/cidranger v0.0.0-20180214081945-928b519e5268 h1:lkoOjizoHqOcEFsvYGE5c8Ykdijjnd0R3r1yDYHzLno= github.com/yl2chen/cidranger v0.0.0-20180214081945-928b519e5268/go.mod h1:mq0zhomp/G6rRTb0dvHWXRHr/2+Qgeq5hMXfJ670+i4= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -294,17 +367,30 @@ golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613 h1:MQ/ZZiDsUapFFiMS+vzwXkCTeEKaum+Do5rINYJDmxc= golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 h1:ACG4HJsFiNMf47Y4PeRoebLNy/2lXT9EtprMuTFWt1M= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191108234033-bd318be0434a h1:R/qVym5WAxsZWQqZCwDY/8sdVKV1m1WgU4/S5IRQAzc= +golang.org/x/crypto v0.0.0-20191108234033-bd318be0434a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 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/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -316,77 +402,140 @@ golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190206173232-65e2d4e15006 h1:bfLnR+k0tq5Lqt6dflRLcZiz6UaXCMt3vhYJ1l4FQ80= golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= +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-20190812203447-cdfb69ac37fc h1:gkKoSkUmnU6bpS/VhkuO27bzQeSA51uaEfbOW5dNb68= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/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-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191108225301-c7154b74f18f h1:nughwhJbEPZ8KHzITXxn59RmA0bhS7vDoe8gQi6Od4M= +golang.org/x/net v0.0.0-20191108225301-c7154b74f18f/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191109021931-daa7c04131f5 h1:bHNaocaoJxYBo5cw41UyTMLjYlb8wPY7+WFrnklbHOM= +golang.org/x/net v0.0.0-20191109021931-daa7c04131f5/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190212230446-3e8b2be13635 h1:dOJmQysgY8iOBECuNp0vlKHWEtfiTnyjisEizRV3/4o= golang.org/x/oauth2 v0.0.0-20190212230446-3e8b2be13635/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA= -golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= 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/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/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 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/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/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU= -golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 h1:xQwXv67TxFo9nC1GJFyab5eq/5B590r6RlnL/G8Sz7w= +golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/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-20190328211700-ab21143f2384 h1:TFlARGu6Czu1z7q93HTxcP1P+/ZFC/IKythI5RzrnRg= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +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/tools v0.0.0-20191108193012-7d206e10da11 h1:Yq9t9jnGoR+dBuitxdo9l6Q7xh/zOyNnYUtDKaQ3x0E= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101 h1:LCmXVkvpQCDj724eX6irUTPCJP5GelFHxqGSWL2D1R0= +golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485 h1:OB/uP/Puiu5vS5QMRPrXCDWUPb+kt8f1KW8oQzFejQw= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= +gonum.org/v1/gonum v0.6.0 h1:DJy6UzXbahnGUf1ujUNkh/NEtK14qMo2nvlBPs4U5yw= +gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20170731182057-09f6ed296fc6/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922 h1:mBVYJnbrXLA/ZCBTCe7PtEgAUP+1bg92qTaFoPHdz+8= google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4= -google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190916214212-f660b8655731 h1:Phvl0+G5t5k/EUFUi0wPdUUeTL2HydMQUXHnunWgSb0= +google.golang.org/genproto v0.0.0-20190916214212-f660b8655731/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.18.0 h1:IZl7mfBGfbhYx2p2rKRtYgDFw6SBz+kclmxYrCksPPA= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1 h1:q4XQuHFC6I28BKZpo6IYyb3mNO+l7lSOxRuYTCiDfXk= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 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/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= @@ -404,60 +553,82 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= istio.io/api v0.0.0-20190213184321-d817a1a3e29a h1:upOFSrZeRWABTJ0VV3z4IibWUsM7kQOAIpQitizrJtY= istio.io/api v0.0.0-20190213184321-d817a1a3e29a/go.mod h1:hhLFQmpHia8zgaM37vb2ml9iS5NfNfqZGRt1pS9aVEo= +istio.io/api v0.0.0-20191101221011-3fcb499e2a05 h1:DY6qiC77faByWMs584AGxo2g7M0fM8QKNApJf4yT9nc= +istio.io/api v0.0.0-20191101221011-3fcb499e2a05/go.mod h1:+cyHH83OwC0rFpwk8eXctzPNpiCAbB+r6kmMiAxxBHw= +istio.io/api v0.0.0-20191107224652-6818c03d25b2 h1:rIP85xkbLc9Df20/Tnz2LxNck7uhOYAEva6crU4C5nE= +istio.io/api v0.0.0-20191107224652-6818c03d25b2/go.mod h1:+cyHH83OwC0rFpwk8eXctzPNpiCAbB+r6kmMiAxxBHw= +istio.io/client-go v0.0.0-20191104174404-7b65e62d85b0 h1:6DWOkG2XZQRAwthO71a0v+AFcONHF6Wo9mtfMNUly24= +istio.io/client-go v0.0.0-20191104174404-7b65e62d85b0/go.mod h1:jdpC7NC/WdBGIxcIHPzlurKaCZb+8LnsJW4U4mbX6n0= +istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a h1:w7zILua2dnYo9CxImhpNW4NE/8ZxEoc/wfBfHrhUhrE= +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= istio.io/istio v0.0.0-20190214070811-6113e155ac85 h1:93mpP4tAl4mcM2oDkr0VrG6ysFfXEuGZhjE1gXG4/4c= istio.io/istio v0.0.0-20190214070811-6113e155ac85/go.mod h1:OWBySrQjjk549IhxWCt7DTl9ZSsXdvbgm+SmgGVRsGA= +istio.io/istio v0.0.0-20191108232952-0419c5da7b6d h1:LSyCcMlMPdNJXnp0rEsi0dHLaxksdaUz8PMe5QTQXjo= k8s.io/api v0.0.0-20190118113203-912cbe2bfef3 h1:lV0+KGoNkvZOt4zGT4H83hQrzWMt/US/LSz4z4+BQS4= k8s.io/api v0.0.0-20190118113203-912cbe2bfef3/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= -k8s.io/api v0.0.0-20190620084959-7cf5895f2711/go.mod h1:TBhBqb1AWbBQbW3XRusr7n7E4v2+5ZY8r8sAMnyFC5A= -k8s.io/api v0.0.0-20191004120003-3a12735a829a h1:JYR+aajVYYu7/KEA5zOSj+hCe7HyzETQcC6EKcYvKHo= -k8s.io/api v0.0.0-20191004120003-3a12735a829a/go.mod h1:ceHJE/vDjU8jKnRV6Vqn/+vyZmC6NvOluInN+RhQkIs= +k8s.io/api v0.0.0-20191016110408-35e52d86657a h1:VVUE9xTCXP6KUPMf92cQmN88orz600ebexcRRaBTepQ= +k8s.io/api v0.0.0-20191016110408-35e52d86657a/go.mod h1:/L5qH+AD540e7Cetbui1tuJeXdmNhO8jM6VkXeDdDhQ= +k8s.io/api v0.0.0-20191108065827-59e77acf588f h1:ZTXCVdYGBbAblNUJ5B19ztoy6WHMNrPerxQJF9agLpY= +k8s.io/api v0.0.0-20191108065827-59e77acf588f/go.mod h1:uQDmBYHoPSuhbg8FGTRzrOdaNqLiws/LAtBrHv0kN5U= k8s.io/apiextensions-apiserver v0.0.0-20181204003618-e419c5771cdc h1:IOukeE9HtTwpLslbujLDfRpfFU6tsjq28yO0fjnl/hk= k8s.io/apiextensions-apiserver v0.0.0-20181204003618-e419c5771cdc/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE= k8s.io/apiextensions-apiserver v0.0.0-20191014073952-6b6acca910e3 h1:11T1avcLWsxcVNglsvkStUw3GJFjOYxuCb17+88+GI8= k8s.io/apiextensions-apiserver v0.0.0-20191014073952-6b6acca910e3/go.mod h1:0Th7mO8ClkyT95SnaNvHaXwqLwCQbIiqo95WWDqrX/E= k8s.io/apimachinery v0.0.0-20190208202428-1a579f8a7b42 h1:ju2lLx7i6XE8A9QLtwxlQngelP8SosMIjK4IYE/TLFI= k8s.io/apimachinery v0.0.0-20190208202428-1a579f8a7b42/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= -k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719/go.mod h1:I4A+glKBHiTgiEjQiCCQfCAIcIMFGt291SmsvcrFzJA= -k8s.io/apimachinery v0.0.0-20191004115701-31ade1b30762/go.mod h1:Xc10RHc1U+F/e9GCloJ8QAeCGevSVP5xhOhqlE+e1kM= -k8s.io/apimachinery v0.15.8-beta.1 h1:agYiZIyyNE1KwsiQQAeF/SqXdsQRgoD+bj4wQSYggGc= -k8s.io/apimachinery v0.15.8-beta.1/go.mod h1:Xc10RHc1U+F/e9GCloJ8QAeCGevSVP5xhOhqlE+e1kM= -k8s.io/apiserver v0.0.0-20191009121109-e383d20f5094 h1:Smkstrlg4MRqNG7CMYGr6N+9FZS0lTKSK0n5OVQN/no= -k8s.io/apiserver v0.0.0-20191009121109-e383d20f5094/go.mod h1:EB3CoZ8WNX95G9ftm5Wc/qdc9qqbxgyYHIVJtmiOLUs= +k8s.io/apimachinery v0.0.0-20191004115801-a2eda9f80ab8 h1:Iieh/ZEgT3BWwbLD5qEKcY06jKuPEl6zC7gPSehoLw4= +k8s.io/apimachinery v0.0.0-20191004115801-a2eda9f80ab8/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ= +k8s.io/apimachinery v0.0.0-20191108065633-c18f71bf2947 h1:f3H3Rf7KD9fjmmbIMwxBye3ctEuXnbskaX/l1xy+68E= +k8s.io/apimachinery v0.0.0-20191108065633-c18f71bf2947/go.mod h1:nEP/6rwhzfljWYGVS6pfyES3ipZTR19vzMnSM+ur3ho= +k8s.io/apimachinery v0.0.0-20191111054156-6eb29fdf75dc h1:hC0UI7qlplCVlRexiPMHwcOCT3IPk9Pgo599vKGOOS4= +k8s.io/apimachinery v0.0.0-20191111054156-6eb29fdf75dc/go.mod h1:+6CX7hP4aLfX2sb91JYDMIp0VqDSog2kZu0BHe+lP+s= k8s.io/client-go v0.0.0-20190117233410-4022682532b3 h1:7VVBo3+/iX6dzB8dshNuo6Duds/6AoNP5R59IUnwoxg= k8s.io/client-go v0.0.0-20190117233410-4022682532b3/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= -k8s.io/client-go v0.0.0-20190620085101-78d2af792bab h1:E8Fecph0qbNsAbijJJQryKu4Oi9QTp5cVpjTE+nqg6g= -k8s.io/client-go v0.0.0-20190620085101-78d2af792bab/go.mod h1:E95RaSlHr79aHaX0aGSwcPNfygDiPKOVXdmivCIZT0k= -k8s.io/client-go v0.0.0-20191004120415-b2f42092e376 h1:hhNzwziFzNl7AjG5ImSgJZz1VIr+ixfQT0g1oVtcyQ0= -k8s.io/client-go v0.0.0-20191004120415-b2f42092e376/go.mod h1:ksVkYlACXo9hR9AV+cYyCkuWL1xnWcGtAFxsfqMcozg= -k8s.io/code-generator v0.0.0-20190612205613-18da4a14b22b/go.mod h1:G8bQwmHm2eafm5bgtX67XDZQ8CWKSGu9DekI+yN4Y5I= -k8s.io/code-generator v0.15.8-beta.1 h1:b5I3V4CmDjUglQiducVbXau0h8i0Dn37fC5IqccKVx4= -k8s.io/code-generator v0.15.8-beta.1/go.mod h1:G8bQwmHm2eafm5bgtX67XDZQ8CWKSGu9DekI+yN4Y5I= -k8s.io/component-base v0.0.0-20191004121406-d5138742ad72 h1:9J8Xo2pZVmyiG90zbgUPPII51wNP9IuAnIVCuMCA4oM= -k8s.io/component-base v0.0.0-20191004121406-d5138742ad72/go.mod h1:zT8T6A3K4wLlbQkLUC62skjmWoiNJ9B8WUQj3KIvcrQ= -k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af h1:SwjZbO0u5ZuaV6TRMWOGB40iaycX8sbdMQHtjNZ19dk= -k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/klog v0.3.1 h1:RVgyDHY/kFKtLqh67NvEWIgkMneNoIrdkN0CxDSQc68= -k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/client-go v0.0.0-20191016111102-bec269661e48 h1:C2XVy2z0dV94q9hSSoCuTPp1KOG7IegvbdXuz9VGxoU= +k8s.io/client-go v0.0.0-20191016111102-bec269661e48/go.mod h1:hrwktSwYGI4JK+TJA3dMaFyyvHVi/aLarVHpbs8bgCU= +k8s.io/code-generator v0.0.0-20191108065441-3c1097069dc3 h1:qcl3ardofg2+EAvhYmwX+XMLXyDQ6dOp8yL2ir1JfsM= +k8s.io/code-generator v0.0.0-20191108065441-3c1097069dc3/go.mod h1:OJTI2RPXj6kq4bfFqT1JrTEC1S4toTWinGOm1O8jUuY= +k8s.io/code-generator v0.0.0-20191109100332-a9a0d9c0b3aa h1:4feCF84yk6VEXdpOwOhwB1YIwobejEwKBzgHY0xa9Co= +k8s.io/code-generator v0.0.0-20191109100332-a9a0d9c0b3aa/go.mod h1:fRFrKVixH946mn5PeglV2fvxbE86JesGi16bsWZ1xz4= +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/gengo v0.0.0-20191108084044-e500ee069b5c h1:iraFntD6FA5K/hBaPW2z/ZItJZEG63uc3ak5S0oDVEo= +k8s.io/gengo v0.0.0-20191108084044-e500ee069b5c/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.4.0 h1:lCJCxf/LIowc2IGS9TPjWDyXY4nOmdGdfcwwDQCOURQ= +k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/kube-openapi v0.0.0-20190208205540-d7c86cdc46e3 h1:exy9uNfm+xG+9rQsfPhmJQt+l/Pufd86hB9T/QrHeR4= k8s.io/kube-openapi v0.0.0-20190208205540-d7c86cdc46e3/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= -k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30 h1:TRb4wNWoBVrH9plmkp2q86FIDppkbrEXdXlxU3a3BMI= -k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= -k8s.io/utils v0.0.0-20190221042446-c2654d5206da h1:ElyM7RPonbKnQqOcw7dG2IK5uvQQn3b/WPHqD5mBvP4= -k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= +k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf h1:EYm5AW/UUDbnmnI+gK0TJDVK9qPLhM+sRHYanNKw0EQ= +k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/utils v0.0.0-20190801114015-581e00157fb1 h1:+ySTxfHnfzZb9ys375PXNlLhkJPLKgHajBU0N62BDvE= +k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d h1:1P0iBJsBzxRmR+dIFnM+Iu4aLxnoa7lBqozW/0uHbT8= +k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= -sigs.k8s.io/structured-merge-diff v0.0.0-20190302045857-e85c7b244fd2 h1:9r5DY45ef9LtcA6BnkhW8MPV7OKAfbf2AUwUhq3LeRk= -sigs.k8s.io/structured-merge-diff v0.0.0-20190302045857-e85c7b244fd2/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= diff --git a/install/admiral/base/deployments.yaml b/install/admiral/base/deployments.yaml index 8c3f6313..44bb57d8 100644 --- a/install/admiral/base/deployments.yaml +++ b/install/admiral/base/deployments.yaml @@ -30,7 +30,7 @@ spec: - $(secret_resolver) - --secret_resolver_config_path - /etc/admiral/config.yaml - image: docker.io/admiralproj/admiral:v0.1-alpha + image: docker.io/admiralproj/admiral:v0.1-beta # livenessProbe: # failureThreshold: 5 # httpGet: From b0e46621e02c92bc39af758aa5edbc643e7a23c8 Mon Sep 17 00:00:00 2001 From: josephpeacock <51184065+josephpeacock@users.noreply.github.com> Date: Wed, 26 Feb 2020 15:44:24 -0800 Subject: [PATCH 20/45] Support long identities (#71) * formalizing the behavior to fall back to annotation if label isn't present Signed-off-by: Joe Peacock * Removing the hardcoded references to a default identity label Signed-off-by: Joe Peacock * Nil check Signed-off-by: Joe Peacock * Refactoring to add the rest of the admiral params to the singleton (and to enforce singleton-ness Signed-off-by: Joe Peacock * Another test Signed-off-by: Joe Peacock * coverage bump Signed-off-by: Joe Peacock * Whoops broke a test Signed-off-by: Joe Peacock * Adding warning for config re-initialization Signed-off-by: Joe Peacock * Fixed rebase-related issues Signed-off-by: Joe Peacock Signed-off-by: Madeline --- admiral/cmd/admiral/cmd/root.go | 9 +-- .../typed/admiral/model/admiral_client.go | 7 +- admiral/pkg/clusters/handler.go | 45 ++++++----- admiral/pkg/clusters/registry.go | 23 +++--- admiral/pkg/clusters/registry_test.go | 38 +++++---- admiral/pkg/clusters/serviceentry.go | 30 +++---- admiral/pkg/clusters/types.go | 35 +------- admiral/pkg/controller/admiral/configmap.go | 6 +- .../pkg/controller/admiral/configmap_test.go | 5 +- admiral/pkg/controller/admiral/deployment.go | 4 +- .../pkg/controller/admiral/deployment_test.go | 2 +- admiral/pkg/controller/admiral/pod.go | 4 +- admiral/pkg/controller/admiral/pod_test.go | 2 +- admiral/pkg/controller/common/common.go | 30 ++++--- admiral/pkg/controller/common/common_test.go | 11 ++- admiral/pkg/controller/common/config.go | 79 +++++++++++++++++++ .../controller/common/configInitializer.go | 25 ++++++ admiral/pkg/controller/common/config_test.go | 72 +++++++++++++++++ admiral/pkg/controller/common/types.go | 30 ++++++- admiral/pkg/controller/util/util.go | 2 +- go.mod | 22 +++--- go.sum | 23 ++++++ 22 files changed, 361 insertions(+), 143 deletions(-) create mode 100644 admiral/pkg/controller/common/config.go create mode 100644 admiral/pkg/controller/common/configInitializer.go create mode 100644 admiral/pkg/controller/common/config_test.go diff --git a/admiral/cmd/admiral/cmd/root.go b/admiral/cmd/admiral/cmd/root.go index 9f612c37..54ca4a31 100644 --- a/admiral/cmd/admiral/cmd/root.go +++ b/admiral/cmd/admiral/cmd/root.go @@ -5,8 +5,8 @@ import ( "flag" "fmt" "github.com/istio-ecosystem/admiral/admiral/pkg/clusters" - "github.com/prometheus/common/log" "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + log "github.com/sirupsen/logrus" "os" "os/signal" "syscall" @@ -21,10 +21,9 @@ var ( // GetRootCmd returns the root of the cobra command-tree. func GetRootCmd(args []string) *cobra.Command { - var () - params := clusters.AdmiralParams{LabelSet: &common.LabelSet{}} + params := common.AdmiralParams{LabelSet: &common.LabelSet{}} rootCmd := &cobra.Command{ Use: "Admiral", @@ -82,8 +81,8 @@ func GetRootCmd(args []string) *cobra.Command { "The label value, on a namespace, which tells Istio to perform sidecar injection") rootCmd.PersistentFlags().StringVar(¶ms.HostnameSuffix, "hostname_suffix", "global", "The hostname suffix to customize the cname generated by admiral. Default suffix value will be \"global\"") - rootCmd.PersistentFlags().StringVar(¶ms.LabelSet.WorkloadIdentityLabel, "workload_identity_label", "identity", - "The workload identity label key, on deployment which holds identity value used to generate cname by admiral. Default label key will be \"identity\"") + rootCmd.PersistentFlags().StringVar(¶ms.LabelSet.WorkloadIdentityKey, "workload_identity_key", "identity", + "The workload identity key, on deployment which holds identity value used to generate cname by admiral. Default label key will be \"identity\" Admiral will look for a label with this key. If present, that will be used. If not, it will try an annotation (for use cases where an identity is longer than 63 chars)") return rootCmd } diff --git a/admiral/pkg/client/clientset/versioned/typed/admiral/model/admiral_client.go b/admiral/pkg/client/clientset/versioned/typed/admiral/model/admiral_client.go index c481a5cc..76a8d069 100644 --- a/admiral/pkg/client/clientset/versioned/typed/admiral/model/admiral_client.go +++ b/admiral/pkg/client/clientset/versioned/typed/admiral/model/admiral_client.go @@ -19,10 +19,9 @@ limitations under the License. package model import ( - model "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/model" + "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/model" "github.com/istio-ecosystem/admiral/admiral/pkg/client/clientset/versioned/scheme" - "k8s.io/apimachinery/pkg/runtime/serializer" - rest "k8s.io/client-go/rest" + "k8s.io/client-go/rest" ) type AdmiralModelInterface interface { @@ -66,7 +65,7 @@ func setConfigDefaults(config *rest.Config) error { gv := model.SchemeGroupVersion config.GroupVersion = &gv config.APIPath = "/apis" - config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} + config.NegotiatedSerializer = scheme.Codecs if config.UserAgent == "" { diff --git a/admiral/pkg/clusters/handler.go b/admiral/pkg/clusters/handler.go index 6a4ce38b..d54e453a 100644 --- a/admiral/pkg/clusters/handler.go +++ b/admiral/pkg/clusters/handler.go @@ -40,7 +40,7 @@ func updateIdentityDependencyCache(sourceIdentity string, identityDependencyCach log.Infof(LogFormat, "Update", "dependency-cache", dr.Name, "", "Updated=true namespace="+dr.Namespace) } -func handleDependencyRecord(sourceIdentity string, r *RemoteRegistry, rcs map[string]*RemoteController, config AdmiralParams, obj *v1.Dependency) { +func handleDependencyRecord(sourceIdentity string, r *RemoteRegistry, rcs map[string]*RemoteController, obj *v1.Dependency) { destinationIdentitys := obj.Spec.Destinations @@ -78,7 +78,7 @@ func handleDependencyRecord(sourceIdentity string, r *RemoteRegistry, rcs map[st continue } //TODO pass deployment - tmpSe := createServiceEntry(rc, config, r.AdmiralCache, deployment[0], serviceEntries) + tmpSe := createServiceEntry(rc, r.AdmiralCache, deployment[0], serviceEntries) if tmpSe == nil { continue @@ -109,7 +109,7 @@ func handleDependencyRecord(sourceIdentity string, r *RemoteRegistry, rcs map[st } //add service entries for all dependencies in source cluster - AddServiceEntriesWithDr(r, sourceClusters, rcs, serviceEntries, config.SyncNamespace) + AddServiceEntriesWithDr(r.AdmiralCache, sourceClusters, rcs, serviceEntries) } func getIstioResourceName(host string, suffix string) string { @@ -211,7 +211,7 @@ func handleDestinationRuleEvent(obj *v1alpha3.DestinationRule, dh *DestinationRu var localIdentityId string - syncNamespace := dh.RemoteRegistry.config.SyncNamespace + syncNamespace := common.GetSyncNamespace() r := dh.RemoteRegistry @@ -253,7 +253,7 @@ func handleDestinationRuleEvent(obj *v1alpha3.DestinationRule, dh *DestinationRu var drServiceEntries = make(map[string]*v1alpha32.ServiceEntry) - exist, err := rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(r.config.SyncNamespace).Get(basicSEName, v12.GetOptions{}) + exist, err := rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(syncNamespace).Get(basicSEName, v12.GetOptions{}) var identityId = "" @@ -279,36 +279,36 @@ func handleDestinationRuleEvent(obj *v1alpha3.DestinationRule, dh *DestinationRu if event == common.Delete { - rc.DestinationRuleController.IstioClient.NetworkingV1alpha3().DestinationRules(r.config.SyncNamespace).Delete(obj.Name, &v12.DeleteOptions{}) + rc.DestinationRuleController.IstioClient.NetworkingV1alpha3().DestinationRules(syncNamespace).Delete(obj.Name, &v12.DeleteOptions{}) log.Infof(LogFormat, "Delete", "DestinationRule", obj.Name, clusterId, "success") - rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(r.config.SyncNamespace).Delete(seName, &v12.DeleteOptions{}) + rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(syncNamespace).Delete(seName, &v12.DeleteOptions{}) log.Infof(LogFormat, "Delete", "ServiceEntry", seName, clusterId, "success") for _, subset := range destinationRule.Subsets { sseName := seName + common.Dash + subset.Name - rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(r.config.SyncNamespace).Delete(sseName, &v12.DeleteOptions{}) + rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(syncNamespace).Delete(sseName, &v12.DeleteOptions{}) log.Infof(LogFormat, "Delete", "ServiceEntry", sseName, clusterId, "success") } - rc.DestinationRuleController.IstioClient.NetworkingV1alpha3().DestinationRules(r.config.SyncNamespace).Delete(localDrName, &v12.DeleteOptions{}) + rc.DestinationRuleController.IstioClient.NetworkingV1alpha3().DestinationRules(syncNamespace).Delete(localDrName, &v12.DeleteOptions{}) log.Infof(LogFormat, "Delete", "DestinationRule", localDrName, clusterId, "success") } else { - exist, _ := rc.DestinationRuleController.IstioClient.NetworkingV1alpha3().DestinationRules(r.config.SyncNamespace).Get(obj.Name, v12.GetOptions{}) + exist, _ := rc.DestinationRuleController.IstioClient.NetworkingV1alpha3().DestinationRules(syncNamespace).Get(obj.Name, v12.GetOptions{}) //copy destination rule only to other clusters if dependentCluster != clusterId { - addUpdateDestinationRule(obj, exist, r.config.SyncNamespace, rc) + addUpdateDestinationRule(obj, exist, syncNamespace, rc) } if drServiceEntries != nil { for _seName, se := range drServiceEntries { - existsServiceEntry, _ = rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(r.config.SyncNamespace).Get(_seName, v12.GetOptions{}) - newServiceEntry = createServiceEntrySkeletion(*se, _seName, r.config.SyncNamespace) + existsServiceEntry, _ = rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(syncNamespace).Get(_seName, v12.GetOptions{}) + newServiceEntry = createServiceEntrySkeletion(*se, _seName, syncNamespace) if err != nil { log.Warnf(LogErrFormat, "Create", "ServiceEntry", seName, clusterId, err) } if newServiceEntry != nil { - addUpdateServiceEntry(newServiceEntry, existsServiceEntry, r.config.SyncNamespace, rc) + addUpdateServiceEntry(newServiceEntry, existsServiceEntry, syncNamespace, rc) } //cache the subset service entries for updating them later for pod events if dependentCluster == clusterId && se.Resolution == v1alpha32.ServiceEntry_STATIC { @@ -319,7 +319,7 @@ func handleDestinationRuleEvent(obj *v1alpha3.DestinationRule, dh *DestinationRu if dependentCluster == clusterId { //we need a destination rule with local fqdn for destination rules created with cnames to work in local cluster - createDestinationRuleForLocal(rc, localDrName, localIdentityId, clusterId, &destinationRule, r.config.SyncNamespace, r.config.HostnameSuffix, r.config.LabelSet.WorkloadIdentityLabel) + createDestinationRuleForLocal(rc, localDrName, localIdentityId, clusterId, &destinationRule) } } @@ -327,7 +327,7 @@ func handleDestinationRuleEvent(obj *v1alpha3.DestinationRule, dh *DestinationRu } func createDestinationRuleForLocal(remoteController *RemoteController, localDrName string, identityId string, clusterId string, - destinationRule *v1alpha32.DestinationRule, syncNamespace string, nameSuffix string, identifier string) { + destinationRule *v1alpha32.DestinationRule) { deployment := remoteController.DeploymentController.Cache.Get(identityId) @@ -343,9 +343,10 @@ func createDestinationRuleForLocal(remoteController *RemoteController, localDrNa break } + syncNamespace := common.GetSyncNamespace() serviceInstance := getServiceForDeployment(remoteController, deploymentInstance) - cname := common.GetCname(deploymentInstance, identifier, nameSuffix) + cname := common.GetCname(deploymentInstance, common.GetHostnameSuffix(), common.GetWorkloadIdentifier()) if cname == destinationRule.Host { destinationRule.Host = serviceInstance.Name + common.Sep + serviceInstance.Namespace + common.DotLocalDomainSuffix existsDestinationRule, err := remoteController.DestinationRuleController.IstioClient.NetworkingV1alpha3().DestinationRules(syncNamespace).Get(localDrName, v12.GetOptions{}) @@ -371,7 +372,9 @@ func handleVirtualServiceEvent(obj *v1alpha3.VirtualService, vh *VirtualServiceH r := vh.RemoteRegistry - if obj.Namespace == r.config.SyncNamespace { + syncNamespace := common.GetSyncNamespace() + + if obj.Namespace == syncNamespace { log.Infof(LogFormat, "Event", resourceType, obj.Name, clusterId, "Skipping the namespace: "+obj.Namespace) return } @@ -398,11 +401,11 @@ func handleVirtualServiceEvent(obj *v1alpha3.VirtualService, vh *VirtualServiceH if event == common.Delete { log.Infof(LogFormat, "Delete", "VirtualService", obj.Name, clusterId, "Success") - rc.VirtualServiceController.IstioClient.NetworkingV1alpha3().VirtualServices(r.config.SyncNamespace).Delete(obj.Name, &v12.DeleteOptions{}) + rc.VirtualServiceController.IstioClient.NetworkingV1alpha3().VirtualServices(syncNamespace).Delete(obj.Name, &v12.DeleteOptions{}) } else { - exist, _ := rc.VirtualServiceController.IstioClient.NetworkingV1alpha3().VirtualServices(r.config.SyncNamespace).Get(obj.Name, v12.GetOptions{}) + exist, _ := rc.VirtualServiceController.IstioClient.NetworkingV1alpha3().VirtualServices(syncNamespace).Get(obj.Name, v12.GetOptions{}) //change destination host for all http routes .. to same as host on the virtual service for _, httpRoute := range virtualService.Http { @@ -419,7 +422,7 @@ func handleVirtualServiceEvent(obj *v1alpha3.VirtualService, vh *VirtualServiceH } } - addUpdateVirtualService(obj, exist, vh.RemoteRegistry.config.SyncNamespace, rc) + addUpdateVirtualService(obj, exist, syncNamespace, rc) } } diff --git a/admiral/pkg/clusters/registry.go b/admiral/pkg/clusters/registry.go index 749be19e..bf4d08b6 100644 --- a/admiral/pkg/clusters/registry.go +++ b/admiral/pkg/clusters/registry.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/istio-ecosystem/admiral/admiral/pkg/controller/istio" + "k8s.io/client-go/rest" "sync" "time" @@ -11,7 +12,6 @@ import ( "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" "github.com/istio-ecosystem/admiral/admiral/pkg/controller/secret" log "github.com/sirupsen/logrus" - "k8s.io/client-go/rest" ) @@ -20,10 +20,12 @@ const ( LogErrFormat = "op=%s type=%v name=%v cluster=%s, e=%v" ) -func InitAdmiral(ctx context.Context, params AdmiralParams) (*RemoteRegistry, error) { +func InitAdmiral(ctx context.Context, params common.AdmiralParams) (*RemoteRegistry, error) { log.Infof("Initializing Admiral with params: %v", params) + common.InitializeConfig(params) + w := RemoteRegistry{ ctx: ctx, } @@ -38,7 +40,6 @@ func InitAdmiral(ctx context.Context, params AdmiralParams) (*RemoteRegistry, er return nil, fmt.Errorf(" Error with dependency controller init: %v", err) } - w.config = params w.remoteControllers = make(map[string]*RemoteController) w.AdmiralCache = &AdmiralCache{ @@ -51,14 +52,14 @@ func InitAdmiral(ctx context.Context, params AdmiralParams) (*RemoteRegistry, er SubsetServiceEntryIdentityCache: &sync.Map{}, ServiceEntryAddressStore: &ServiceEntryAddressStore{EntryAddresses: map[string]string{}, Addresses: []string{}}} - configMapController, err := admiral.NewConfigMapController(w.config.KubeconfigPath, w.config.SyncNamespace) + configMapController, err := admiral.NewConfigMapController() if err != nil { return nil, fmt.Errorf(" Error with configmap controller init: %v", err) } w.AdmiralCache.ConfigMapController = configMapController loadServiceEntryCacheData(w.AdmiralCache.ConfigMapController, w.AdmiralCache) - err = createSecretController(ctx, &w, params) + err = createSecretController(ctx, &w) if err != nil { return nil, fmt.Errorf(" Error with secret control init: %v", err) } @@ -68,10 +69,10 @@ func InitAdmiral(ctx context.Context, params AdmiralParams) (*RemoteRegistry, er return &w, nil } -func createSecretController(ctx context.Context, w *RemoteRegistry, params AdmiralParams) error { +func createSecretController(ctx context.Context, w *RemoteRegistry) error { var err error - w.secretClient, err = admiral.K8sClientFromPath(params.KubeconfigPath) + w.secretClient, err = admiral.K8sClientFromPath(common.GetKubeconfigPath()) if err != nil { return fmt.Errorf("could not create K8s client: %v", err) } @@ -79,8 +80,8 @@ func createSecretController(ctx context.Context, w *RemoteRegistry, params Admir err = secret.StartSecretController(w.secretClient, w.createCacheController, w.deleteCacheController, - w.config.ClusterRegistriesNamespace, - ctx, params.SecretResolver) + common.GetClusterRegistriesNamespace(), + ctx, common.GetSecretResolver()) if err != nil { return fmt.Errorf("could not start secret controller: %v", err) @@ -108,14 +109,14 @@ func (r *RemoteRegistry) createCacheController(clientConfig *rest.Config, cluste } log.Infof("starting deployment controller clusterID: %v", clusterID) - rc.DeploymentController, err = admiral.NewDeploymentController(stop, &DeploymentHandler{RemoteRegistry: r}, clientConfig, resyncPeriod, r.config.LabelSet) + rc.DeploymentController, err = admiral.NewDeploymentController(stop, &DeploymentHandler{RemoteRegistry: r}, clientConfig, resyncPeriod) if err != nil { return fmt.Errorf(" Error with DeploymentController controller init: %v", err) } log.Infof("starting pod controller clusterID: %v", clusterID) - rc.PodController, err = admiral.NewPodController(stop, &PodHandler{RemoteRegistry: r}, clientConfig, resyncPeriod, r.config.LabelSet) + rc.PodController, err = admiral.NewPodController(stop, &PodHandler{RemoteRegistry: r}, clientConfig, resyncPeriod) if err != nil { return fmt.Errorf(" Error with PodController controller init: %v", err) diff --git a/admiral/pkg/clusters/registry_test.go b/admiral/pkg/clusters/registry_test.go index 705cd237..ceae61f5 100644 --- a/admiral/pkg/clusters/registry_test.go +++ b/admiral/pkg/clusters/registry_test.go @@ -18,6 +18,7 @@ import ( "time" ) + func TestDeleteCacheControllerThatDoesntExist(t *testing.T) { w := RemoteRegistry{ @@ -94,7 +95,7 @@ func TestCreateDestinationRuleForLocalNoDeployLabel(t *testing.T) { Host: "localhost", } - d, e := admiral.NewDeploymentController(make(chan struct{}), &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300), &common.LabelSet{}) + d, e := admiral.NewDeploymentController(make(chan struct{}), &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300)) if e != nil { t.Fail() @@ -111,7 +112,7 @@ func TestCreateDestinationRuleForLocalNoDeployLabel(t *testing.T) { }, } - createDestinationRuleForLocal(&rc, "local.name", "identity", "cluster1", &des, "sync", ".global", "identity") + createDestinationRuleForLocal(&rc, "local.name", "identity", "cluster1", &des) } @@ -133,7 +134,7 @@ func TestCreateDestinationRuleForLocal(t *testing.T) { }, } - createDestinationRuleForLocal(rc, "local.name", "bar", "cluster1", &des, "sync", ".global", "identity") + createDestinationRuleForLocal(rc, "local.name", "bar", "cluster1", &des) } @@ -142,7 +143,7 @@ func createMockRemoteController(f func(interface{})) (*RemoteController, error) Host: "localhost", } stop := make(chan struct{}) - d, e := admiral.NewDeploymentController(stop, &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300), &common.LabelSet{}) + d, e := admiral.NewDeploymentController(stop, &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300)) s, e := admiral.NewServiceController(stop, &test.MockServiceHandler{}, &config, time.Second*time.Duration(300)) n, e := admiral.NewNodeController(stop, &test.MockNodeHandler{}, &config) @@ -192,23 +193,19 @@ func createMockRemoteController(f func(interface{})) (*RemoteController, error) func TestCreateSecretController(t *testing.T) { - p := AdmiralParams{ - KubeconfigPath: "testdata/fake.config", - } - rr := RemoteRegistry{} - err := createSecretController(context.Background(), &rr, p) + err := createSecretController(context.Background(), &rr) if err != nil { t.Fail() } - p = AdmiralParams{ - KubeconfigPath: "fail", - } + common.SetKubeconfigPath("fail") rr = RemoteRegistry{} - err = createSecretController(context.Background(), &rr, p) + err = createSecretController(context.Background(), &rr) + + common.SetKubeconfigPath("testdata/fake.config") if err == nil { t.Fail() @@ -217,10 +214,13 @@ func TestCreateSecretController(t *testing.T) { func TestInitAdmiral(t *testing.T) { - p := AdmiralParams{ + p := common.AdmiralParams{ KubeconfigPath: "testdata/fake.config", + LabelSet: &common.LabelSet{}, } + p.LabelSet.WorkloadIdentityKey="overridden-key" + rr, err := InitAdmiral(context.Background(), p) if err != nil { @@ -229,11 +229,15 @@ func TestInitAdmiral(t *testing.T) { if len(rr.remoteControllers) != 0 { t.Fail() } + + if common.GetWorkloadIdentifier() != "identity" { + t.Errorf("Workload identity label override failed. Expected \"identity\", got %v", common.GetWorkloadIdentifier()) + } } func TestAdded(t *testing.T) { - p := AdmiralParams{ + p := common.AdmiralParams{ KubeconfigPath: "testdata/fake.config", } rr, _ := InitAdmiral(context.Background(), p) @@ -278,7 +282,7 @@ func TestMakeVirtualService(t *testing.T) { func TestDeploymentHandler(t *testing.T) { - p := AdmiralParams{ + p := common.AdmiralParams{ KubeconfigPath: "testdata/fake.config", } @@ -317,7 +321,7 @@ func TestDeploymentHandler(t *testing.T) { func TestPodHandler(t *testing.T) { - p := AdmiralParams{ + p := common.AdmiralParams{ KubeconfigPath: "testdata/fake.config", } diff --git a/admiral/pkg/clusters/serviceentry.go b/admiral/pkg/clusters/serviceentry.go index 036ca57a..62bf01bc 100644 --- a/admiral/pkg/clusters/serviceentry.go +++ b/admiral/pkg/clusters/serviceentry.go @@ -18,10 +18,11 @@ import ( "time" ) -func createServiceEntry(rc *RemoteController, config AdmiralParams, admiralCache *AdmiralCache, +func createServiceEntry(rc *RemoteController, admiralCache *AdmiralCache, destDeployment *k8sAppsV1.Deployment, serviceEntries map[string]*networking.ServiceEntry) *networking.ServiceEntry { - globalFqdn := common.GetCname(destDeployment, config.LabelSet.WorkloadIdentityLabel, config.HostnameSuffix) + workloadIdentityKey := common.GetWorkloadIdentifier() + globalFqdn := common.GetCname(destDeployment, workloadIdentityKey, common.GetHostnameSuffix()) //Handling retries for getting/putting service entries from/in cache @@ -61,10 +62,10 @@ func createServiceEntry(rc *RemoteController, config AdmiralParams, admiralCache } var san []string - if config.EnableSAN { - tmpSan := common.GetSAN(config.SANPrefix, destDeployment, config.LabelSet.WorkloadIdentityLabel) + if common.GetEnableSAN() { + tmpSan := common.GetSAN(common.GetSANPrefix(), destDeployment, workloadIdentityKey) if len(tmpSan) > 0 { - san = []string{common.GetSAN(config.SANPrefix, destDeployment, config.LabelSet.WorkloadIdentityLabel)} + san = []string{common.GetSAN(common.GetSANPrefix(), destDeployment, workloadIdentityKey)} } } else { san = nil @@ -125,7 +126,7 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem serviceInstance := getServiceForDeployment(rc, deploymentInstance[0]) - cname = common.GetCname(deploymentInstance[0], remoteRegistry.config.LabelSet.WorkloadIdentityLabel, cname) + cname = common.GetCname(deploymentInstance[0], common.GetWorkloadIdentifier(), cname) remoteRegistry.AdmiralCache.IdentityClusterCache.Put(sourceIdentity, rc.ClusterID, rc.ClusterID) remoteRegistry.AdmiralCache.CnameClusterCache.Put(cname, rc.ClusterID, rc.ClusterID) @@ -134,7 +135,7 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem sourceDeployments[rc.ClusterID] = deploymentInstance[0] - createServiceEntry(rc, remoteRegistry.config, remoteRegistry.AdmiralCache, deploymentInstance[0], serviceEntries) + createServiceEntry(rc, remoteRegistry.AdmiralCache, deploymentInstance[0], serviceEntries) } @@ -147,8 +148,7 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem remoteRegistry.AdmiralCache.CnameDependentClusterCache.Put(cname, clusterId, clusterId) } - AddServiceEntriesWithDr(remoteRegistry, dependentClusters, remoteRegistry.remoteControllers, serviceEntries, - remoteRegistry.config.SyncNamespace) + AddServiceEntriesWithDr(remoteRegistry.AdmiralCache, dependentClusters, remoteRegistry.remoteControllers, serviceEntries) //update the address to local fqdn for service entry in a cluster local to the service instance for sourceCluster, serviceInstance := range sourceServices { @@ -163,8 +163,8 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem ep.Address = localFqdn oldPorts := ep.Ports ep.Ports = meshPorts - AddServiceEntriesWithDr(remoteRegistry, map[string]string{sourceCluster: sourceCluster}, remoteRegistry.remoteControllers, - map[string]*networking.ServiceEntry{key: serviceEntry}, remoteRegistry.config.SyncNamespace) + AddServiceEntriesWithDr(remoteRegistry.AdmiralCache, map[string]string{sourceCluster: sourceCluster}, remoteRegistry.remoteControllers, + map[string]*networking.ServiceEntry{key: serviceEntry}) //swap it back to use for next iteration ep.Address = clusterIngress ep.Ports = oldPorts @@ -233,8 +233,8 @@ func createSeWithDrLabels(remoteController *RemoteController, localCluster bool, return allSes } -func AddServiceEntriesWithDr(r *RemoteRegistry, sourceClusters map[string]string, rcs map[string]*RemoteController, serviceEntries map[string]*networking.ServiceEntry, - syncNamespace string) { +func AddServiceEntriesWithDr(cache *AdmiralCache, sourceClusters map[string]string, rcs map[string]*RemoteController, serviceEntries map[string]*networking.ServiceEntry) { + syncNamespace := common.GetSyncNamespace() for _, se := range serviceEntries { //add service entry @@ -257,8 +257,8 @@ func AddServiceEntriesWithDr(r *RemoteRegistry, sourceClusters map[string]string //Add a label var identityId string - if identityId, ok := r.AdmiralCache.CnameIdentityCache.Load(se.Hosts[0]); ok { - newServiceEntry.Labels = map[string]string{common.DefaultGlobalIdentifier(): fmt.Sprintf("%v", identityId)} + if identityId, ok := cache.CnameIdentityCache.Load(se.Hosts[0]); ok { + newServiceEntry.Labels = map[string]string{common.GetWorkloadIdentifier(): fmt.Sprintf("%v", identityId)} } if newServiceEntry != nil { diff --git a/admiral/pkg/clusters/types.go b/admiral/pkg/clusters/types.go index 9ee40d89..1b90fa82 100644 --- a/admiral/pkg/clusters/types.go +++ b/admiral/pkg/clusters/types.go @@ -2,7 +2,6 @@ package clusters import ( "context" - "fmt" "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" @@ -13,35 +12,8 @@ import ( k8s "k8s.io/client-go/kubernetes" "sync" - "time" ) -type AdmiralParams struct { - KubeconfigPath string - CacheRefreshDuration time.Duration - ClusterRegistriesNamespace string - DependenciesNamespace string - SyncNamespace string - EnableSAN bool - SANPrefix string - SecretResolver string - LabelSet *common.LabelSet - HostnameSuffix string - -} - -func (b AdmiralParams) String() string { - return fmt.Sprintf("KubeconfigPath=%v ", b.KubeconfigPath) + - fmt.Sprintf("CacheRefreshDuration=%v ", b.CacheRefreshDuration) + - fmt.Sprintf("ClusterRegistriesNamespace=%v ", b.ClusterRegistriesNamespace) + - fmt.Sprintf("DependenciesNamespace=%v ", b.DependenciesNamespace) + - fmt.Sprintf("EnableSAN=%v ", b.EnableSAN) + - fmt.Sprintf("SANPrefix=%v ", b.SANPrefix) + - fmt.Sprintf("LabelSet=%v ", b.LabelSet) + - fmt.Sprintf("SecretResolver=%v ", b.SecretResolver) -} - - type RemoteController struct { ClusterID string GlobalTraffic *admiral.GlobalTrafficController @@ -69,7 +41,6 @@ type AdmiralCache struct { } type RemoteRegistry struct { - config AdmiralParams sync.Mutex remoteControllers map[string]*RemoteController secretClient k8s.Interface @@ -131,7 +102,7 @@ func (dh *DependencyHandler) Added(obj *v1.Dependency) { updateIdentityDependencyCache(sourceIdentity, dh.RemoteRegistry.AdmiralCache.IdentityDependencyCache, obj) - handleDependencyRecord(sourceIdentity, dh.RemoteRegistry, dh.RemoteRegistry.remoteControllers, dh.RemoteRegistry.config, obj) + handleDependencyRecord(sourceIdentity, dh.RemoteRegistry, dh.RemoteRegistry.remoteControllers, obj) } @@ -158,7 +129,7 @@ func (pc *DeploymentHandler) Added(obj *k8sAppsV1.Deployment) { globalIdentifier := common.GetDeploymentGlobalIdentifier(obj) if len(globalIdentifier) == 0 { - log.Infof(LogFormat, "Event", "deployment", obj.Name, "", "Skipped as '"+common.DefaultGlobalIdentifier()+" was not found', namespace="+obj.Namespace) + log.Infof(LogFormat, "Event", "deployment", obj.Name, "", "Skipped as '"+common.GetWorkloadIdentifier()+" was not found', namespace="+obj.Namespace) return } @@ -177,7 +148,7 @@ func (pc *PodHandler) Added(obj *k8sV1.Pod) { globalIdentifier := common.GetPodGlobalIdentifier(obj) if len(globalIdentifier) == 0 { - log.Infof(LogFormat, "Event", "deployment", obj.Name, "", "Skipped as '"+common.DefaultGlobalIdentifier()+" was not found', namespace="+obj.Namespace) + log.Infof(LogFormat, "Event", "deployment", obj.Name, "", "Skipped as '"+common.GetWorkloadIdentifier()+" was not found', namespace="+obj.Namespace) return } diff --git a/admiral/pkg/controller/admiral/configmap.go b/admiral/pkg/controller/admiral/configmap.go index 20ae0a74..3ac403cf 100644 --- a/admiral/pkg/controller/admiral/configmap.go +++ b/admiral/pkg/controller/admiral/configmap.go @@ -1,6 +1,7 @@ package admiral import ( + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" "k8s.io/api/core/v1" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -23,8 +24,9 @@ type ConfigMapController struct { //todo this is a temp state, eventually changes will have to be made to give each cluster it's own configmap -func NewConfigMapController(kubeconfigPath string, namespaceToUse string) (*ConfigMapController, error) { - +func NewConfigMapController() (*ConfigMapController, error) { + kubeconfigPath := common.GetKubeconfigPath() + namespaceToUse := common.GetSyncNamespace() if kubeconfigPath == "" { config, err := rest.InClusterConfig() if err != nil { diff --git a/admiral/pkg/controller/admiral/configmap_test.go b/admiral/pkg/controller/admiral/configmap_test.go index 358990f5..87d26a01 100644 --- a/admiral/pkg/controller/admiral/configmap_test.go +++ b/admiral/pkg/controller/admiral/configmap_test.go @@ -3,6 +3,8 @@ package admiral import ( "errors" "github.com/google/go-cmp/cmp" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + _ "github.com/istio-ecosystem/admiral/admiral/pkg/test" log "github.com/sirupsen/logrus" "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes/fake" @@ -99,7 +101,8 @@ func TestNewConfigMapController(t *testing.T) { for _, c := range testCases { t.Run(c.name, func(t *testing.T) { - controller, err := NewConfigMapController(c.kubeconfigPath, c.namespace) + common.SetKubeconfigPath(c.kubeconfigPath) + controller, err := NewConfigMapController() if err==nil && c.expectedError==nil { //only do these in an error-less context if c.namespace != controller.ConfigmapNamespace { diff --git a/admiral/pkg/controller/admiral/deployment.go b/admiral/pkg/controller/admiral/deployment.go index 49e9be93..c3cac070 100644 --- a/admiral/pkg/controller/admiral/deployment.go +++ b/admiral/pkg/controller/admiral/deployment.go @@ -126,11 +126,11 @@ func (d *DeploymentController) GetDeployments() ([]*k8sAppsV1.Deployment, error) return res, nil } -func NewDeploymentController(stopCh <-chan struct{}, handler DeploymentHandler, config *rest.Config, resyncPeriod time.Duration, labelSet *common.LabelSet) (*DeploymentController, error) { +func NewDeploymentController(stopCh <-chan struct{}, handler DeploymentHandler, config *rest.Config, resyncPeriod time.Duration) (*DeploymentController, error) { deploymentController := DeploymentController{} deploymentController.DeploymentHandler = handler - deploymentController.labelSet = labelSet + deploymentController.labelSet = common.GetLabelSet() deploymentCache := deploymentCache{} deploymentCache.cache = make(map[string]*DeploymentClusterEntry) diff --git a/admiral/pkg/controller/admiral/deployment_test.go b/admiral/pkg/controller/admiral/deployment_test.go index 639fc0e5..ca0d2556 100644 --- a/admiral/pkg/controller/admiral/deployment_test.go +++ b/admiral/pkg/controller/admiral/deployment_test.go @@ -154,7 +154,7 @@ func TestNewDeploymentController(t *testing.T) { stop := make(chan struct{}) depHandler := test.MockDeploymentHandler{} - depCon, err := NewDeploymentController(stop, &depHandler, config, time.Duration(1000), &common.LabelSet{}) + depCon, err := NewDeploymentController(stop, &depHandler, config, time.Duration(1000)) if depCon == nil { t.Errorf("Deployment controller should not be nil") diff --git a/admiral/pkg/controller/admiral/pod.go b/admiral/pkg/controller/admiral/pod.go index 60aa8317..003818a8 100644 --- a/admiral/pkg/controller/admiral/pod.go +++ b/admiral/pkg/controller/admiral/pod.go @@ -127,11 +127,11 @@ func (d *PodController) GetPods() ([]*k8sV1.Pod, error) { return res, nil } -func NewPodController(stopCh <-chan struct{}, handler PodHandler, config *rest.Config, resyncPeriod time.Duration, labelSet *common.LabelSet) (*PodController, error) { +func NewPodController(stopCh <-chan struct{}, handler PodHandler, config *rest.Config, resyncPeriod time.Duration) (*PodController, error) { podController := PodController{} podController.PodHandler = handler - podController.labelSet = labelSet + podController.labelSet = common.GetLabelSet() podCache := podCache{} podCache.cache = make(map[string]*PodClusterEntry) diff --git a/admiral/pkg/controller/admiral/pod_test.go b/admiral/pkg/controller/admiral/pod_test.go index 61563615..ebc68fa3 100644 --- a/admiral/pkg/controller/admiral/pod_test.go +++ b/admiral/pkg/controller/admiral/pod_test.go @@ -20,7 +20,7 @@ func TestNewPodController(t *testing.T) { stop := make(chan struct{}) handler := test.MockPodHandler{} - podController, err := NewPodController(stop, &handler, config, time.Duration(1000), &common.LabelSet{}) + podController, err := NewPodController(stop, &handler, config, time.Duration(1000)) if err != nil { t.Errorf("Unexpected err %v", err) diff --git a/admiral/pkg/controller/common/common.go b/admiral/pkg/controller/common/common.go index 8ec39d61..a7511f2a 100644 --- a/admiral/pkg/controller/common/common.go +++ b/admiral/pkg/controller/common/common.go @@ -45,36 +45,32 @@ const ( ) func GetPodGlobalIdentifier(pod *k8sV1.Pod) string { - identity := pod.Labels[DefaultGlobalIdentifier()] + identity := pod.Labels[GetWorkloadIdentifier()] if len(identity) == 0 { - identity = pod.Annotations[DefaultGlobalIdentifier()] + identity = pod.Annotations[GetWorkloadIdentifier()] } return identity } func GetDeploymentGlobalIdentifier(deployment *k8sAppsV1.Deployment) string { - identity := deployment.Spec.Template.Labels[DefaultGlobalIdentifier()] + identity := deployment.Spec.Template.Labels[GetWorkloadIdentifier()] if len(identity) == 0 { //TODO can this be removed now? This was for backward compatibility - identity = deployment.Spec.Template.Annotations[DefaultGlobalIdentifier()] + identity = deployment.Spec.Template.Annotations[GetWorkloadIdentifier()] } return identity } -func DefaultGlobalIdentifier() string { - return Identity -} - // GetCname returns cname in the format ..global, Ex: stage.Admiral.services.registry.global func GetCname(deployment *k8sAppsV1.Deployment, identifier string, nameSuffix string) string { var environment = GetEnv(deployment) - alias := deployment.Spec.Template.Labels[identifier] + alias := GetValueForKeyFromDeployment(identifier, deployment) if len(alias) == 0 { - log.Warnf("%v label missing on service %v in namespace %v. Falling back to annotation to create cname.", identifier, deployment.Name, deployment.Namespace) + log.Warnf("%v label missing on deployment %v in namespace %v. Falling back to annotation to create cname.", identifier, deployment.Name, deployment.Namespace) alias = deployment.Spec.Template.Annotations[identifier] } if len(alias) == 0 { - log.Errorf("Unable to get cname for service with name %v in namespace %v as it doesn't have the %v annotation", deployment.Name, deployment.Namespace, identifier) + log.Errorf("Unable to get cname for deployment with name %v in namespace %v as it doesn't have the %v annotation", deployment.Name, deployment.Namespace, identifier) return "" } return environment + Sep + alias + Sep + nameSuffix @@ -99,8 +95,9 @@ func GetEnv(deployment *k8sAppsV1.Deployment) string { // GetSAN returns SAN for a service entry in the format spiffe:///, Ex: spiffe://subdomain.domain.com/Admiral.platform.mesh.server func GetSAN(domain string, deployment *k8sAppsV1.Deployment, identifier string) string { - identifierVal := deployment.Spec.Template.Labels[identifier] + identifierVal := GetValueForKeyFromDeployment(identifier, deployment) if len(identifierVal) == 0 { + log.Errorf("Unable to get SAN for deployment with name %v in namespace %v as it doesn't have the %v annotation or label", deployment.Name, deployment.Namespace, identifier) return "" } if len(domain) > 0 { @@ -114,3 +111,12 @@ func GetNodeLocality(node *k8sV1.Node) string { region, _ := node.Labels[NodeRegionLabel] return region } + +func GetValueForKeyFromDeployment(key string, deployment *k8sAppsV1.Deployment) string { + value := deployment.Spec.Template.Labels[key] + if len(value) == 0 { + log.Warnf("%v label missing on deployment %v in namespace %v. Falling back to annotation.", key, deployment.Name, deployment.Namespace) + value = deployment.Spec.Template.Annotations[key] + } + return value +} diff --git a/admiral/pkg/controller/common/common_test.go b/admiral/pkg/controller/common/common_test.go index 304837e9..e6ca55fb 100644 --- a/admiral/pkg/controller/common/common_test.go +++ b/admiral/pkg/controller/common/common_test.go @@ -6,7 +6,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1" "reflect" "testing" -) + ) func TestGetSAN(t *testing.T) { t.Parallel() @@ -16,6 +16,7 @@ func TestGetSAN(t *testing.T) { domain := "preprd" deployment := k8sAppsV1.Deployment{Spec: k8sAppsV1.DeploymentSpec{Template: v12.PodTemplateSpec{ObjectMeta: v1.ObjectMeta{Labels: map[string]string{identifier: identifierVal}}}}} + deploymentWithAnnotation := k8sAppsV1.Deployment{Spec: k8sAppsV1.DeploymentSpec{Template: v12.PodTemplateSpec{ObjectMeta: v1.ObjectMeta{Annotations: map[string]string{identifier: identifierVal}}}}} deploymentWithNoIdentifier := k8sAppsV1.Deployment{} @@ -26,11 +27,17 @@ func TestGetSAN(t *testing.T) { wantSAN string }{ { - name: "should return valid SAN", + name: "should return valid SAN (from label)", deployment: deployment, domain: domain, wantSAN: "spiffe://" + domain + "/" + identifierVal, }, + { + name: "should return valid SAN (from annotation)", + deployment: deploymentWithAnnotation, + domain: domain, + wantSAN: "spiffe://" + domain + "/" + identifierVal, + }, { name: "should return valid SAN with no domain prefix", deployment: deployment, diff --git a/admiral/pkg/controller/common/config.go b/admiral/pkg/controller/common/config.go new file mode 100644 index 00000000..a8471005 --- /dev/null +++ b/admiral/pkg/controller/common/config.go @@ -0,0 +1,79 @@ +package common + +import ( + log "github.com/sirupsen/logrus" + "sync" + "time" +) + +var admiralParams = AdmiralParams{ + LabelSet: &LabelSet{}, +} + +var once sync.Once + +func InitializeConfig(params AdmiralParams) { + var initHappened = false + once.Do(func() { + admiralParams = params + initHappened = true + }) + if !initHappened { + log.Warn("InitializeConfig was called but didn't take effect. It can only be called once, and thus has already been initialized. Please ensure you aren't re-initializing the config.") + } +} + + +func GetAdmiralParams() AdmiralParams { + return admiralParams +} + +func GetKubeconfigPath() string { + return admiralParams.KubeconfigPath +} + +func GetCacheRefreshDuration() time.Duration { + return admiralParams.CacheRefreshDuration +} + +func GetClusterRegistriesNamespace() string { + return admiralParams.ClusterRegistriesNamespace +} + +func GetDependenciesNamespace() string { + return admiralParams.DependenciesNamespace +} + +func GetSyncNamespace() string { + return admiralParams.SyncNamespace +} + +func GetEnableSAN() bool { + return admiralParams.EnableSAN +} + +func GetSANPrefix() string { + return admiralParams.SANPrefix +} + +func GetSecretResolver() string { + return admiralParams.SecretResolver +} + +func GetLabelSet() *LabelSet { + return admiralParams.LabelSet +} + +func GetHostnameSuffix() string { + return admiralParams.HostnameSuffix +} + +func GetWorkloadIdentifier() string { + return admiralParams.LabelSet.WorkloadIdentityKey +} + +///Setters - be careful + +func SetKubeconfigPath(path string) { + admiralParams.KubeconfigPath = path +} \ No newline at end of file diff --git a/admiral/pkg/controller/common/configInitializer.go b/admiral/pkg/controller/common/configInitializer.go new file mode 100644 index 00000000..5080385c --- /dev/null +++ b/admiral/pkg/controller/common/configInitializer.go @@ -0,0 +1,25 @@ +package common + +import "time" + +//Because the admiralParams are not a global singleton, we'll initialize them here and then use setters as needed throughout the tests. Import this package to ensure the initialization happens in your test +//If you're not using anything from the package (and getting an unused import error), import it with the name of _ (_ "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common") +func init() { + p := AdmiralParams{ + KubeconfigPath: "testdata/fake.config", + LabelSet: &LabelSet{}, + EnableSAN: true, + SANPrefix: "prefix", + HostnameSuffix: "mesh", + SyncNamespace: "ns", + CacheRefreshDuration: time.Minute, + ClusterRegistriesNamespace: "default", + DependenciesNamespace: "default", + SecretResolver: "", + + } + + p.LabelSet.WorkloadIdentityKey="identity" + + InitializeConfig(p) +} \ No newline at end of file diff --git a/admiral/pkg/controller/common/config_test.go b/admiral/pkg/controller/common/config_test.go new file mode 100644 index 00000000..e7c5b39c --- /dev/null +++ b/admiral/pkg/controller/common/config_test.go @@ -0,0 +1,72 @@ +package common + +import ( + "testing" + "time" +) + +func TestConfigManagement(t *testing.T) { + + //Initial state comes from the init method in configInitializer.go + //p := AdmiralParams{ + // KubeconfigPath: "testdata/fake.config", + // LabelSet: &LabelSet{}, + // EnableSAN: true, + // SANPrefix: "prefix", + // HostnameSuffix: "mesh", + // SyncNamespace: "ns", + //} + // + //p.LabelSet.WorkloadIdentityKey="identity" + + //trying to initialize again. If the singleton pattern works, none of these will have changed + p := AdmiralParams{ + KubeconfigPath: "DIFFERENT", + LabelSet: &LabelSet{}, + EnableSAN: false, + SANPrefix: "BAD_PREFIX", + HostnameSuffix: "NOT_MESH", + SyncNamespace: "NOT_A_NAMESPACE", + CacheRefreshDuration: time.Hour, + ClusterRegistriesNamespace: "NOT_DEFAULT", + DependenciesNamespace: "NOT_DEFAULT", + SecretResolver: "INSECURE_RESOLVER", + + } + + p.LabelSet.WorkloadIdentityKey="BAD_LABEL" + + InitializeConfig(p) + + if GetWorkloadIdentifier() != "identity" { + t.Errorf("Workload identifier mismatch, expected identity, got %v", GetWorkloadIdentifier()) + } + if GetKubeconfigPath() != "testdata/fake.config" { + t.Errorf("Kubeconfig path mismatch, expected testdata/fake.config, got %v", GetKubeconfigPath()) + } + if GetSANPrefix() != "prefix" { + t.Errorf("San prefix mismatch, expected prefix, got %v", GetSANPrefix()) + } + if GetHostnameSuffix() != "mesh" { + t.Errorf("Hostname suffix mismatch, expected mesh, got %v", GetHostnameSuffix()) + } + if GetSyncNamespace() != "ns" { + t.Errorf("Sync namespace mismatch, expected ns, got %v", GetSyncNamespace()) + } + if GetEnableSAN() != true { + t.Errorf("Enable SAN mismatch, expected true, got %v", GetEnableSAN()) + } + if GetCacheRefreshDuration() != time.Minute { + t.Errorf("Cachee refresh duration mismatch, expected %v, got %v", time.Minute, GetCacheRefreshDuration()) + } + if GetClusterRegistriesNamespace() != "default" { + t.Errorf("Cluster registry namespace mismatch, expected default, got %v", GetClusterRegistriesNamespace()) + } + if GetDependenciesNamespace() != "default" { + t.Errorf("Dependency namespace mismatch, expected default, got %v", GetDependenciesNamespace()) + } + if GetSecretResolver() != "" { + t.Errorf("Secret resolver mismatch, expected empty string, got %v", GetSecretResolver()) + } + +} \ No newline at end of file diff --git a/admiral/pkg/controller/common/types.go b/admiral/pkg/controller/common/types.go index dbe2abda..93903a66 100644 --- a/admiral/pkg/controller/common/types.go +++ b/admiral/pkg/controller/common/types.go @@ -1,7 +1,9 @@ package common import ( + "fmt" "sync" + "time" ) type Map struct { @@ -15,13 +17,39 @@ type MapOfMaps struct { mutex *sync.Mutex } +type AdmiralParams struct { + KubeconfigPath string + CacheRefreshDuration time.Duration + ClusterRegistriesNamespace string + DependenciesNamespace string + SyncNamespace string + EnableSAN bool + SANPrefix string + SecretResolver string + LabelSet *LabelSet + HostnameSuffix string + +} + +func (b AdmiralParams) String() string { + return fmt.Sprintf("KubeconfigPath=%v ", b.KubeconfigPath) + + fmt.Sprintf("CacheRefreshDuration=%v ", b.CacheRefreshDuration) + + fmt.Sprintf("ClusterRegistriesNamespace=%v ", b.ClusterRegistriesNamespace) + + fmt.Sprintf("DependenciesNamespace=%v ", b.DependenciesNamespace) + + fmt.Sprintf("EnableSAN=%v ", b.EnableSAN) + + fmt.Sprintf("SANPrefix=%v ", b.SANPrefix) + + fmt.Sprintf("LabelSet=%v ", b.LabelSet) + + fmt.Sprintf("SecretResolver=%v ", b.SecretResolver) +} + + type LabelSet struct { DeploymentAnnotation string SubsetLabel string NamespaceSidecarInjectionLabel string NamespaceSidecarInjectionLabelValue string AdmiralIgnoreLabel string - WorkloadIdentityLabel 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) } func NewMap() *Map { diff --git a/admiral/pkg/controller/util/util.go b/admiral/pkg/controller/util/util.go index d468840b..f712db23 100644 --- a/admiral/pkg/controller/util/util.go +++ b/admiral/pkg/controller/util/util.go @@ -35,4 +35,4 @@ func Contains(vs []string, t string) bool { } } return false -} +} \ No newline at end of file diff --git a/go.mod b/go.mod index af757104..6f9b864d 100644 --- a/go.mod +++ b/go.mod @@ -6,26 +6,23 @@ require ( github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect github.com/cenkalti/backoff v2.2.1+incompatible github.com/emicklei/go-restful v2.11.1+incompatible // indirect - github.com/ghodss/yaml v1.0.0 github.com/go-openapi/jsonreference v0.19.3 // indirect github.com/go-openapi/spec v0.19.4 // indirect github.com/gogo/googleapis v1.1.0 // indirect github.com/gogo/protobuf v1.3.1 github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc // indirect github.com/golang/protobuf v1.3.2 - github.com/google/btree v1.0.0 // indirect - github.com/google/go-cmp v0.3.1 // indirect + github.com/google/go-cmp v0.3.1 github.com/gorilla/mux v1.7.0 // indirect github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc // indirect - github.com/hashicorp/go-multierror v1.0.0 + github.com/hashicorp/go-multierror v1.0.0 // indirect github.com/imdario/mergo v0.3.8 // indirect github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/lyft/protoc-gen-validate v0.0.13 // indirect github.com/mailru/easyjson v0.7.0 // indirect github.com/natefinch/lumberjack v0.0.0-20170531160350-a96e63847dc3 // indirect github.com/onsi/ginkgo v1.10.2 // indirect - github.com/prometheus/client_golang v1.0.0 - github.com/prometheus/common v0.7.0 + github.com/prometheus/common v0.7.0 // indirect github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v0.0.5 github.com/yl2chen/cidranger v0.0.0-20180214081945-928b519e5268 // indirect @@ -43,14 +40,13 @@ require ( google.golang.org/grpc v1.25.1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v2 v2.2.5 - istio.io/api v0.0.0-20191107224652-6818c03d25b2 - istio.io/client-go v0.0.0-20191104174404-7b65e62d85b0 + istio.io/api v0.0.0-20200226024546-cca495b82b03 + istio.io/client-go v0.0.0-20200226182959-cde3e69bd9dd istio.io/gogo-genproto v0.0.0-20191024203824-d079cc8b1d55 // indirect - k8s.io/api v0.0.0-20191108065827-59e77acf588f - k8s.io/apiextensions-apiserver v0.0.0-20181204003618-e419c5771cdc - k8s.io/apimachinery v0.0.0-20191111054156-6eb29fdf75dc - k8s.io/client-go v0.0.0-20191016111102-bec269661e48 + k8s.io/api v0.17.2 + k8s.io/apiextensions-apiserver v0.0.0-20181204003618-e419c5771cdc // indirect + k8s.io/apimachinery v0.17.2 + k8s.io/client-go v0.17.2 k8s.io/code-generator v0.0.0-20191109100332-a9a0d9c0b3aa // indirect k8s.io/gengo v0.0.0-20191108084044-e500ee069b5c // indirect - k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d // indirect ) diff --git a/go.sum b/go.sum index c7d25b9e..826d6027 100644 --- a/go.sum +++ b/go.sum @@ -186,6 +186,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -205,6 +207,8 @@ github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62F github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= @@ -373,6 +377,7 @@ golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 h1:ACG4HJsFiNMf47Y4PeRoebLNy/2lXT9EtprMuTFWt1M= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191108234033-bd318be0434a h1:R/qVym5WAxsZWQqZCwDY/8sdVKV1m1WgU4/S5IRQAzc= @@ -461,6 +466,7 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg= 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-20190921001708-c4c64cad1fd0 h1:xQwXv67TxFo9nC1GJFyab5eq/5B590r6RlnL/G8Sz7w= golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= @@ -566,8 +572,12 @@ istio.io/api v0.0.0-20191101221011-3fcb499e2a05 h1:DY6qiC77faByWMs584AGxo2g7M0fM istio.io/api v0.0.0-20191101221011-3fcb499e2a05/go.mod h1:+cyHH83OwC0rFpwk8eXctzPNpiCAbB+r6kmMiAxxBHw= istio.io/api v0.0.0-20191107224652-6818c03d25b2 h1:rIP85xkbLc9Df20/Tnz2LxNck7uhOYAEva6crU4C5nE= istio.io/api v0.0.0-20191107224652-6818c03d25b2/go.mod h1:+cyHH83OwC0rFpwk8eXctzPNpiCAbB+r6kmMiAxxBHw= +istio.io/api v0.0.0-20200226024546-cca495b82b03 h1:c0Lsnavz2gRslJxqtqiP7VOxr8EJHJJWgfSJabfnZ2s= +istio.io/api v0.0.0-20200226024546-cca495b82b03/go.mod h1:bcY3prusO/6vA6zGHz4PNG2v79clPyTw06Xx3fprJSQ= istio.io/client-go v0.0.0-20191104174404-7b65e62d85b0 h1:6DWOkG2XZQRAwthO71a0v+AFcONHF6Wo9mtfMNUly24= istio.io/client-go v0.0.0-20191104174404-7b65e62d85b0/go.mod h1:jdpC7NC/WdBGIxcIHPzlurKaCZb+8LnsJW4U4mbX6n0= +istio.io/client-go v0.0.0-20200226182959-cde3e69bd9dd h1:U5eWo5yvlRsk4pr5s6ky63G1dJkAgugXzoiKzkMQxGc= +istio.io/client-go v0.0.0-20200226182959-cde3e69bd9dd/go.mod h1:wLFtAm266NbVvt1Y8ZwbZXCdp1kgnBuxZTAIaMwUkto= istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a h1:w7zILua2dnYo9CxImhpNW4NE/8ZxEoc/wfBfHrhUhrE= 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= @@ -575,12 +585,16 @@ istio.io/gogo-genproto v0.0.0-20191024203824-d079cc8b1d55/go.mod h1:OzpAts7jljZc istio.io/istio v0.0.0-20190214070811-6113e155ac85 h1:93mpP4tAl4mcM2oDkr0VrG6ysFfXEuGZhjE1gXG4/4c= istio.io/istio v0.0.0-20190214070811-6113e155ac85/go.mod h1:OWBySrQjjk549IhxWCt7DTl9ZSsXdvbgm+SmgGVRsGA= istio.io/istio v0.0.0-20191108232952-0419c5da7b6d h1:LSyCcMlMPdNJXnp0rEsi0dHLaxksdaUz8PMe5QTQXjo= +istio.io/istio v0.0.0-20200226212315-574abcf6de40 h1:3YxxG88xvEKTg1SiV/qtRqgkha++u+vU5igph88oFEo= k8s.io/api v0.0.0-20190118113203-912cbe2bfef3 h1:lV0+KGoNkvZOt4zGT4H83hQrzWMt/US/LSz4z4+BQS4= k8s.io/api v0.0.0-20190118113203-912cbe2bfef3/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= k8s.io/api v0.0.0-20191016110408-35e52d86657a h1:VVUE9xTCXP6KUPMf92cQmN88orz600ebexcRRaBTepQ= k8s.io/api v0.0.0-20191016110408-35e52d86657a/go.mod h1:/L5qH+AD540e7Cetbui1tuJeXdmNhO8jM6VkXeDdDhQ= k8s.io/api v0.0.0-20191108065827-59e77acf588f h1:ZTXCVdYGBbAblNUJ5B19ztoy6WHMNrPerxQJF9agLpY= k8s.io/api v0.0.0-20191108065827-59e77acf588f/go.mod h1:uQDmBYHoPSuhbg8FGTRzrOdaNqLiws/LAtBrHv0kN5U= +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/apiextensions-apiserver v0.0.0-20181204003618-e419c5771cdc h1:IOukeE9HtTwpLslbujLDfRpfFU6tsjq28yO0fjnl/hk= k8s.io/apiextensions-apiserver v0.0.0-20181204003618-e419c5771cdc/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE= k8s.io/apiextensions-apiserver v0.0.0-20191014073952-6b6acca910e3 h1:11T1avcLWsxcVNglsvkStUw3GJFjOYxuCb17+88+GI8= @@ -593,10 +607,17 @@ k8s.io/apimachinery v0.0.0-20191108065633-c18f71bf2947 h1:f3H3Rf7KD9fjmmbIMwxBye k8s.io/apimachinery v0.0.0-20191108065633-c18f71bf2947/go.mod h1:nEP/6rwhzfljWYGVS6pfyES3ipZTR19vzMnSM+ur3ho= k8s.io/apimachinery v0.0.0-20191111054156-6eb29fdf75dc h1:hC0UI7qlplCVlRexiPMHwcOCT3IPk9Pgo599vKGOOS4= k8s.io/apimachinery v0.0.0-20191111054156-6eb29fdf75dc/go.mod h1:+6CX7hP4aLfX2sb91JYDMIp0VqDSog2kZu0BHe+lP+s= +k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= +k8s.io/apimachinery v0.17.2 h1:hwDQQFbdRlpnnsR64Asdi55GyCaIP/3WQpMmbNBeWr4= +k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= k8s.io/client-go v0.0.0-20190117233410-4022682532b3 h1:7VVBo3+/iX6dzB8dshNuo6Duds/6AoNP5R59IUnwoxg= k8s.io/client-go v0.0.0-20190117233410-4022682532b3/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= k8s.io/client-go v0.0.0-20191016111102-bec269661e48 h1:C2XVy2z0dV94q9hSSoCuTPp1KOG7IegvbdXuz9VGxoU= k8s.io/client-go v0.0.0-20191016111102-bec269661e48/go.mod h1:hrwktSwYGI4JK+TJA3dMaFyyvHVi/aLarVHpbs8bgCU= +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 v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o= +k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= k8s.io/code-generator v0.0.0-20191108065441-3c1097069dc3 h1:qcl3ardofg2+EAvhYmwX+XMLXyDQ6dOp8yL2ir1JfsM= k8s.io/code-generator v0.0.0-20191108065441-3c1097069dc3/go.mod h1:OJTI2RPXj6kq4bfFqT1JrTEC1S4toTWinGOm1O8jUuY= k8s.io/code-generator v0.0.0-20191109100332-a9a0d9c0b3aa h1:4feCF84yk6VEXdpOwOhwB1YIwobejEwKBzgHY0xa9Co= @@ -622,6 +643,8 @@ k8s.io/utils v0.0.0-20190801114015-581e00157fb1 h1:+ySTxfHnfzZb9ys375PXNlLhkJPLK k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d h1:1P0iBJsBzxRmR+dIFnM+Iu4aLxnoa7lBqozW/0uHbT8= k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= From 9248d8c6249ea30d6677dfd01e7daa6876e3fee7 Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Wed, 26 Feb 2020 15:47:26 -0800 Subject: [PATCH 21/45] Stop publishing tags for PRs (#74) Signed-off-by: Madeline --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 72ee04ca..ce90c650 100644 --- a/Makefile +++ b/Makefile @@ -87,6 +87,7 @@ endif endif ifndef TAG override TAG=$(SHA) +override DO_NOT_PUBLISH=1 endif docker-build: set-tag @@ -94,12 +95,16 @@ docker-build: set-tag docker build -t $(IMAGE):$(TAG) -f ./admiral/docker/Dockerfile.admiral . docker-publish: +ifndef DO_NOT_PUBLISH echo "$(DOCKER_PASS)" | docker login -u $(DOCKER_USER) --password-stdin +endif ifeq ($(TAG),) echo "This is not a Tag/Release, skipping docker publish" else +ifndef DO_NOT_PUBLISH docker push $(IMAGE):$(TAG) endif +endif #no tag set and its master branch, in this case publish `latest` tag ifeq ($(TAG),) ifeq ($(BRANCH),master) From 540ac87ae6a6d4a246ba364726cbca98ef74284d Mon Sep 17 00:00:00 2001 From: josephpeacock <51184065+josephpeacock@users.noreply.github.com> Date: Fri, 28 Feb 2020 16:43:06 -0800 Subject: [PATCH 22/45] Stability fixes (#75) Signed-off-by: Madeline --- admiral/cmd/admiral/cmd/root.go | 6 ++--- admiral/pkg/clusters/handler.go | 2 +- admiral/pkg/clusters/registry_test.go | 21 +++++++++++++++- .../pkg/controller/admiral/configmap_test.go | 23 +++++++++++++++++ admiral/pkg/controller/common/common_test.go | 25 ++++++++++++++++++- .../controller/common/configInitializer.go | 25 ------------------- .../demosinglecluster/envconfig_values.yaml | 2 -- 7 files changed, 71 insertions(+), 33 deletions(-) delete mode 100644 admiral/pkg/controller/common/configInitializer.go diff --git a/admiral/cmd/admiral/cmd/root.go b/admiral/cmd/admiral/cmd/root.go index 54ca4a31..b7fa7c32 100644 --- a/admiral/cmd/admiral/cmd/root.go +++ b/admiral/cmd/admiral/cmd/root.go @@ -55,11 +55,11 @@ func GetRootCmd(args []string) *cobra.Command { rootCmd.PersistentFlags().AddGoFlagSet(flag.CommandLine) rootCmd.PersistentFlags().StringVar(¶ms.KubeconfigPath, "kube_config", "", "Use a Kubernetes configuration file instead of in-cluster configuration") - rootCmd.PersistentFlags().StringVar(¶ms.ClusterRegistriesNamespace, "secret_namespace", "default", + rootCmd.PersistentFlags().StringVar(¶ms.ClusterRegistriesNamespace, "secret_namespace", "admiral", "Namespace to monitor for secrets defaults to admiral-secrets") - rootCmd.PersistentFlags().StringVar(¶ms.DependenciesNamespace, "dependency_namespace", "default", + rootCmd.PersistentFlags().StringVar(¶ms.DependenciesNamespace, "dependency_namespace", "admiral", "Namespace to monitor for secrets defaults to admiral-secrets") - rootCmd.PersistentFlags().StringVar(¶ms.SyncNamespace, "sync_namespace", "default", + rootCmd.PersistentFlags().StringVar(¶ms.SyncNamespace, "sync_namespace", "admiral-sync", "Namespace to monitor for secrets defaults to admiral-secrets") rootCmd.PersistentFlags().DurationVar(¶ms.CacheRefreshDuration, "sync_period", 5*time.Minute, "Interval for syncing Kubernetes resources, defaults to 5 min") diff --git a/admiral/pkg/clusters/handler.go b/admiral/pkg/clusters/handler.go index d54e453a..aeeae08d 100644 --- a/admiral/pkg/clusters/handler.go +++ b/admiral/pkg/clusters/handler.go @@ -455,7 +455,7 @@ func addUpdateVirtualService(obj *v1alpha3.VirtualService, exist *v1alpha3.Virtu func addUpdateServiceEntry(obj *v1alpha3.ServiceEntry, exist *v1alpha3.ServiceEntry, namespace string, rc *RemoteController) { var err error var op string - if exist == nil { + if exist == nil || exist.Spec.Hosts == nil { obj.Namespace = namespace obj.ResourceVersion = "" _, err = rc.ServiceEntryController.IstioClient.NetworkingV1alpha3().ServiceEntries(namespace).Create(obj) diff --git a/admiral/pkg/clusters/registry_test.go b/admiral/pkg/clusters/registry_test.go index ceae61f5..9b45e102 100644 --- a/admiral/pkg/clusters/registry_test.go +++ b/admiral/pkg/clusters/registry_test.go @@ -18,6 +18,26 @@ import ( "time" ) +func init() { + p := common.AdmiralParams{ + KubeconfigPath: "testdata/fake.config", + LabelSet: &common.LabelSet{}, + EnableSAN: true, + SANPrefix: "prefix", + HostnameSuffix: "mesh", + SyncNamespace: "ns", + CacheRefreshDuration: time.Minute, + ClusterRegistriesNamespace: "default", + DependenciesNamespace: "default", + SecretResolver: "", + + } + + p.LabelSet.WorkloadIdentityKey="identity" + + common.InitializeConfig(p) +} + func TestDeleteCacheControllerThatDoesntExist(t *testing.T) { @@ -192,7 +212,6 @@ func createMockRemoteController(f func(interface{})) (*RemoteController, error) } func TestCreateSecretController(t *testing.T) { - rr := RemoteRegistry{} err := createSecretController(context.Background(), &rr) diff --git a/admiral/pkg/controller/admiral/configmap_test.go b/admiral/pkg/controller/admiral/configmap_test.go index 87d26a01..7ca69c0b 100644 --- a/admiral/pkg/controller/admiral/configmap_test.go +++ b/admiral/pkg/controller/admiral/configmap_test.go @@ -9,8 +9,31 @@ import ( "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes/fake" "testing" + "time" ) +func init() { + p := common.AdmiralParams{ + KubeconfigPath: "testdata/fake.config", + LabelSet: &common.LabelSet{}, + EnableSAN: true, + SANPrefix: "prefix", + HostnameSuffix: "mesh", + SyncNamespace: "ns", + CacheRefreshDuration: time.Minute, + ClusterRegistriesNamespace: "default", + DependenciesNamespace: "default", + SecretResolver: "", + + } + + p.LabelSet.WorkloadIdentityKey="identity" + + common.InitializeConfig(p) +} + + + func TestConfigMapController_GetConfigMap(t *testing.T) { configmapController := ConfigMapController{ ConfigmapNamespace: "admiral", diff --git a/admiral/pkg/controller/common/common_test.go b/admiral/pkg/controller/common/common_test.go index e6ca55fb..37505a7b 100644 --- a/admiral/pkg/controller/common/common_test.go +++ b/admiral/pkg/controller/common/common_test.go @@ -6,7 +6,30 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1" "reflect" "testing" - ) + "time" +) + +func init() { + p := AdmiralParams{ + KubeconfigPath: "testdata/fake.config", + LabelSet: &LabelSet{}, + EnableSAN: true, + SANPrefix: "prefix", + HostnameSuffix: "mesh", + SyncNamespace: "ns", + CacheRefreshDuration: time.Minute, + ClusterRegistriesNamespace: "default", + DependenciesNamespace: "default", + SecretResolver: "", + + } + + p.LabelSet.WorkloadIdentityKey="identity" + + InitializeConfig(p) +} + + func TestGetSAN(t *testing.T) { t.Parallel() diff --git a/admiral/pkg/controller/common/configInitializer.go b/admiral/pkg/controller/common/configInitializer.go deleted file mode 100644 index 5080385c..00000000 --- a/admiral/pkg/controller/common/configInitializer.go +++ /dev/null @@ -1,25 +0,0 @@ -package common - -import "time" - -//Because the admiralParams are not a global singleton, we'll initialize them here and then use setters as needed throughout the tests. Import this package to ensure the initialization happens in your test -//If you're not using anything from the package (and getting an unused import error), import it with the name of _ (_ "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common") -func init() { - p := AdmiralParams{ - KubeconfigPath: "testdata/fake.config", - LabelSet: &LabelSet{}, - EnableSAN: true, - SANPrefix: "prefix", - HostnameSuffix: "mesh", - SyncNamespace: "ns", - CacheRefreshDuration: time.Minute, - ClusterRegistriesNamespace: "default", - DependenciesNamespace: "default", - SecretResolver: "", - - } - - p.LabelSet.WorkloadIdentityKey="identity" - - InitializeConfig(p) -} \ No newline at end of file diff --git a/install/admiral/overlays/demosinglecluster/envconfig_values.yaml b/install/admiral/overlays/demosinglecluster/envconfig_values.yaml index 95ab838b..e07355c5 100644 --- a/install/admiral/overlays/demosinglecluster/envconfig_values.yaml +++ b/install/admiral/overlays/demosinglecluster/envconfig_values.yaml @@ -10,8 +10,6 @@ spec: spec: containers: - args: - - --log_output_level - - info - --dependency_namespace - admiral - --secret_namespace From ee1ccb3dd6335d5893a7dd7f7684d86d8c454cef Mon Sep 17 00:00:00 2001 From: Martin Baillie Date: Tue, 3 Mar 2020 09:15:10 +1100 Subject: [PATCH 23/45] Skip service instances that were not matched with deployments (#73) Signed-off-by: Martin Baillie Signed-off-by: Madeline --- admiral/pkg/clusters/serviceentry.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/admiral/pkg/clusters/serviceentry.go b/admiral/pkg/clusters/serviceentry.go index 62bf01bc..5a6d2e53 100644 --- a/admiral/pkg/clusters/serviceentry.go +++ b/admiral/pkg/clusters/serviceentry.go @@ -125,6 +125,9 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem deploymentInstance := deployment.Deployments[env] serviceInstance := getServiceForDeployment(rc, deploymentInstance[0]) + if serviceInstance == nil { + continue + } cname = common.GetCname(deploymentInstance[0], common.GetWorkloadIdentifier(), cname) From 1f72175d89b482c99042517f99881631d1851006 Mon Sep 17 00:00:00 2001 From: josephpeacock <51184065+josephpeacock@users.noreply.github.com> Date: Mon, 2 Mar 2020 15:41:20 -0800 Subject: [PATCH 24/45] Fixing demo to use admiral-sync for sync namespace (#77) * Fixing demo to use admiral-sync for sync namespace Signed-off-by: Joe Peacock * Missed on Signed-off-by: Joe Peacock Signed-off-by: Madeline --- install/admiral/base/deployments.yaml | 1 + install/admiral/base/role_bindings.yaml | 4 +++- install/admiral/base/roles.yaml | 4 +++- install/admiral/base/service_accounts.yaml | 1 + .../admiral/overlays/demosinglecluster/envconfig_values.yaml | 3 ++- install/admiral/overlays/demosinglecluster/kustomization.yaml | 2 -- 6 files changed, 10 insertions(+), 5 deletions(-) diff --git a/install/admiral/base/deployments.yaml b/install/admiral/base/deployments.yaml index 44bb57d8..1c715432 100644 --- a/install/admiral/base/deployments.yaml +++ b/install/admiral/base/deployments.yaml @@ -5,6 +5,7 @@ metadata: labels: app: admiral name: admiral + namespace: admiral spec: replicas: 1 selector: diff --git a/install/admiral/base/role_bindings.yaml b/install/admiral/base/role_bindings.yaml index 61dbd45c..0fcf3414 100644 --- a/install/admiral/base/role_bindings.yaml +++ b/install/admiral/base/role_bindings.yaml @@ -4,6 +4,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: admiral-secret-role-binding + namespace: admiral roleRef: apiGroup: rbac.authorization.k8s.io kind: Role @@ -19,6 +20,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: admiral-dependency-role-binding + namespace: admiral roleRef: apiGroup: rbac.authorization.k8s.io kind: Role @@ -34,7 +36,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: admiral-configmap-role-binding - namespace: admiral + namespace: admiral-sync roleRef: apiGroup: rbac.authorization.k8s.io kind: Role diff --git a/install/admiral/base/roles.yaml b/install/admiral/base/roles.yaml index 28c55335..f7f71c68 100644 --- a/install/admiral/base/roles.yaml +++ b/install/admiral/base/roles.yaml @@ -4,6 +4,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: admiral-dependency-role + namespace: admiral rules: - apiGroups: ["admiral.io"] resources: ["dependencies"] @@ -15,6 +16,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: admiral-secret-role + namespace: admiral rules: - apiGroups: [""] resources: ["secrets"] @@ -26,7 +28,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: admiral-configmap-role - namespace: admiral + namespace: admiral-sync rules: - apiGroups: [""] resources: ["configmaps"] diff --git a/install/admiral/base/service_accounts.yaml b/install/admiral/base/service_accounts.yaml index 74adbb26..beaffea2 100644 --- a/install/admiral/base/service_accounts.yaml +++ b/install/admiral/base/service_accounts.yaml @@ -4,6 +4,7 @@ apiVersion: v1 kind: ServiceAccount metadata: name: admiral + namespace: admiral labels: app: admiral --- \ No newline at end of file diff --git a/install/admiral/overlays/demosinglecluster/envconfig_values.yaml b/install/admiral/overlays/demosinglecluster/envconfig_values.yaml index e07355c5..13fce7f9 100644 --- a/install/admiral/overlays/demosinglecluster/envconfig_values.yaml +++ b/install/admiral/overlays/demosinglecluster/envconfig_values.yaml @@ -4,6 +4,7 @@ apiVersion: apps/v1beta2 kind: Deployment metadata: name: admiral + namespace: admiral spec: template: @@ -15,5 +16,5 @@ spec: - --secret_namespace - admiral - --sync_namespace - - admiral + - admiral-sync name: admiral \ No newline at end of file diff --git a/install/admiral/overlays/demosinglecluster/kustomization.yaml b/install/admiral/overlays/demosinglecluster/kustomization.yaml index edf6350e..fd84fed8 100644 --- a/install/admiral/overlays/demosinglecluster/kustomization.yaml +++ b/install/admiral/overlays/demosinglecluster/kustomization.yaml @@ -1,8 +1,6 @@ apiversion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -namespace: admiral - images: - name: docker.io/admiralproj/admiral newTag: v0.1-beta From d5367b9929b87227b2c2ad4afbf7c30b02e96fb0 Mon Sep 17 00:00:00 2001 From: josephpeacock <51184065+josephpeacock@users.noreply.github.com> Date: Mon, 2 Mar 2020 16:57:27 -0800 Subject: [PATCH 25/45] fixed bug in adding to cnameidentitycache and variably assignment bug in getting GTP identity (#79) Signed-off-by: Joe Peacock Signed-off-by: Madeline --- admiral/cmd/admiral/cmd/root.go | 2 +- admiral/pkg/clusters/serviceentry.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/admiral/cmd/admiral/cmd/root.go b/admiral/cmd/admiral/cmd/root.go index b7fa7c32..ff4143df 100644 --- a/admiral/cmd/admiral/cmd/root.go +++ b/admiral/cmd/admiral/cmd/root.go @@ -70,7 +70,7 @@ func GetRootCmd(args []string) *cobra.Command { rootCmd.PersistentFlags().StringVar(¶ms.SecretResolver, "secret_resolver", "", "Type of resolver to use to fetch kubeconfig for monitored clusters") rootCmd.PersistentFlags().StringVar(¶ms.LabelSet.DeploymentAnnotation, "deployment_annotation", "sidecar.istio.io/inject", - "The annotation, on a deployment, which must be set to \"true\" for Admiral to listen on the deployment") + "The annotation, on a pod spec in a deployment, which must be set to \"true\" for Admiral to listen on the deployment") rootCmd.PersistentFlags().StringVar(¶ms.LabelSet.SubsetLabel, "subset_label", "subset", "The label, on a deployment, tells admiral which target group this deployment is a part of. Used for traffic splits via the Global Traffic Policy object") rootCmd.PersistentFlags().StringVar(¶ms.LabelSet.NamespaceSidecarInjectionLabel, "namespace_injected_label", "istio-injection", diff --git a/admiral/pkg/clusters/serviceentry.go b/admiral/pkg/clusters/serviceentry.go index 5a6d2e53..cd8f6a45 100644 --- a/admiral/pkg/clusters/serviceentry.go +++ b/admiral/pkg/clusters/serviceentry.go @@ -129,7 +129,7 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem continue } - cname = common.GetCname(deploymentInstance[0], common.GetWorkloadIdentifier(), cname) + cname = common.GetCname(deploymentInstance[0], common.GetWorkloadIdentifier(), common.GetHostnameSuffix()) remoteRegistry.AdmiralCache.IdentityClusterCache.Put(sourceIdentity, rc.ClusterID, rc.ClusterID) remoteRegistry.AdmiralCache.CnameClusterCache.Put(cname, rc.ClusterID, rc.ClusterID) @@ -260,7 +260,8 @@ func AddServiceEntriesWithDr(cache *AdmiralCache, sourceClusters map[string]stri //Add a label var identityId string - if identityId, ok := cache.CnameIdentityCache.Load(se.Hosts[0]); ok { + if identityValue, ok := cache.CnameIdentityCache.Load(se.Hosts[0]); ok { + identityId = fmt.Sprint(identityValue) newServiceEntry.Labels = map[string]string{common.GetWorkloadIdentifier(): fmt.Sprintf("%v", identityId)} } From 8c496cd27c7bb9d5fcbb0117ade5cd90a83cf9c1 Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Mon, 2 Mar 2020 16:59:51 -0800 Subject: [PATCH 26/45] Fix default. Add sync interval. (#78) Signed-off-by: Madeline --- install/admiral/base/deployments.yaml | 18 +++++++----------- .../demosinglecluster/envconfig_values.yaml | 2 ++ .../demosinglecluster/kustomization.yaml | 2 +- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/install/admiral/base/deployments.yaml b/install/admiral/base/deployments.yaml index 1c715432..a9cbe4df 100644 --- a/install/admiral/base/deployments.yaml +++ b/install/admiral/base/deployments.yaml @@ -19,19 +19,15 @@ spec: serviceAccountName: admiral containers: - args: - - --log_output_level - - $(log_output_level) - --dependency_namespace - - $(dependency_namespace) + - admiral - --secret_namespace - - $(secret_namespace) - - --san_domain - - $(san_domain) - - --secret_resolver - - $(secret_resolver) - - --secret_resolver_config_path - - /etc/admiral/config.yaml - image: docker.io/admiralproj/admiral:v0.1-beta + - admiral + - --sync_namespace + - admiral-sync + - --sync_period + - 20s + image: docker.io/admiralproj/admiral:latest # livenessProbe: # failureThreshold: 5 # httpGet: diff --git a/install/admiral/overlays/demosinglecluster/envconfig_values.yaml b/install/admiral/overlays/demosinglecluster/envconfig_values.yaml index 13fce7f9..79057287 100644 --- a/install/admiral/overlays/demosinglecluster/envconfig_values.yaml +++ b/install/admiral/overlays/demosinglecluster/envconfig_values.yaml @@ -17,4 +17,6 @@ spec: - admiral - --sync_namespace - admiral-sync + - --sync_period + - 20s name: admiral \ No newline at end of file diff --git a/install/admiral/overlays/demosinglecluster/kustomization.yaml b/install/admiral/overlays/demosinglecluster/kustomization.yaml index fd84fed8..c2ef32d6 100644 --- a/install/admiral/overlays/demosinglecluster/kustomization.yaml +++ b/install/admiral/overlays/demosinglecluster/kustomization.yaml @@ -3,7 +3,7 @@ kind: Kustomization images: - name: docker.io/admiralproj/admiral - newTag: v0.1-beta + newTag: latest bases: - ../../base From cb58c6fdcd20093576ab9695af5b0662b7f6c3de Mon Sep 17 00:00:00 2001 From: josephpeacock <51184065+josephpeacock@users.noreply.github.com> Date: Tue, 10 Mar 2020 16:33:49 -0700 Subject: [PATCH 27/45] Linking GTPs with Deployments (#80) Tying GTP and Deployments together with a common label Signed-off-by: Joe Peacock Co-authored-by: Joe Peacock Signed-off-by: Madeline --- admiral/cmd/admiral/cmd/root.go | 6 +- admiral/pkg/clusters/handler.go | 5 - admiral/pkg/clusters/registry.go | 13 +- admiral/pkg/clusters/registry_test.go | 40 +- admiral/pkg/clusters/serviceentry.go | 3 +- admiral/pkg/clusters/types.go | 189 +++++- admiral/pkg/clusters/types_test.go | 585 ++++++++++++++++++ .../pkg/controller/admiral/configmap_test.go | 1 + admiral/pkg/controller/admiral/deployment.go | 19 + .../pkg/controller/admiral/deployment_test.go | 126 ++++ .../pkg/controller/admiral/globaltraffic.go | 60 +- admiral/pkg/controller/common/common.go | 118 +++- admiral/pkg/controller/common/common_test.go | 336 +++++++++- admiral/pkg/controller/common/config.go | 4 + admiral/pkg/controller/common/config_test.go | 4 + admiral/pkg/controller/common/types.go | 1 + go.mod | 26 +- go.sum | 39 ++ 18 files changed, 1437 insertions(+), 138 deletions(-) create mode 100644 admiral/pkg/clusters/types_test.go diff --git a/admiral/cmd/admiral/cmd/root.go b/admiral/cmd/admiral/cmd/root.go index ff4143df..540a2ab1 100644 --- a/admiral/cmd/admiral/cmd/root.go +++ b/admiral/cmd/admiral/cmd/root.go @@ -58,9 +58,9 @@ func GetRootCmd(args []string) *cobra.Command { rootCmd.PersistentFlags().StringVar(¶ms.ClusterRegistriesNamespace, "secret_namespace", "admiral", "Namespace to monitor for secrets defaults to admiral-secrets") rootCmd.PersistentFlags().StringVar(¶ms.DependenciesNamespace, "dependency_namespace", "admiral", - "Namespace to monitor for secrets defaults to admiral-secrets") + "Namespace to monitor for changes to dependency objects") rootCmd.PersistentFlags().StringVar(¶ms.SyncNamespace, "sync_namespace", "admiral-sync", - "Namespace to monitor for secrets defaults to admiral-secrets") + "Namespace in which Admiral will put its generated configurations") rootCmd.PersistentFlags().DurationVar(¶ms.CacheRefreshDuration, "sync_period", 5*time.Minute, "Interval for syncing Kubernetes resources, defaults to 5 min") rootCmd.PersistentFlags().BoolVar(¶ms.EnableSAN, "enable_san", false, @@ -83,6 +83,8 @@ func GetRootCmd(args []string) *cobra.Command { "The hostname suffix to customize the cname generated by admiral. Default suffix value will be \"global\"") rootCmd.PersistentFlags().StringVar(¶ms.LabelSet.WorkloadIdentityKey, "workload_identity_key", "identity", "The workload identity key, on deployment which holds identity value used to generate cname by admiral. Default label key will be \"identity\" Admiral will look for a label with this key. If present, that will be used. If not, it will try an annotation (for use cases where an identity is longer than 63 chars)") + rootCmd.PersistentFlags().StringVar(¶ms.LabelSet.GlobalTrafficDeploymentLabel, "globaltraffic_deployment_label", "identity", + "The label key which will be used to tie globaltrafficpolicy objects to deployments. Configured separately to the workload identity key because this one won't fall back to annotations.") return rootCmd } diff --git a/admiral/pkg/clusters/handler.go b/admiral/pkg/clusters/handler.go index aeeae08d..e60408e6 100644 --- a/admiral/pkg/clusters/handler.go +++ b/admiral/pkg/clusters/handler.go @@ -116,11 +116,6 @@ func getIstioResourceName(host string, suffix string) string { return strings.ToLower(host) + suffix } -//TODO use selector on pod, instead of hardcoded identityId -func getMatchingGlobalTrafficPolicy(rc *RemoteController, identityId string) *v1.GlobalTrafficPolicy { - return rc.GlobalTraffic.Cache.Get(identityId) -} - func makeVirtualService(host string, destination string, port uint32) *v1alpha32.VirtualService { return &v1alpha32.VirtualService{Hosts: []string{host}, Gateways: []string{common.MulticlusterIngressGateway}, diff --git a/admiral/pkg/clusters/registry.go b/admiral/pkg/clusters/registry.go index bf4d08b6..5b90aa1a 100644 --- a/admiral/pkg/clusters/registry.go +++ b/admiral/pkg/clusters/registry.go @@ -3,7 +3,9 @@ package clusters import ( "context" "fmt" + "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" "github.com/istio-ecosystem/admiral/admiral/pkg/controller/istio" + v12 "k8s.io/api/apps/v1" "k8s.io/client-go/rest" "sync" "time" @@ -12,7 +14,6 @@ import ( "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" "github.com/istio-ecosystem/admiral/admiral/pkg/controller/secret" log "github.com/sirupsen/logrus" - ) const ( @@ -42,6 +43,11 @@ func InitAdmiral(ctx context.Context, params common.AdmiralParams) (*RemoteRegis w.remoteControllers = make(map[string]*RemoteController) + gtpCache := &globalTrafficCache{} + gtpCache.identityCache = make(map[string]*v1.GlobalTrafficPolicy) + gtpCache.dependencyCache = make(map[string]*v12.Deployment) + gtpCache.mutex = &sync.Mutex{} + w.AdmiralCache = &AdmiralCache{ IdentityClusterCache: common.NewMapOfMaps(), CnameClusterCache: common.NewMapOfMaps(), @@ -50,7 +56,9 @@ func InitAdmiral(ctx context.Context, params common.AdmiralParams) (*RemoteRegis IdentityDependencyCache: common.NewMapOfMaps(), CnameIdentityCache: &sync.Map{}, SubsetServiceEntryIdentityCache: &sync.Map{}, - ServiceEntryAddressStore: &ServiceEntryAddressStore{EntryAddresses: map[string]string{}, Addresses: []string{}}} + ServiceEntryAddressStore: &ServiceEntryAddressStore{EntryAddresses: map[string]string{}, Addresses: []string{}}, + GlobalTrafficCache: gtpCache, + } configMapController, err := admiral.NewConfigMapController() if err != nil { @@ -102,6 +110,7 @@ func (r *RemoteRegistry) createCacheController(clientConfig *rest.Config, cluste var err error log.Infof("starting global traffic policy controller custerID: %v", clusterID) + rc.GlobalTraffic, err = admiral.NewGlobalTrafficController(stop, &GlobalTrafficHandler{RemoteRegistry: r}, clientConfig, resyncPeriod) if err != nil { diff --git a/admiral/pkg/clusters/registry_test.go b/admiral/pkg/clusters/registry_test.go index 9b45e102..01f0067f 100644 --- a/admiral/pkg/clusters/registry_test.go +++ b/admiral/pkg/clusters/registry_test.go @@ -34,6 +34,7 @@ func init() { } p.LabelSet.WorkloadIdentityKey="identity" + p.LabelSet.GlobalTrafficDeploymentLabel="identity" common.InitializeConfig(p) } @@ -299,45 +300,6 @@ func TestMakeVirtualService(t *testing.T) { } } -func TestDeploymentHandler(t *testing.T) { - - p := common.AdmiralParams{ - KubeconfigPath: "testdata/fake.config", - } - - rr, _ := InitAdmiral(context.Background(), p) - - rc, _ := createMockRemoteController(func(i interface{}) { - - }) - rr.remoteControllers["test.cluster"] = rc - - dh := DeploymentHandler{ - RemoteRegistry: rr, - } - - deployment := k8sAppsV1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test", - }, - Spec: k8sAppsV1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"identity": "bar"}, - }, - Template: k8sCoreV1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{"identity": "bar", "istio-injected": "true", "env": "dev"}, - }, - }, - }, - } - - dh.Added(&deployment) - - dh.Deleted(&deployment) -} - func TestPodHandler(t *testing.T) { p := common.AdmiralParams{ diff --git a/admiral/pkg/clusters/serviceentry.go b/admiral/pkg/clusters/serviceentry.go index cd8f6a45..54dab051 100644 --- a/admiral/pkg/clusters/serviceentry.go +++ b/admiral/pkg/clusters/serviceentry.go @@ -15,6 +15,7 @@ import ( "math" "math/rand" "strconv" + "strings" "time" ) @@ -275,7 +276,7 @@ func AddServiceEntriesWithDr(cache *AdmiralCache, sourceClusters map[string]stri oldDestinationRule = nil } - globalTrafficPolicy := getMatchingGlobalTrafficPolicy(rc, identityId) + globalTrafficPolicy := cache.GlobalTrafficCache.GetFromIdentity(identityId, strings.Split(se.Hosts[0], ".")[0]) destinationRule := getDestinationRule(se.Hosts[0], rc.NodeController.Locality.Region, globalTrafficPolicy) diff --git a/admiral/pkg/clusters/types.go b/admiral/pkg/clusters/types.go index 1b90fa82..37bfa381 100644 --- a/admiral/pkg/clusters/types.go +++ b/admiral/pkg/clusters/types.go @@ -2,6 +2,8 @@ package clusters import ( "context" + "errors" + "fmt" "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" @@ -15,16 +17,16 @@ import ( ) type RemoteController struct { - ClusterID string - GlobalTraffic *admiral.GlobalTrafficController - DeploymentController *admiral.DeploymentController - ServiceController *admiral.ServiceController - PodController *admiral.PodController - NodeController *admiral.NodeController - ServiceEntryController *istio.ServiceEntryController - DestinationRuleController * istio.DestinationRuleController - VirtualServiceController * istio.VirtualServiceController - stop chan struct{} + ClusterID string + GlobalTraffic *admiral.GlobalTrafficController + DeploymentController *admiral.DeploymentController + ServiceController *admiral.ServiceController + PodController *admiral.PodController + NodeController *admiral.NodeController + ServiceEntryController *istio.ServiceEntryController + DestinationRuleController *istio.DestinationRuleController + VirtualServiceController *istio.VirtualServiceController + stop chan struct{} //listener for normal types } @@ -36,8 +38,9 @@ type AdmiralCache struct { ClusterLocalityCache *common.MapOfMaps IdentityDependencyCache *common.MapOfMaps SubsetServiceEntryIdentityCache *sync.Map - ServiceEntryAddressStore *ServiceEntryAddressStore - ConfigMapController admiral.ConfigMapControllerInterface //todo this should be in the remotecontrollers map once we expand it to have one configmap per cluster + ServiceEntryAddressStore *ServiceEntryAddressStore + ConfigMapController admiral.ConfigMapControllerInterface //todo this should be in the remotecontrollers map once we expand it to have one configmap per cluster + GlobalTrafficCache *globalTrafficCache //The cache needs to live in the handler because it needs access to deployments } type RemoteRegistry struct { @@ -61,8 +64,8 @@ func (r *RemoteRegistry) shutdown() { } type ServiceEntryAddressStore struct { - EntryAddresses map[string]string `yaml:"entry-addresses,omitempty"` - Addresses []string `yaml:"addresses,omitempty"` //trading space for efficiency - this will give a quick way to validate that the address is unique + EntryAddresses map[string]string `yaml:"entry-addresses,omitempty"` + Addresses []string `yaml:"addresses,omitempty"` //trading space for efficiency - this will give a quick way to validate that the address is unique } type DependencyHandler struct { @@ -74,6 +77,83 @@ type GlobalTrafficHandler struct { RemoteRegistry *RemoteRegistry } +type globalTrafficCache struct { + //map of global traffic policies key=environment.identity, value: GlobalTrafficPolicy object + identityCache map[string]*v1.GlobalTrafficPolicy + + //map of dependencies. key=namespace.globaltrafficpolicy name. value Deployment object + dependencyCache map[string]*k8sAppsV1.Deployment + + mutex *sync.Mutex +} + +func (g *globalTrafficCache) GetFromIdentity(identity string, environment string) *v1.GlobalTrafficPolicy { + return g.identityCache[getCacheKey(environment, identity)] +} + +func (g *globalTrafficCache) GetDeployment(gtpName string) *k8sAppsV1.Deployment { + return g.dependencyCache[gtpName] +} + +func (g *globalTrafficCache) Put(gtp *v1.GlobalTrafficPolicy, deployment *k8sAppsV1.Deployment) error { + if gtp.Name == "" { + //no GTP, throw error + return errors.New("cannot add an empty globaltrafficpolicy to the cache") + } + defer g.mutex.Unlock() + g.mutex.Lock() + if deployment != nil && deployment.Labels != nil { + log.Infof("Adding Deployment with name %v and gtp with name %v to GTP cache. LabelMatch=%v env=%v", deployment.Name, gtp.Name,gtp.Labels[common.GetGlobalTrafficDeploymentLabel()], gtp.Labels[common.Env]) + //we have a valid deployment + env := deployment.Spec.Template.Labels[common.Env] + if env == "" { + //No environment label, use default value + env = common.Default + } + identity := deployment.Labels[common.GetWorkloadIdentifier()] + key := getCacheKey(env, identity) + g.identityCache[key] = gtp + } else if g.dependencyCache[gtp.Name] != nil { + log.Infof("Adding gtp with name %v to GTP cache. LabelMatch=%v env=%v", gtp.Name,gtp.Labels[common.GetGlobalTrafficDeploymentLabel()], gtp.Labels[common.Env]) + //The old GTP matched a deployment, the new one doesn't. So we need to clear that cache. + oldDeployment := g.dependencyCache[gtp.Name] + env := oldDeployment.Spec.Template.Labels[common.Env] + identity := oldDeployment.Labels[common.GetWorkloadIdentifier()] + key := getCacheKey(env, identity) + delete(g.identityCache, key) + } + + g.dependencyCache[gtp.Name] = deployment + return nil +} + +func (g *globalTrafficCache) Delete(gtp *v1.GlobalTrafficPolicy) { + if gtp.Name == "" { + //no GTP, nothing to delete + return + } + defer g.mutex.Unlock() + g.mutex.Lock() + log.Infof("Deleting gtp with name %v to GTP cache. LabelMatch=%v env=%v", gtp.Name,gtp.Labels[common.GetGlobalTrafficDeploymentLabel()], gtp.Labels[common.Env]) + + deployment := g.dependencyCache[gtp.Name] + + if deployment != nil && deployment.Labels != nil { + + //we have a valid deployment + env := deployment.Spec.Template.Labels[common.Env] + if env == "" { + //No environment label, use default value + env = common.Default + } + identity := deployment.Labels[common.GetWorkloadIdentifier()] + key := getCacheKey(env, identity) + delete(g.identityCache, key) + } + + delete(g.dependencyCache, gtp.Name) +} + type DeploymentHandler struct { RemoteRegistry *RemoteRegistry } @@ -106,21 +186,71 @@ func (dh *DependencyHandler) Added(obj *v1.Dependency) { } - func (dh *DependencyHandler) Deleted(obj *v1.Dependency) { log.Infof(LogFormat, "Deleted", "dependency", obj.Name, obj.ClusterName, "Skipping, not implemented") } func (gtp *GlobalTrafficHandler) Added(obj *v1.GlobalTrafficPolicy) { - log.Infof(LogFormat, "Added", "trafficpolicy", obj.Name, obj.ClusterName, "Skipping, not implemented") + log.Infof(LogFormat, "Added", "trafficpolicy", obj.Name, obj.ClusterName, "received") + + var matchedDeployments []k8sAppsV1.Deployment + + //IMPORTANT: The deployment matched with a GTP will not necessarily be from the same cluster. This is because the same service could be deployed in multiple clusters and we need to guarantee consistent behavior + for _, remoteCluster := range gtp.RemoteRegistry.remoteControllers { + matchedDeployments = append(matchedDeployments, remoteCluster.DeploymentController.GetDeploymentByLabel(obj.Labels[common.GetGlobalTrafficDeploymentLabel()], obj.Namespace)...) + } + + deployments := common.MatchDeploymentsToGTP(obj, matchedDeployments) + + if len(deployments) != 0 { + for _, deployment := range deployments { + err := gtp.RemoteRegistry.AdmiralCache.GlobalTrafficCache.Put(obj, &deployment) + if err != nil { + log.Errorf("Failed to add nw GTP to cache. Error=%v", err) + log.Infof(LogFormat, "Added", "trafficpolicy", obj.Name, obj.ClusterName, "Failed") + } + } + } else { + log.Infof(LogErrFormat, "Added", "trafficpolicy", obj.Name, obj.ClusterName, "Skipping, no matched deployments") + } + } func (gtp *GlobalTrafficHandler) Updated(obj *v1.GlobalTrafficPolicy) { - log.Infof(LogFormat, "Updated", "trafficpolicy", obj.Name, obj.ClusterName, "Skipping, not implemented") + log.Infof(LogFormat, "Updated", "trafficpolicy", obj.Name, obj.ClusterName, "received") + + var matchedDeployments []k8sAppsV1.Deployment + + //IMPORTANT: The deployment matched with a GTP will not necessarily be from the same cluster. This is because the same service could be deployed in multiple clusters and we need to guarantee consistent behavior + for _, remoteCluster := range gtp.RemoteRegistry.remoteControllers { + matchedDeployments = append(matchedDeployments, remoteCluster.DeploymentController.GetDeploymentByLabel(obj.Labels[common.GetGlobalTrafficDeploymentLabel()], obj.Namespace)...) + } + + deployments := common.MatchDeploymentsToGTP(obj, matchedDeployments) + + if len(deployments) != 0 { + for _, deployment := range deployments { + err := gtp.RemoteRegistry.AdmiralCache.GlobalTrafficCache.Put(obj, &deployment) + if err != nil { + log.Errorf("Failed to add updated GTP to cache. Error=%v", err) + log.Infof(LogFormat, "Updated", "trafficpolicy", obj.Name, obj.ClusterName, "Failed") + } + } + } else { + err := gtp.RemoteRegistry.AdmiralCache.GlobalTrafficCache.Put(obj, nil) + if err != nil { + log.Errorf("Failed to add updated GTP to cache. Error=%v", err) + log.Infof(LogFormat, "Updated", "trafficpolicy", obj.Name, obj.ClusterName, "Failed") + } else { + log.Infof(LogErrFormat, "Updated", "trafficpolicy", obj.Name, obj.ClusterName, "Skipping, no matched deployments") + } + } } func (gtp *GlobalTrafficHandler) Deleted(obj *v1.GlobalTrafficPolicy) { - log.Infof(LogFormat, "Deleted", "trafficpolicy", obj.Name, obj.ClusterName, "Skipping, not implemented") + log.Infof(LogFormat, "Deleted", "trafficpolicy", obj.Name, obj.ClusterName, "received") + + gtp.RemoteRegistry.AdmiralCache.GlobalTrafficCache.Delete(obj) } func (pc *DeploymentHandler) Added(obj *k8sAppsV1.Deployment) { @@ -133,12 +263,31 @@ func (pc *DeploymentHandler) Added(obj *k8sAppsV1.Deployment) { return } + var matchedGTPs []v1.GlobalTrafficPolicy + for _, remoteCluster := range pc.RemoteRegistry.remoteControllers { + matchedGTPs = append(matchedGTPs, remoteCluster.GlobalTraffic.GetGTPByLabel(obj.Labels[common.GetGlobalTrafficDeploymentLabel()], obj.Namespace)...) + } + + gtp := common.MatchGTPsToDeployment(matchedGTPs, obj) + + if gtp != nil { + err := pc.RemoteRegistry.AdmiralCache.GlobalTrafficCache.Put(gtp, obj) + if err != nil { + log.Errorf("Failed to add Deployment to GTP cache. Error=%v", err) + } else { + log.Infof(LogFormat, "Event", "deployment", obj.Name, obj.ClusterName, "Matched to GTP name="+gtp.Name) + } + } + env := common.GetEnv(obj) createServiceEntryForNewServiceOrPod(env, globalIdentifier, pc.RemoteRegistry) } func (pc *DeploymentHandler) Deleted(obj *k8sAppsV1.Deployment) { + log.Infof(LogFormat, "Deleted", "deployment", obj.Name, obj.ClusterName, "Skipped, not implemented") + //todo remove from gtp cache + //TODO update subset service entries } @@ -186,3 +335,7 @@ func (sc *ServiceHandler) Added(obj *k8sV1.Service) { func (sc *ServiceHandler) Deleted(obj *k8sV1.Service) { } + +func getCacheKey(environment string, identity string) string { + return fmt.Sprintf("%s.%s", environment, identity) +} diff --git a/admiral/pkg/clusters/types_test.go b/admiral/pkg/clusters/types_test.go new file mode 100644 index 00000000..b1ae87b6 --- /dev/null +++ b/admiral/pkg/clusters/types_test.go @@ -0,0 +1,585 @@ +package clusters + +import ( + "context" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" + admiralFake "github.com/istio-ecosystem/admiral/admiral/pkg/client/clientset/versioned/fake" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/admiral" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + v12 "k8s.io/api/apps/v1" + v13 "k8s.io/api/core/v1" + time2 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" + "log" + "sync" + "testing" + "time" +) + +var ignoreUnexported = cmpopts.IgnoreUnexported(v1.GlobalTrafficPolicy{}.Status) + +func init() { + p := common.AdmiralParams{ + KubeconfigPath: "testdata/fake.config", + LabelSet: &common.LabelSet{}, + EnableSAN: true, + SANPrefix: "prefix", + HostnameSuffix: "mesh", + SyncNamespace: "ns", + CacheRefreshDuration: time.Minute, + ClusterRegistriesNamespace: "default", + DependenciesNamespace: "default", + SecretResolver: "", + + } + + p.LabelSet.WorkloadIdentityKey="identity" + p.LabelSet.GlobalTrafficDeploymentLabel="identity" + + common.InitializeConfig(p) +} + + +func TestGlobalTrafficHandler(t *testing.T) { + //lots of setup + handler := GlobalTrafficHandler{} + registry := &RemoteRegistry{} + admiralCacle := &AdmiralCache{} + + gtpCache := &globalTrafficCache{} + gtpCache.identityCache = make(map[string]*v1.GlobalTrafficPolicy) + gtpCache.dependencyCache = make(map[string]*v12.Deployment) + gtpCache.mutex = &sync.Mutex{} + + + fakeClient := fake.NewSimpleClientset() + + deployment := v12.Deployment{} + deployment.Namespace = "namespace" + deployment.Name = "fake-app-deployment-qal" + deployment.Spec = v12.DeploymentSpec{ + Template: v13.PodTemplateSpec{ + ObjectMeta: time2.ObjectMeta{ + Labels: map[string]string{"identity": "app1", "env":"qal"}, + }, + }, + } + deployment.Labels = map[string]string{"identity": "app1"} + + deployment2 := v12.Deployment{} + deployment2.Namespace = "namespace" + deployment2.Name = "fake-app-deployment-e2e" + deployment2.Spec = v12.DeploymentSpec{ + Template: v13.PodTemplateSpec{ + ObjectMeta: time2.ObjectMeta{ + Labels: map[string]string{"identity": "app1", "env":"e2e"}, + }, + }, + } + deployment2.Labels = map[string]string{"identity": "app1"} + + deployment3 := v12.Deployment{} + deployment3.Namespace = "namespace" + deployment3.Name = "fake-app-deployment-prf-1" + deployment3.CreationTimestamp = time2.Now() + deployment3.Spec = v12.DeploymentSpec{ + Template: v13.PodTemplateSpec{ + ObjectMeta: time2.ObjectMeta{ + Labels: map[string]string{"identity": "app1", "env":"prf"}, + }, + }, + } + deployment3.Labels = map[string]string{"identity": "app1"} + + deployment4 := v12.Deployment{} + deployment4.Namespace = "namespace" + deployment4.Name = "fake-app-deployment-prf-2" + deployment4.CreationTimestamp = time2.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC) + deployment4.Spec = v12.DeploymentSpec{ + Template: v13.PodTemplateSpec{ + ObjectMeta: time2.ObjectMeta{ + Labels: map[string]string{"identity": "app1", "env":"prf"}, + }, + }, + } + deployment4.Labels = map[string]string{"identity": "app1"} + + _, err := fakeClient.AppsV1().Deployments("namespace").Create(&deployment) + _, err = fakeClient.AppsV1().Deployments("namespace").Create(&deployment2) + _, err = fakeClient.AppsV1().Deployments("namespace").Create(&deployment3) + _, err = fakeClient.AppsV1().Deployments("namespace").Create(&deployment4) + if err != nil { + log.Fatalf("Failed to set up mock k8s client. Failing test. Error=%v", err) + } + + deploymentController := &admiral.DeploymentController{K8sClient:fakeClient} + remoteController := &RemoteController{} + remoteController.DeploymentController = deploymentController + + registry.remoteControllers = map[string]*RemoteController{"cluster-1": remoteController} + + admiralCacle.GlobalTrafficCache = gtpCache + registry.AdmiralCache = admiralCacle + handler.RemoteRegistry = registry + + + e2eGtp := v1.GlobalTrafficPolicy{} + e2eGtp.Labels = map[string]string{"identity": "app1", "env":"e2e"} + e2eGtp.Namespace = "namespace" + e2eGtp.Name = "myGTP" + + noMatchGtp := v1.GlobalTrafficPolicy{} + noMatchGtp.Labels = map[string]string{"identity": "app2", "env":"e2e"} + noMatchGtp.Namespace = "namespace" + noMatchGtp.Name = "myGTP" + + prfGtp := v1.GlobalTrafficPolicy{} + prfGtp.Labels = map[string]string{"identity": "app1", "env":"prf"} + prfGtp.Namespace = "namespace" + prfGtp.Name = "myGTP" + + + + testCases := []struct { + name string + gtp *v1.GlobalTrafficPolicy + expectedIdentity string + expectedEnv string + expectedIdentityCacheValue *v1.GlobalTrafficPolicy + expectedDeploymentCacheValue *v12.Deployment + + }{ + { + name: "Should return matching environment", + gtp: &e2eGtp, + expectedIdentity: "app1", + expectedEnv: "e2e", + expectedDeploymentCacheValue: &deployment2, + expectedIdentityCacheValue: &e2eGtp, + }, + { + name: "Should return nothing when identity labels don't match", + gtp: &noMatchGtp, + expectedIdentity: "app1", + expectedEnv: "e2e", + expectedDeploymentCacheValue: nil, + expectedIdentityCacheValue: nil, + }, + { + name: "Should return oldest deployment when multiple match", + gtp: &prfGtp, + expectedIdentity: "app1", + expectedEnv: "prf", + expectedDeploymentCacheValue: &deployment4, + expectedIdentityCacheValue: &prfGtp, + }, + } + + //Run the test for every provided case + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + //clearing admiralCache + gtpCache = &globalTrafficCache{} + gtpCache.identityCache = make(map[string]*v1.GlobalTrafficPolicy) + gtpCache.dependencyCache = make(map[string]*v12.Deployment) + gtpCache.mutex = &sync.Mutex{} + handler.RemoteRegistry.AdmiralCache.GlobalTrafficCache = gtpCache + + //run the method under test then make assertions + handler.Added(c.gtp) + if !cmp.Equal(handler.RemoteRegistry.AdmiralCache.GlobalTrafficCache.GetFromIdentity(c.expectedIdentity, c.expectedEnv), c.expectedIdentityCacheValue, ignoreUnexported) { + t.Fatalf("GTP Mismatch. Diff: %v", cmp.Diff(c.expectedIdentityCacheValue, handler.RemoteRegistry.AdmiralCache.GlobalTrafficCache.GetFromIdentity(c.expectedIdentity, c.expectedEnv), ignoreUnexported)) + } + if !cmp.Equal(handler.RemoteRegistry.AdmiralCache.GlobalTrafficCache.GetDeployment(c.gtp.Name), c.expectedDeploymentCacheValue, ignoreUnexported) { + t.Fatalf("Deployment Mismatch. Diff: %v", cmp.Diff(c.expectedDeploymentCacheValue, handler.RemoteRegistry.AdmiralCache.GlobalTrafficCache.GetDeployment(c.gtp.Name), ignoreUnexported)) + } + + handler.Deleted(c.gtp) + + if handler.RemoteRegistry.AdmiralCache.GlobalTrafficCache.GetFromIdentity(c.expectedIdentity, c.expectedEnv) != nil { + t.Fatalf("Delete failed for Identity Cache") + } + if handler.RemoteRegistry.AdmiralCache.GlobalTrafficCache.GetDeployment(c.gtp.Name) != nil { + t.Fatalf("Delete failed for Dependency Cache") + } + + }) + } +} + +func TestGlobalTrafficHandler_Updated(t *testing.T) { + //lots of setup + handler := GlobalTrafficHandler{} + registry := &RemoteRegistry{} + admiralCacle := &AdmiralCache{} + + gtpCache := &globalTrafficCache{} + gtpCache.identityCache = make(map[string]*v1.GlobalTrafficPolicy) + gtpCache.dependencyCache = make(map[string]*v12.Deployment) + gtpCache.mutex = &sync.Mutex{} + + + fakeClient := fake.NewSimpleClientset() + + deployment := v12.Deployment{} + deployment.Namespace = "namespace" + deployment.Name = "fake-app-deployment-qal" + deployment.Spec = v12.DeploymentSpec{ + Template: v13.PodTemplateSpec{ + ObjectMeta: time2.ObjectMeta{ + Labels: map[string]string{"identity": "app1", "env":"qal"}, + }, + }, + } + deployment.Labels = map[string]string{"identity": "app1"} + + deployment2 := v12.Deployment{} + deployment2.Namespace = "namespace" + deployment2.Name = "fake-app-deployment-e2e" + deployment2.Spec = v12.DeploymentSpec{ + Template: v13.PodTemplateSpec{ + ObjectMeta: time2.ObjectMeta{ + Labels: map[string]string{"identity": "app1", "env":"e2e"}, + }, + }, + } + deployment2.Labels = map[string]string{"identity": "app1"} + + deployment3 := v12.Deployment{} + deployment3.Namespace = "namespace" + deployment3.Name = "fake-app-deployment-prf-1" + deployment3.CreationTimestamp = time2.Now() + deployment3.Spec = v12.DeploymentSpec{ + Template: v13.PodTemplateSpec{ + ObjectMeta: time2.ObjectMeta{ + Labels: map[string]string{"identity": "app1", "env":"prf"}, + }, + }, + } + deployment3.Labels = map[string]string{"identity": "app1"} + + deployment4 := v12.Deployment{} + deployment4.Namespace = "namespace" + deployment4.Name = "fake-app-deployment-prf-2" + deployment4.CreationTimestamp = time2.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC) + deployment4.Spec = v12.DeploymentSpec{ + Template: v13.PodTemplateSpec{ + ObjectMeta: time2.ObjectMeta{ + Labels: map[string]string{"identity": "app1", "env":"prf"}, + }, + }, + } + deployment4.Labels = map[string]string{"identity": "app1"} + + + _, err := fakeClient.AppsV1().Deployments("namespace").Create(&deployment) + _, err = fakeClient.AppsV1().Deployments("namespace").Create(&deployment2) + _, err = fakeClient.AppsV1().Deployments("namespace").Create(&deployment3) + _, err = fakeClient.AppsV1().Deployments("namespace").Create(&deployment4) + if err != nil { + log.Fatalf("Failed to set up mock k8s client. Failing test. Error=%v", err) + } + + deploymentController := &admiral.DeploymentController{K8sClient:fakeClient} + remoteController := &RemoteController{} + remoteController.DeploymentController = deploymentController + + registry.remoteControllers = map[string]*RemoteController{"cluster-1": remoteController} + + admiralCacle.GlobalTrafficCache = gtpCache + registry.AdmiralCache = admiralCacle + handler.RemoteRegistry = registry + + + e2eGtp := v1.GlobalTrafficPolicy{} + e2eGtp.Labels = map[string]string{"identity": "app1", "env":"e2e"} + e2eGtp.Namespace = "namespace" + e2eGtp.Name = "myGTP" + + e2eGtpExtraLabel := v1.GlobalTrafficPolicy{} + e2eGtpExtraLabel.Labels = map[string]string{"identity": "app1", "env":"e2e", "random": "foobar"} + e2eGtpExtraLabel.Namespace = "namespace" + e2eGtpExtraLabel.Name = "myGTP" + + noMatchGtp := v1.GlobalTrafficPolicy{} + noMatchGtp.Labels = map[string]string{"identity": "app2", "env":"e2e"} + noMatchGtp.Namespace = "namespace" + noMatchGtp.Name = "myGTP" + + prfGtp := v1.GlobalTrafficPolicy{} + prfGtp.Labels = map[string]string{"identity": "app1", "env":"prf"} + prfGtp.Namespace = "namespace" + prfGtp.Name = "myGTP" + + + + testCases := []struct { + name string + gtp *v1.GlobalTrafficPolicy + updatedGTP *v1.GlobalTrafficPolicy + expectedIdentity string + expectedEnv string + expectedIdentityCacheValue *v1.GlobalTrafficPolicy + expectedDeploymentCacheValue *v12.Deployment + + }{ + { + name: "Should return matching environment", + gtp: &e2eGtp, + updatedGTP: &e2eGtpExtraLabel, + expectedIdentity: "app1", + expectedEnv: "e2e", + expectedDeploymentCacheValue: &deployment2, + expectedIdentityCacheValue: &e2eGtpExtraLabel, + }, + { + name: "Should return nothing when identity labels don't match after update", + gtp: &e2eGtp, + updatedGTP: &noMatchGtp, + expectedIdentity: "app1", + expectedEnv: "e2e", + expectedDeploymentCacheValue: nil, + expectedIdentityCacheValue: nil, + }, + { + name: "Should return oldest deployment when multiple match", + gtp: &e2eGtp, + updatedGTP: &prfGtp, + expectedIdentity: "app1", + expectedEnv: "prf", + expectedDeploymentCacheValue: &deployment4, + expectedIdentityCacheValue: &prfGtp, + }, + } + + //Run the test for every provided case + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + //clearing admiralCache + gtpCache = &globalTrafficCache{} + gtpCache.identityCache = make(map[string]*v1.GlobalTrafficPolicy) + gtpCache.dependencyCache = make(map[string]*v12.Deployment) + gtpCache.mutex = &sync.Mutex{} + handler.RemoteRegistry.AdmiralCache.GlobalTrafficCache = gtpCache + + //run the method under test then make assertions + handler.Added(c.gtp) + handler.Updated(c.updatedGTP) + if !cmp.Equal(handler.RemoteRegistry.AdmiralCache.GlobalTrafficCache.GetFromIdentity(c.expectedIdentity, c.expectedEnv), c.expectedIdentityCacheValue, ignoreUnexported) { + t.Fatalf("GTP Mismatch. Diff: %v", cmp.Diff(c.expectedIdentityCacheValue, handler.RemoteRegistry.AdmiralCache.GlobalTrafficCache.GetFromIdentity(c.expectedIdentity, c.expectedEnv), ignoreUnexported)) + } + if !cmp.Equal(handler.RemoteRegistry.AdmiralCache.GlobalTrafficCache.GetDeployment(c.gtp.Name), c.expectedDeploymentCacheValue, ignoreUnexported) { + t.Fatalf("Deployment Mismatch. Diff: %v", cmp.Diff(c.expectedDeploymentCacheValue, handler.RemoteRegistry.AdmiralCache.GlobalTrafficCache.GetDeployment(c.gtp.Name), ignoreUnexported)) + } + + handler.Deleted(c.updatedGTP) + + if handler.RemoteRegistry.AdmiralCache.GlobalTrafficCache.GetFromIdentity(c.expectedIdentity, c.expectedEnv) != nil { + t.Fatalf("Delete failed for Identity Cache") + } + if handler.RemoteRegistry.AdmiralCache.GlobalTrafficCache.GetDeployment(c.gtp.Name) != nil { + t.Fatalf("Delete failed for Dependency Cache") + } + + }) + } +} + +func TestDeploymentHandler(t *testing.T) { + + p := common.AdmiralParams{ + KubeconfigPath: "testdata/fake.config", + } + + registry, _ := InitAdmiral(context.Background(), p) + + handler := DeploymentHandler{} + + gtpCache := &globalTrafficCache{} + gtpCache.identityCache = make(map[string]*v1.GlobalTrafficPolicy) + gtpCache.dependencyCache = make(map[string]*v12.Deployment) + gtpCache.mutex = &sync.Mutex{} + + + + fakeCrdClient := admiralFake.NewSimpleClientset() + + gtpController := &admiral.GlobalTrafficController{CrdClient:fakeCrdClient} + remoteController, _ := createMockRemoteController(func(i interface{}) { + + }) + remoteController.GlobalTraffic = gtpController + + registry.remoteControllers = map[string]*RemoteController{"cluster-1": remoteController} + + registry.AdmiralCache.GlobalTrafficCache = gtpCache + handler.RemoteRegistry = registry + + deployment := v12.Deployment{ + ObjectMeta: time2.ObjectMeta{ + Name: "test", + Namespace: "namespace", + Labels: map[string]string{"identity": "app1"}, + }, + Spec: v12.DeploymentSpec{ + Selector: &time2.LabelSelector{ + MatchLabels: map[string]string{"identity": "bar"}, + }, + Template: v13.PodTemplateSpec{ + ObjectMeta: time2.ObjectMeta{ + Labels: map[string]string{"identity": "bar", "istio-injected": "true", "env": "dev"}, + }, + }, + }, + } + + + //Struct of test case info. Name is required. + testCases := []struct { + name string + addedDeployment *v12.Deployment + expectedDeploymentCacheKey string + expectedIdentityCacheValue *v1.GlobalTrafficPolicy + expectedDeploymentCacheValue *v12.Deployment + + }{ + { + name: "Shouldn't throw errors when called", + addedDeployment: &deployment, + expectedDeploymentCacheKey: "myGTP1", + expectedIdentityCacheValue: nil, + expectedDeploymentCacheValue: nil, + + }, + } + + //Rather annoying, but wasn't able to get the autogenerated fake k8s client for GTP objects to allow me to list resources, so this test is only for not throwing errors. I'll be testing the rest of the fucntionality picemeal. + //Side note, if anyone knows how to fix `level=error msg="Failed to list deployments in cluster, error: no kind \"GlobalTrafficPolicyList\" is registered for version \"admiral.io/v1\" in scheme \"pkg/runtime/scheme.go:101\""`, I'd love to hear it! + //Already tried working through this: https://github.com/camilamacedo86/operator-sdk/blob/e40d7db97f0d132333b1e46ddf7b7f3cab1e379f/doc/user/unit-testing.md with no luck + + //Run the test for every provided case + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + gtpCache = &globalTrafficCache{} + gtpCache.identityCache = make(map[string]*v1.GlobalTrafficPolicy) + gtpCache.dependencyCache = make(map[string]*v12.Deployment) + gtpCache.mutex = &sync.Mutex{} + handler.RemoteRegistry.AdmiralCache.GlobalTrafficCache = gtpCache + + handler.Added(&deployment) + handler.Deleted(&deployment) + }) + } +} + + +func TestGlobalTrafficCache(t *testing.T) { + deployment := v12.Deployment{} + deployment.Namespace = "namespace" + deployment.Name = "fake-app-deployment-qal" + deployment.Spec = v12.DeploymentSpec{ + Template: v13.PodTemplateSpec{ + ObjectMeta: time2.ObjectMeta{ + Labels: map[string]string{"identity": "app1", "env":"e2e"}, + }, + }, + } + deployment.Labels = map[string]string{"identity": "app1"} + + deploymentNoEnv := v12.Deployment{} + deploymentNoEnv.Namespace = "namespace" + deploymentNoEnv.Name = "fake-app-deployment-qal" + deploymentNoEnv.Spec = v12.DeploymentSpec{ + Template: v13.PodTemplateSpec{ + ObjectMeta: time2.ObjectMeta{ + Labels: map[string]string{"identity": "app1"}, + }, + }, + } + deploymentNoEnv.Labels = map[string]string{"identity": "app1"} + + e2eGtp := v1.GlobalTrafficPolicy{} + e2eGtp.Labels = map[string]string{"identity": "app1", "env":"e2e"} + e2eGtp.Namespace = "namespace" + e2eGtp.Name = "myGTP" + + gtpCache := &globalTrafficCache{} + gtpCache.identityCache = make(map[string]*v1.GlobalTrafficPolicy) + gtpCache.dependencyCache = make(map[string]*v12.Deployment) + gtpCache.mutex = &sync.Mutex{} + + + //Struct of test case info. Name is required. + testCases := []struct { + name string + gtp *v1.GlobalTrafficPolicy + deployment *v12.Deployment + identity string + environment string + gtpName string + }{ + { + name: "Base case", + gtp: &e2eGtp, + deployment: &deployment, + identity: "app1", + environment: "e2e", + gtpName: "myGTP", + }, + { + name: "No Deployment", + gtp: &e2eGtp, + deployment: nil, + identity: "app1", + environment: "e2e", + gtpName: "myGTP", + }, + { + name: "Handles lack of environment label properly", + gtp: &e2eGtp, + deployment: &deploymentNoEnv, + identity: "app1", + environment: "default", + gtpName: "myGTP", + }, + } + + //Run the test for every provided case + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + + gtpCache.identityCache = make(map[string]*v1.GlobalTrafficPolicy) + gtpCache.dependencyCache = make(map[string]*v12.Deployment) + gtpCache.mutex = &sync.Mutex{} + err := gtpCache.Put(c.gtp, c.deployment) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + foundGtp := gtpCache.GetFromIdentity(c.identity, c.environment) + if c.deployment != nil && !cmp.Equal(foundGtp, c.gtp, ignoreUnexported) { + t.Fatalf("GTP Mismatch: %v", cmp.Diff(foundGtp, c.gtp, ignoreUnexported)) + } + + //no deployment means there should be nothing in the identity cache + if c.deployment == nil && foundGtp != nil { + t.Fatalf("GTP Mismatch: %v", cmp.Diff(foundGtp, nil, ignoreUnexported)) + } + + foundDeployment := gtpCache.GetDeployment(c.gtpName) + if !cmp.Equal(foundDeployment, c.deployment, ignoreUnexported) { + t.Fatalf("Deployment Mismatch: %v", cmp.Diff(foundDeployment, c.deployment, ignoreUnexported)) + } + + gtpCache.Delete(c.gtp) + if len(gtpCache.dependencyCache) != 0 { + t.Fatalf("Dependency cache not cleared properly on delete") + } + if len(gtpCache.identityCache) != 0 { + t.Fatalf("Identity cache not cleared properly on delete") + } + }) + } + +} \ No newline at end of file diff --git a/admiral/pkg/controller/admiral/configmap_test.go b/admiral/pkg/controller/admiral/configmap_test.go index 7ca69c0b..cfaaac48 100644 --- a/admiral/pkg/controller/admiral/configmap_test.go +++ b/admiral/pkg/controller/admiral/configmap_test.go @@ -28,6 +28,7 @@ func init() { } p.LabelSet.WorkloadIdentityKey="identity" + p.LabelSet.GlobalTrafficDeploymentLabel="identity" common.InitializeConfig(p) } diff --git a/admiral/pkg/controller/admiral/deployment.go b/admiral/pkg/controller/admiral/deployment.go index c3cac070..3c73b112 100644 --- a/admiral/pkg/controller/admiral/deployment.go +++ b/admiral/pkg/controller/admiral/deployment.go @@ -3,6 +3,7 @@ package admiral import ( "fmt" "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + "github.com/sirupsen/logrus" k8sAppsV1 "k8s.io/api/apps/v1" k8sAppsinformers "k8s.io/client-go/informers/apps/v1" "k8s.io/client-go/rest" @@ -179,3 +180,21 @@ func (d *DeploymentController) shouldIgnoreBasedOnLabels(deployment *k8sAppsV1.D } return false //labels are fine, we should not ignore } + +func (d *DeploymentController) GetDeploymentByLabel(labelValue string, namespace string) []k8sAppsV1.Deployment { + matchLabel := common.GetGlobalTrafficDeploymentLabel() + labelOptions := meta_v1.ListOptions{} + labelOptions.LabelSelector = fmt.Sprintf("%s=%s", matchLabel, labelValue) + matchedDeployments, err := d.K8sClient.AppsV1().Deployments(namespace).List(labelOptions) + + if err != nil { + logrus.Errorf("Failed to list deployments in cluster, error: %v", err) + return nil + } + + if matchedDeployments.Items == nil { + return []k8sAppsV1.Deployment{} + } + + return matchedDeployments.Items + } \ No newline at end of file diff --git a/admiral/pkg/controller/admiral/deployment_test.go b/admiral/pkg/controller/admiral/deployment_test.go index ca0d2556..dafa5434 100644 --- a/admiral/pkg/controller/admiral/deployment_test.go +++ b/admiral/pkg/controller/admiral/deployment_test.go @@ -7,8 +7,10 @@ import ( log "github.com/sirupsen/logrus" k8sAppsV1 "k8s.io/api/apps/v1" coreV1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/tools/clientcmd" + "sort" "sync" "testing" "time" @@ -160,3 +162,127 @@ func TestNewDeploymentController(t *testing.T) { t.Errorf("Deployment controller should not be nil") } } + +func TestDeploymentController_GetDeploymentByLabel(t *testing.T) { + deployment := k8sAppsV1.Deployment{} + deployment.Namespace = "namespace" + deployment.Name = "fake-app-deployment-qal" + deployment.Spec = k8sAppsV1.DeploymentSpec{ + Template: coreV1.PodTemplateSpec{ + ObjectMeta: v1.ObjectMeta{ + Labels: map[string]string{"identity": "app1", "env":"qal"}, + }, + }, + } + deployment.Labels = map[string]string{"identity": "app1"} + + deployment2 := k8sAppsV1.Deployment{} + deployment2.Namespace = "namespace" + deployment2.Name = "fake-app-deployment-e2e" + deployment2.Spec = k8sAppsV1.DeploymentSpec{ + Template: coreV1.PodTemplateSpec{ + ObjectMeta: v1.ObjectMeta{ + Labels: map[string]string{"identity": "app1", "env":"e2e"}, + }, + }, + } + deployment2.Labels = map[string]string{"identity": "app1"} + + deployment3 := k8sAppsV1.Deployment{} + deployment3.Namespace = "namespace" + deployment3.Name = "fake-app-deployment-prf-1" + deployment3.CreationTimestamp = v1.Now() + deployment3.Spec = k8sAppsV1.DeploymentSpec{ + Template: coreV1.PodTemplateSpec{ + ObjectMeta: v1.ObjectMeta{ + Labels: map[string]string{"identity": "app1", "env":"prf"}, + }, + }, + } + deployment3.Labels = map[string]string{"identity": "app1"} + + deployment4 := k8sAppsV1.Deployment{} + deployment4.Namespace = "namespace" + deployment4.Name = "fake-app-deployment-prf-2" + deployment4.CreationTimestamp = v1.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC) + deployment4.Spec = k8sAppsV1.DeploymentSpec{ + Template: coreV1.PodTemplateSpec{ + ObjectMeta: v1.ObjectMeta{ + Labels: map[string]string{"identity": "app2", "env":"prf"}, + }, + }, + } + deployment4.Labels = map[string]string{"identity": "app2"} + + oneDeploymentClient := fake.NewSimpleClientset(&deployment) + + allDeploymentsClient := fake.NewSimpleClientset(&deployment, &deployment2, &deployment3, &deployment4) + + noDeploymentsClient := fake.NewSimpleClientset() + + deploymentController := &DeploymentController{} + + //Struct of test case info. Name is required. + testCases := []struct { + name string + expectedDeployments []k8sAppsV1.Deployment + fakeClient *fake.Clientset + labelValue string + }{ + { + name: "Get one", + expectedDeployments: []k8sAppsV1.Deployment{deployment}, + fakeClient:oneDeploymentClient, + labelValue: "app1", + }, + { + name: "Get one from long list", + expectedDeployments: []k8sAppsV1.Deployment{deployment4}, + fakeClient:allDeploymentsClient, + labelValue: "app2", + }, + { + name: "Get many from long list", + expectedDeployments: []k8sAppsV1.Deployment{deployment, deployment3, deployment2}, + fakeClient:allDeploymentsClient, + labelValue: "app1", + }, + { + name: "Get none from long list", + expectedDeployments: []k8sAppsV1.Deployment{}, + fakeClient:allDeploymentsClient, + labelValue: "app3", + }, + { + name: "Get none from empty list", + expectedDeployments: []k8sAppsV1.Deployment{}, + fakeClient:noDeploymentsClient, + labelValue: "app1", + }, + } + + //Run the test for every provided case + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + deploymentController.K8sClient = c.fakeClient + returnedDeployments := deploymentController.GetDeploymentByLabel(c.labelValue, "namespace") + + sort.Slice(returnedDeployments, func(i, j int) bool { + return returnedDeployments[i].Name > returnedDeployments[j].Name + }) + + sort.Slice(c.expectedDeployments, func(i, j int) bool { + return c.expectedDeployments[i].Name > c.expectedDeployments[j].Name + }) + + if len(returnedDeployments) != len(c.expectedDeployments) { + t.Fatalf("Returned the wrong number of deploymenrs. Found %v but expected %v", len(returnedDeployments), len(c.expectedDeployments)) + } + + if !cmp.Equal(returnedDeployments, c.expectedDeployments) { + t.Fatalf("Deployment mismatch. Diff: %v", cmp.Diff(returnedDeployments, c.expectedDeployments)) + } + + }) + } +} \ No newline at end of file diff --git a/admiral/pkg/controller/admiral/globaltraffic.go b/admiral/pkg/controller/admiral/globaltraffic.go index 9ee728f6..650e6703 100644 --- a/admiral/pkg/controller/admiral/globaltraffic.go +++ b/admiral/pkg/controller/admiral/globaltraffic.go @@ -3,10 +3,11 @@ package admiral import ( "fmt" "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + "github.com/sirupsen/logrus" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/rest" "k8s.io/client-go/tools/cache" - "sync" "time" clientset "github.com/istio-ecosystem/admiral/admiral/pkg/client/clientset/versioned" @@ -23,36 +24,9 @@ type GlobalTrafficHandler interface { type GlobalTrafficController struct { CrdClient clientset.Interface GlobalTrafficHandler GlobalTrafficHandler - Cache *globalTarfficCache informer cache.SharedIndexInformer ctl *Controller -} - -type globalTarfficCache struct { - //map of dependencies key=identity value array of onboarded identitys - cache map[string]*v1.GlobalTrafficPolicy - mutex *sync.Mutex -} - -func (g *globalTarfficCache) Put(dep *v1.GlobalTrafficPolicy) { - defer g.mutex.Unlock() - g.mutex.Lock() - key := g.getKey(dep) - g.cache[key] = dep -} - -func (d *globalTarfficCache) getKey(dep *v1.GlobalTrafficPolicy) string { - return dep.Spec.Selector["identity"] -} - -func (g *globalTarfficCache) Get(identity string) *v1.GlobalTrafficPolicy { - return g.cache[identity] -} - -func (g *globalTarfficCache) Delete(dep *v1.GlobalTrafficPolicy) { - defer g.mutex.Unlock() - g.mutex.Lock() - delete(g.cache, g.getKey(dep)) + clusterName string } func (g *GlobalTrafficController) GetGlobalTrafficPolicies() ([]v1.GlobalTrafficPolicy, error) { @@ -69,13 +43,8 @@ func (g *GlobalTrafficController) GetGlobalTrafficPolicies() ([]v1.GlobalTraffic func NewGlobalTrafficController(stopCh <-chan struct{}, handler GlobalTrafficHandler, configPath *rest.Config, resyncPeriod time.Duration) (*GlobalTrafficController, error) { globalTrafficController := GlobalTrafficController{} - globalTrafficController.GlobalTrafficHandler = handler - gtpCache := globalTarfficCache{} - gtpCache.cache = make(map[string]*v1.GlobalTrafficPolicy) - gtpCache.mutex = &sync.Mutex{} - - globalTrafficController.Cache = >pCache + globalTrafficController.GlobalTrafficHandler = handler var err error @@ -98,13 +67,11 @@ func NewGlobalTrafficController(stopCh <-chan struct{}, handler GlobalTrafficHan func (d *GlobalTrafficController) Added(ojb interface{}) { dep := ojb.(*v1.GlobalTrafficPolicy) - d.Cache.Put(dep) d.GlobalTrafficHandler.Added(dep) } func (d *GlobalTrafficController) Updated(ojb interface{}) { dep := ojb.(*v1.GlobalTrafficPolicy) - d.Cache.Put(dep) d.GlobalTrafficHandler.Updated(dep) } @@ -112,3 +79,22 @@ func (d *GlobalTrafficController) Deleted(ojb interface{}) { dep := ojb.(*v1.GlobalTrafficPolicy) d.GlobalTrafficHandler.Deleted(dep) } + +func (g *GlobalTrafficController) GetGTPByLabel(labelValue string, namespace string) []v1.GlobalTrafficPolicy { + matchLabel := common.GetGlobalTrafficDeploymentLabel() + labelOptions := meta_v1.ListOptions{} + labelOptions.LabelSelector = fmt.Sprintf("%s=%s", matchLabel, labelValue) + matchedDeployments, err := g.CrdClient.AdmiralV1().GlobalTrafficPolicies(namespace).List(labelOptions) + + if err != nil { + logrus.Errorf("Failed to list GTPs in cluster, error: %v", err) + return nil + } + + if matchedDeployments.Items == nil { + return []v1.GlobalTrafficPolicy{} + } + + return matchedDeployments.Items +} + diff --git a/admiral/pkg/controller/common/common.go b/admiral/pkg/controller/common/common.go index a7511f2a..5becd0a9 100644 --- a/admiral/pkg/controller/common/common.go +++ b/admiral/pkg/controller/common/common.go @@ -1,9 +1,11 @@ package common import ( + "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" log "github.com/sirupsen/logrus" k8sAppsV1 "k8s.io/api/apps/v1" k8sV1 "k8s.io/api/core/v1" + "sort" "strings" ) @@ -11,7 +13,6 @@ const ( NamespaceKubeSystem = "kube-system" NamespaceIstioSystem = "istio-system" Env = "env" - Identity = "identity" Http = "http" DefaultMtlsPort = 15443 DefaultHttpPort = 80 @@ -120,3 +121,118 @@ func GetValueForKeyFromDeployment(key string, deployment *k8sAppsV1.Deployment) } return value } + +//Returns the list of deployments to which this GTP should apply. It is assumed that all inputs already are an identity match +//If the GTP has an identity label, it should match all deployments which share that label +//If the GTP does not have an identity label, it should return all deployments without an identity label +//IMPORTANT: If an environment label is specified on either the GTP or the deployment, the same value must be specified on the other for them to match +func MatchDeploymentsToGTP(gtp *v1.GlobalTrafficPolicy, deployments []k8sAppsV1.Deployment) []k8sAppsV1.Deployment { + if gtp == nil || gtp.Name == "" { + log.Warn("Nil or empty GlobalTrafficPolicy provided for deployment match. Returning nil.") + return nil + } + + gtpEnv := gtp.Labels[Env] + if gtpEnv == "" { + gtpEnv = Default + } + + if deployments == nil || len(deployments) == 0 { + return nil + } + + var envMatchedDeployments []k8sAppsV1.Deployment + + for _, deployment := range deployments { + deploymentEnvironment := deployment.Spec.Template.Labels[Env] + if deploymentEnvironment == "" { + //No environment label, use default value + deploymentEnvironment = Default + } + if deploymentEnvironment == gtpEnv { + envMatchedDeployments = append(envMatchedDeployments, deployment) + } + } + + if len(envMatchedDeployments) == 0 { + return nil + } + + for _, deployment := range deployments { + log.Infof("Newly added GTP with name=%v matched with Deployment %v in namespace %v. Env=%v", gtp.Name, deployment.Name, deployment.Namespace, gtpEnv) + } + return envMatchedDeployments +} + + +//Find the GTP that best matches the deployment. +//It's assumed that the set of GTPs passed in has already been matched via the GtpDeploymentLabel. Now it's our job to choose the best one. +//In order: +// - If one and only one GTP matches the env label of the deployment - use that one. Use "default" as the default env label for all GTPs and deployments. +// - If multiple GTPs match the deployment label, use the oldest one (Using an old one has less chance of new behavior which could impact workflows) +//IMPORTANT: If an environment label is specified on either the GTP or the deployment, the same value must be specified on the other for them to match +func MatchGTPsToDeployment(gtpList []v1.GlobalTrafficPolicy, deployment *k8sAppsV1.Deployment) *v1.GlobalTrafficPolicy{ + if deployment == nil || deployment.Name == "" { + log.Warn("Nil or empty GlobalTrafficPolicy provided for deployment match. Returning nil.") + return nil + } + deploymentEnvironment:= deployment.Spec.Template.Labels[Env] + if deploymentEnvironment == "" { + //No environment label, use default value + deploymentEnvironment = Default + } + + //If one and only one GTP matches the env label of the deployment - use that one + if len(gtpList) == 1 { + gtpEnv := gtpList[0].Labels[Env] + if gtpEnv == "" { + gtpEnv = Default + } + if gtpEnv == deploymentEnvironment{ + log.Infof("Newly added deployment with name=%v matched with GTP %v in namespace %v. Env=%v", deployment.Name, gtpList[0].Name, deployment.Namespace, gtpEnv) + return >pList[0] + } else { + return nil + } + } + + if len(gtpList) == 0 { + return nil + } + + var envMatchedGTPList []v1.GlobalTrafficPolicy + + for _, gtp := range gtpList { + gtpEnv := gtp.Labels[Env] + if gtpEnv == "" { + gtpEnv = Default + } + if gtpEnv == deploymentEnvironment { + envMatchedGTPList = append(envMatchedGTPList, gtp) + } + } + + //if one matches the environment from the gtp, return it + if len(envMatchedGTPList) == 1 { + log.Infof("Newly added deployment with name=%v matched with GTP %v in namespace %v. Env=%v", deployment.Name, envMatchedGTPList[0].Name, deployment.Namespace, deploymentEnvironment) + return &envMatchedGTPList[0] + } + + //No GTPs matched the environment label + if len(envMatchedGTPList) == 0 { + return nil + } + + //Using age as a tiebreak + sort.Slice(envMatchedGTPList, func(i, j int) bool { + iTime := envMatchedGTPList[i].CreationTimestamp.Nanosecond() + jTime := envMatchedGTPList[j].CreationTimestamp.Nanosecond() + return iTime Date: Thu, 12 Mar 2020 13:53:57 -0700 Subject: [PATCH 28/45] Organize docs into GitHub pages (#82) Signed-off-by: Madeline --- CONTRIBUTING.md | 31 ++- README.md | 413 +---------------------------------- docs/Architecture.md | 129 +++++++++++ docs/Compatibility.md | 15 ++ docs/Examples.md | 265 ++++++++++++++++++++++ docs/Index.md | 7 + install/scripts/dev_setup.sh | 29 +++ 7 files changed, 481 insertions(+), 408 deletions(-) create mode 100644 docs/Architecture.md create mode 100644 docs/Compatibility.md create mode 100644 docs/Examples.md create mode 100644 docs/Index.md create mode 100755 install/scripts/dev_setup.sh diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 85f75b10..07e1b4f6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,13 +4,34 @@ We welcome contributions :) ## Submitting PRs * Make sure to check existing issues and file an issue before starting to work on a feature/bug. This will help prevent duplication of work. +Also refer to [Collaboration](./README.md#Collaboration and Communication) for communication channels. ## Setting up for local Development -* Use `Single Cluster` or `Multi-Cluster` sections [HERE](./README.md) to set up the local test cluster. -* Run `admiral` from your IDE. +* Clone the repo and set ADMIRAL_HOME env variable +```bash +git clone https://github.com/istio-ecosystem/admiral.git +cd admiral +export ADMIRAL_HOME=$(pwd) ``` -/admiral/cmd/admiral/main.go --kube_config +* Run a k8s cluster using [minikube](https://kubernetes.io/docs/setup/learning-environment/minikube/) (you can use any k8s cluster if one exists already) +```bash +minikube start --memory=8192 --cpus=4 --kubernetes-version=v1.14.2 +export KUBECONFIG=~/.kube/config +``` +* Install [Prerequisites](./docs/Examples.md#Prerequisite) +* Set up necessary permissions and configurations for Admiral +```bash +$ADMIRAL_HOME/install/scripts/dev_setup.sh +``` +* Run `admiral` from your IDE (assumes that the kubeconfig is for the cluster is at `~/.kube/config`) +``` +$ADMIRAL_HOME/admiral/cmd/admiral/main.go --kube_config "" +``` + +* `Optional`: Adding a second cluster for admiral to monitor (for multi-cluster) +```bash +$ADMIRAL_HOME/install/scripts/cluster-secret.sh admiral ``` -## Code Generation -* If you've made changes to model objects and need to re-generate their clientsets, use `sh hack/update-codegen.sh` \ No newline at end of file +## Protobuf code generation +* If you've made changes to protobuf model objects and need to re-generate their clientsets, use `sh hack/update-codegen.sh` and checkin the generated files \ No newline at end of file diff --git a/README.md b/README.md index 8978f96f..3e785bea 100644 --- a/README.md +++ b/README.md @@ -8,420 +8,27 @@ **Admiral provides automatic configuration and service discovery for multicluster Istio service mesh** -Istio has a very robust set of multi-cluster capabilities. Managing this configuration across multiple clusters at scale is challenging. Admiral takes an opinionated view on this configuration and provides automatic provisioning and syncing across clusters. This removes the complexity from developers and mesh operators pushing this complexity into automation. +Istio has a very robust set of multi-cluster capabilities. Managing this configuration across multiple clusters at scale is challenging. Admiral takes an opinionated view on this configuration and provides automatic provisioning and syncing across clusters. This removes the complexity for developers and mesh operators. -## Install +## [Docs](./docs/Index.md) -### Prerequisite - -One or more k8s clusters will need the following steps executed - -#### Install the below utilities - -`Note`: If running in windows, a bash shell is required (cygwin) - -* Install [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) -* Install [minikube](https://istio.io/docs/setup/platform-setup/minikube/) to bring up a k8s cluster locally (Make sure your `$KUBECONFIG` points to `minikube` before proceeding) -* Install [helm](https://github.com/helm/helm/blob/master/docs/install.md) -* Install [wget](https://www.gnu.org/software/wget/) - -#### Install Istio - -``` -#Download - -wget https://github.com/istio/istio/releases/download/1.4.3/istio-1.4.3-osx.tar.gz -OR -wget https://github.com/istio/istio/releases/download/1.4.3/istio-1.4.3-linux.tar.gz -OR -wget https://github.com/istio/istio/releases/download/1.4.3/istio-1.4.3-win.tar.gz - -#Extract - -tar -xf istio-1.4.3-osx.tar.gz -OR -tar -xf istio-1.4.3-linux.tar.gz -OR -tar -xf istio-1.4.3-win.tar.gz -``` - -``` -#Create istio-system namespace - -kubectl create ns istio-system -``` -``` -#Create k8s secret to be used by Citadel for mTLS cert generation - -kubectl create secret generic cacerts -n istio-system \ - --from-file=istio-1.4.3/samples/certs/ca-cert.pem \ - --from-file=istio-1.4.3/samples/certs/ca-key.pem \ - --from-file=istio-1.4.3/samples/certs/root-cert.pem \ - --from-file=istio-1.4.3/samples/certs/cert-chain.pem -``` -``` -#Generate, install and verify Istio CRDs - -helm template istio-1.4.3/install/kubernetes/helm/istio-init --name istio-init --namespace istio-system | kubectl apply -f - - -#Make sure Istio crds are installed - -kubectl get crds | grep 'istio.io' | wc -l -``` -``` -#Generate & Install Istio - -helm template istio-1.4.3/install/kubernetes/helm/istio --name istio --namespace istio-system \ - -f istio-1.4.3/install/kubernetes/helm/istio/example-values/values-istio-multicluster-gateways.yaml | kubectl apply -f - - -#Verify that istio pods are up - -kubectl get pods -n istio-system -``` - -#### DNS setup -In a k8s cluster, you will have a DNS component that would resolve names. Admiral generates names ending in global (Ex: `stage.greeting.global`) which can be resolved by istiocoredns (as its watching Istio ServiceEntries created by Admiral with those names) installed as part of Istio. -So you have to point DNS resolution for names ending in `global` to point to `ClusterIp` of istiocoredns service. The below step is to point coredns in a k8s cluster to istiocoredns. If you are using kube-dns, you can tweak this script. - -```Note: The below script wipes out existing codedns config map, please manually edit it if you want to try this in a cluster with real services/traffic``` - -``` -#Run the below script for having coredns point to istiocoredns for dns lookups of names ending in global - -./admiral-install-v0.1-beta/scripts/redirect-dns.sh -``` - -#### Remove envoy cluster rewrite filter -Delete Istio's envoy filter for translating `global` to `svc.cluster.local` at istio-ingressgateway because we don't need that as Admiral generates Service Entries for cross cluster communication to just work! -``` -# Delete envoy filter for translating `global` to `svc.cluster.local` -kubectl delete envoyfilter istio-multicluster-ingressgateway -n istio-system -``` - -`Reference:` [K8s cluster installed with Istio_replicated control planes](https://istio.io/docs/setup/install/multicluster/gateways/#deploy-the-istio-control-plane-in-each-cluster) - - -## Example Installations & Demos - -### Single cluster - -#### Install/Run Admiral - -``` -#Download and extract admiral - -wget https://github.com/istio-ecosystem/admiral/releases/download/v0.1-beta/admiral-install-v0.1-beta.tar.gz -tar xvf admiral-install-v0.1-beta.tar.gz -``` - -``` -#Install admiral - -kubectl apply -f ./admiral-install-v0.1-beta/yaml/remotecluster.yaml -kubectl apply -f ./admiral-install-v0.1-beta/yaml/demosinglecluster.yaml - -#Verify admiral is running - -kubectl get pods -n admiral -``` - -``` -#Create the secret for admiral to monitor. - -#Since this is for a single cluster demo the remote and local context are the same -./admiral-install-v0.1-beta/scripts/cluster-secret.sh $KUBECONFIG $KUBECONFIG admiral -``` -``` -#Verify the secret -kubectl get secrets -n admiral -``` - -#### Deploy Sample Services - -``` -#Install test services - -kubectl apply -f ./admiral-install-v0.1-beta/yaml/sample.yaml -``` -``` -#Install the dependency CR (this is optional) - -kubectl apply -f ./admiral-install-v0.1-beta/yaml/sample_dep.yaml - -#Verify that admiral created service names for 'greeting' service - -kubectl get serviceentry -n admiral-sync - -``` - -#### Demo - -Now, run the command below that uses the CNAME generated by Admiral -``` -kubectl exec --namespace=sample -it $(kubectl get pod -l "app=webapp" --namespace=sample -o jsonpath='{.items[0].metadata.name}') -c webapp -- curl -v http://default.greeting.global -``` - -#### Generated configuration - -Admiral generated Istio configuration. - -##### ServiceEntry - -Two service entries were created in the `admiral-sync` namespace. - -```kubectl get ServiceEntry -n admiral-sync``` - -``` -NAME HOSTS LOCATION RESOLUTION AGE -default.greeting.global-se [default.greeting.global] MESH_INTERNAL DNS 76m -default.webapp.global-se [default.webapp.global] MESH_INTERNAL DNS 76m -``` - -```kubectl get ServiceEntry default.greeting.global-se -n admiral-sync -o yaml``` - -Looking in more detail the hostname default.greeting.global is pointing back the default k8s FQDNs - -``` -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - creationTimestamp: "2019-09-20T22:04:59Z" - generation: 1 - labels: - identity: greeting - name: default.greeting.global-se - namespace: admiral-sync - resourceVersion: "452814" - selfLink: /apis/networking.istio.io/v1alpha3/namespaces/admiral-sync/serviceentries/default.greeting.global-se - uid: b02cdbee-dbf2-11e9-9461-0aa9b467cf9c -spec: - addresses: - - 127.0.10.2 - endpoints: - - address: greeting.sample.svc.cluster.local - locality: us-west-2 - ports: - http: 80 - hosts: - - default.greeting.global - location: MESH_INTERNAL - ports: - - name: http - number: 80 - protocol: http - resolution: DNS -``` - - -### Multicluster - -Finish steps from Single Cluster to have Admiral running and ready to watch other clusters (lets call them remote clusters) which we will be setting in the steps below. - -Let's call the cluster used in Single cluster set up `Cluster 1`. Now we will use the steps below to add `Cluster 2` to the mesh and have it monitored by Admiral - -Finish the steps from `Prerequisites` section for `Cluster 2` - -#### Add Cluster 2 to Admiral's watcher -``` -# Set CLUSTER_1 env variable -export CLUSTER_1= - -# Set CLUSTER_2 env variable -export CLUSTER_2= -``` - -``` -# Switch kubectx to Cluster 2 -export KUBECONFIG=$CLUSTER_2 -# Create admiral role and bindings on Cluster 2 -kubectl apply -f ./admiral-install-v0.1-beta/yaml/remotecluster.yaml -``` - -``` -#Switch kubectx to Cluster 1 -export KUBECONFIG=$CLUSTER_1 - -# Create the k8s secret for admiral to monitor Cluster 2. -./admiral-install-v0.1-beta/scripts/cluster-secret.sh $CLUSTER_1 $CLUSTER_2 admiral -``` - -At this point, admiral is watching `Cluster 2` - -#### Deploy Sample Services in Cluster 2 -``` -#Switch kubectx to Cluster 2 -export KUBECONFIG=$CLUSTER_2 - -#Install test services in Cluster 2 - -kubectl apply -f ./admiral-install-v0.1-beta/yaml/remotecluster_sample.yaml -``` - -#### Verify - -``` -#Switch kubectx to Cluster 1 -export KUBECONFIG=$CLUSTER_1 - -# Verify that the ServiceEntry for greeting service in Cluster 1 now has second endpoint (Cluster 2's istio-ingressgateway address) -kubectl get serviceentry default.greeting.global-se -n admiral-sync -o yaml -``` - -#### Demo - -Now run the below request multiple times and see the requests being load balanced between local (Cluster 1) and remote (Cluster 2) instances of greeting service (You can see the response payload change based on which greeting's instance served the request) - -``` -kubectl exec --namespace=sample -it $(kubectl get pod -l "app=webapp" --namespace=sample -o jsonpath='{.items[0].metadata.name}') -c webapp -- curl -v http://default.greeting.global -``` - - -## Admiral Architecture - -![alt text](https://user-images.githubusercontent.com/35096265/65183155-b8244b00-da17-11e9-9f2d-cce5a96fe2e8.png "Admiral Architecture") - -Admiral acts as a controller watching k8s clusters that have a credential stored as a secret object which the namespace Admiral is running in. Admiral delivers Istio configuration to each cluster to enable services to communicate. This configuration is contextual to each cluster, removing the need for service owners to know the mesh cluster topology. - -### Contextual Configuration -With multiple Istio control planes, Admiral instruments contextual Istio configuration for a given k8s service for a cluster from which this service is being accessed from. For example, k8s service running in k8s cluster 1 and k8s cluster 2 when accessed from k8s cluster 3 should be distributed across Istio ingress load balancer in cluster 1 and cluster 2. When the same k8s service is accessed from cluster 2, it should be distributed across local FQDN and the Istio ingress load balancer in cluster 1. - - -### Why is the Configuration Opinionated? - -There are three main aspects that led to deploying service mesh globally with the opinionated configuration automation. - -- Non homologous k8s clusters for HA/DR deployments. The same service binary would not be deployed in a namespace with the same name across two different regions. The namespace naming schema that is utilized, creates a globally unique name. This prevents us from using the default namespace FQDN. Also, the namespace is an administrative context boundary. This means if ownership of the service binary moved from one team to another the service would be deployed in a namespace owned by the new team. If the FQDN used to address the service is bound to the namespace transitioning ownership becomes cumbersome and causes clients to change how the service is called. For these reasons we chose to use a naming abstraction that does not include the namespace in the service’s FQDN. - -- Authoring context, if a single mesh control plane is used with many remote k8s clusters attached to it the authoring of k8s configuration and Istio configuration would be disparate. We expect development teams to manage and utilize Istio features so having these separate would be cumbersome. For example, Istio routing configurations (VirtualService) deployed with CD pipeline would have to be applied to a k8s api-server in a cluster that is different then the main hosting k8s cluster. Also, the resiliency and scalability of a single control plane was concerning but we didn’t have data to prove it would be an issue. - -- The need to have several FQDN for the same service that have different resolution and load balancing properties. For example, service1.global could resolve to local topology first. Service1 being deployed in multiple regions would need a names like service1-west.global and service1-east.global. Each of these names would resolve the region in the name respectively and the opposite region in a failure scenario. These names are often used for testing in one region during deployments and for troubleshooting. - -## Connecting Clusters - -Each cluster is an independent cluster with an Istio control plane. Admiral needs a k8s context to watch each cluster stored as a secret. This is used for Admiral to watch and generate configuration. - -## Global Identifier - -Admiral utilizes the concept of a global service identifier. This identifier is attached to k8s service definitions as a label. This label can be anything and will be defined in the following Dependency types identityLabel field. - -This global identifier is needed to identify the same service running on multiple regions for Active/Active or DR deployments. This prevents the need to have namespace names consistent across multi-region deployments. - -``` ---- -apiVersion: v1 -kind: Service -metadata: - name: my-service - labels: - app: service1 - identity: service1 - env: dev -spec: - ports: - - port: 80 - protocol: TCP - selector: - app: service1 ---- -``` -Service labels will be used to create the default dns name. - -## DNS Names - -By default dns names will be constructed using the values of two labels from the service. -- env -- global-identifier as defined by the Dependency type - -**{env}.{global-identifier}.global** - -For the service above - -**dev.service1.global** - -If the env label is not present the word default will be used. - -**default.service1.mesh** - -*No "real" dns name are created but the core dns plug is used with back ServiceEntries* - -## Types - -Admiral introduces two new CRDs to control the cross cluster automation. - -### Dependency - -The Dependency type can be used for more than just picking the global identifier. The dependency information is used to prune the configuration syncing so only configuration needed in each -cluster is provisioned by Admiral. - -*If the wild card and specific dependency object are both present any service not matching a specific rule will be sync to all clusters* - -``` ---- -apiVersion: admiral.io/v1alpha1 -kind: Dependency -metadata: - name: dependency - namespace: admiral -spec: - source: service1 - identityLabel: identity - destinations: - - service2 - - service3 ---- -``` -This config tells Admiral to only sync configuration for service2 and service3 to any cluster where service1 is running. -Once granular dependency types are defined the identityLabel can be different for separate entries. - -### Global Traffic Policy +## Who uses Admiral? -Using the Global Traffic policy type will allow for the creation of multiple dns names with different routing locality configuration for the service. +Organizations below are **officially** using Admiral. Please send a PR with your organization name if you are using Admiral. -``` ---- -apiVersion: admiral.io/v1alpha1 -kind: GlobalTrafficPolicy -metadata: - name: gtp-service1 -spec: - selector: - app: service1 - policy: - - dns: service1.global - lbtype: TOPOLOGY - - dns: service1-west.global - lbtype: FAILOVER - target: - - region: uswest-2 - weight: 100 - - region: useast-2 - weight: 0 - - dns: service1-east.global - lbtype: FAILOVER - target: - - region: uswest-2 - weight: 0 - - region: useast-2 - weight: 100 ---- -``` +* [Intuit](https://www.intuit.com/) -In this example the service object with the app=service1 label will have 3 dns names created that map to it. -- service1.global - pins traffic the local region the traffic originated -- service1-west.global - sends traffic to the west region and only to east if west in unavailable -- service1-east.global - sends traffic to the east region and only to west if east in unavailable +## Blogs and Presentations -## Who uses Admiral? +* [Multicluster Istio configuration and service discovery using Admiral](https://istio.io/blog/2020/multi-cluster-mesh-automation/) -Organizations below are **officially** using Admiral. Please send a PR with your organization name if you are using Admiral. +* [Stitching a Service Mesh Across Hundreds of Discrete Networks](https://www.youtube.com/watch?v=EWyNbBn1vns) -1. [Intuit](https://www.intuit.com/) +* [Scaling Service Mesh to an Enterprise Microservices Ecosystem](https://apiworld2019aidevworld2019.sched.com/event/SLIQ/pro-talk-scaling-service-mesh-to-an-enterprise-microservices-ecosystem) -## Community Blogs and Presentations +* [Admiral – Enabling Multi-Cluster Mesh](https://www.meetup.com/San-Diego-Cloud-Native-Computing-Meetup/events/262826967/) -1. [Stitching a Service Mesh Across Hundreds of Discrete Networks](https://www.youtube.com/watch?v=EWyNbBn1vns) -2. [Multicluster Istio configuration and service discovery using Admiral](https://istio.io/blog/2020/multi-cluster-mesh-automation/) ## Collaboration and Communication diff --git a/docs/Architecture.md b/docs/Architecture.md new file mode 100644 index 00000000..46b2e778 --- /dev/null +++ b/docs/Architecture.md @@ -0,0 +1,129 @@ +# Admiral Architecture + +![alt text](https://user-images.githubusercontent.com/35096265/65183155-b8244b00-da17-11e9-9f2d-cce5a96fe2e8.png "Admiral Architecture") + +Admiral acts as a controller watching k8s clusters that have a credential stored as a secret object which the namespace Admiral is running in. Admiral delivers Istio configuration to each cluster to enable services to communicate. This configuration is contextual to each cluster, removing the need for service owners to know the mesh cluster topology. + +## Contextual Configuration +With multiple Istio control planes, Admiral instruments contextual Istio configuration for a given k8s service for a cluster from which this service is being accessed from. For example, k8s service running in k8s cluster 1 and k8s cluster 2 when accessed from k8s cluster 3 should be distributed across Istio ingress load balancer in cluster 1 and cluster 2. When the same k8s service is accessed from cluster 2, it should be distributed across local FQDN and the Istio ingress load balancer in cluster 1. + + +## Why is the Configuration Opinionated? + +There are three main aspects that led to deploying service mesh globally with the opinionated configuration automation. + +- Non homologous k8s clusters for HA/DR deployments. The same service binary would not be deployed in a namespace with the same name across two different regions. The namespace naming schema that is utilized, creates a globally unique name. This prevents us from using the default namespace FQDN. Also, the namespace is an administrative context boundary. This means if ownership of the service binary moved from one team to another the service would be deployed in a namespace owned by the new team. If the FQDN used to address the service is bound to the namespace transitioning ownership becomes cumbersome and causes clients to change how the service is called. For these reasons we chose to use a naming abstraction that does not include the namespace in the service’s FQDN. + +- Authoring context, if a single mesh control plane is used with many remote k8s clusters attached to it the authoring of k8s configuration and Istio configuration would be disparate. We expect development teams to manage and utilize Istio features so having these separate would be cumbersome. For example, Istio routing configurations (VirtualService) deployed with CD pipeline would have to be applied to a k8s api-server in a cluster that is different then the main hosting k8s cluster. Also, the resiliency and scalability of a single control plane was concerning but we didn’t have data to prove it would be an issue. + +- The need to have several FQDN for the same service that have different resolution and load balancing properties. For example, service1.global could resolve to local topology first. Service1 being deployed in multiple regions would need a names like service1-west.global and service1-east.global. Each of these names would resolve the region in the name respectively and the opposite region in a failure scenario. These names are often used for testing in one region during deployments and for troubleshooting. + +# Connecting Clusters + +Each cluster is an independent cluster with an Istio control plane. Admiral needs a k8s context to watch each cluster stored as a secret. This is used for Admiral to watch and generate configuration. + +# Global Identifier + +Admiral utilizes the concept of a global service identifier. This identifier is attached to k8s service definitions as a label. This label can be anything and will be defined in the following Dependency types identityLabel field. + +This global identifier is needed to identify the same service running on multiple regions for Active/Active or DR deployments. This prevents the need to have namespace names consistent across multi-region deployments. + + --- + apiVersion: v1 + kind: Service + metadata: + name: my-service + labels: + app: service1 + identity: service1 + env: dev + spec: + ports: + - port: 80 + protocol: TCP + selector: + app: service1 + --- +Service labels will be used to create the default dns name. + +# DNS Names + +By default dns names will be constructed using the values of two labels from the service. +- env +- global-identifier as defined by the Dependency type + +**{env}.{global-identifier}.global** + +For the service above + +**dev.service1.global** + +If the env label is not present the word default will be used. + +**default.service1.mesh** + +*No "real" dns name are created but the core dns plug is used with back ServiceEntries* + +# Types + +Admiral introduces two new CRDs to control the cross cluster automation. + +## Dependency + +The Dependency type can be used for more than just picking the global identifier. The dependency information is used to prune the configuration syncing so only configuration needed in each +cluster is provisioned by Admiral. + +*If the wild card and specific dependency object are both present any service not matching a specific rule will be sync to all clusters* + + --- + apiVersion: admiral.io/v1alpha1 + kind: Dependency + metadata: + name: dependency + namespace: admiral + spec: + source: service1 + identityLabel: identity + destinations: + - service2 + - service3 + --- + +This config tells Admiral to only sync configuration for service2 and service3 to any cluster where service1 is running. +Once granular dependency types are defined the identityLabel can be different for separate entries. + +## Global Traffic Policy + +Using the Global Traffic policy type will allow for the creation of multiple dns names with different routing locality configuration for the service. + + --- + apiVersion: admiral.io/v1alpha1 + kind: GlobalTrafficPolicy + metadata: + name: gtp-service1 + spec: + selector: + app: service1 + policy: + - dns: service1.global + lbtype: TOPOLOGY + - dns: service1-west.global + lbtype: FAILOVER + target: + - region: uswest-2 + weight: 100 + - region: useast-2 + weight: 0 + - dns: service1-east.global + lbtype: FAILOVER + target: + - region: uswest-2 + weight: 0 + - region: useast-2 + weight: 100 + --- + +In this example the service object with the app=service1 label will have 3 dns names created that map to it. +- service1.global - pins traffic the local region the traffic originated +- service1-west.global - sends traffic to the west region and only to east if west in unavailable +- service1-east.global - sends traffic to the east region and only to west if east in unavailable diff --git a/docs/Compatibility.md b/docs/Compatibility.md new file mode 100644 index 00000000..bef97e9f --- /dev/null +++ b/docs/Compatibility.md @@ -0,0 +1,15 @@ +#Compatibility + +##Version compatibility by Istio & K8s +The below information is based on the testing done, please submit a PR if you have it working for versions outside the ones listed below. + +| Admiral Version | Min. Istio Version | Max. Istio Version | Min. K8s Version | Max. K8s Version +|:-----------------:|:---------------------:|:---------------------:|:-----------------:|:-----------------: +v0.1-beta | 1.2.3 | 1.4.6 | 1.13 | 1.14 + +##Admiral feature support by Istio Version + +| Admiral Version | Syncing | Dependency | Global Traffic Policy +|:-----------------:|:---------:|:-------------:|:--------------------: +v0.1-beta | Yes | Yes | No +v0.9 | Yes | Yes | Yes diff --git a/docs/Examples.md b/docs/Examples.md new file mode 100644 index 00000000..32ddb499 --- /dev/null +++ b/docs/Examples.md @@ -0,0 +1,265 @@ + +## Install + +### Prerequisite + +One or more k8s clusters will need the following steps executed + +#### Install the below utilities + +`Note`: If running in windows, a bash shell is required (cygwin) + +* Install [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) +* Install [minikube](https://istio.io/docs/setup/platform-setup/minikube/) to bring up a k8s cluster locally (Make sure your `$KUBECONFIG` points to `minikube` before proceeding) +* Install [helm](https://github.com/helm/helm/blob/master/docs/install.md) +* Install [wget](https://www.gnu.org/software/wget/) + +#### Install Istio + +``` +#Download + +wget https://github.com/istio/istio/releases/download/1.4.3/istio-1.4.3-osx.tar.gz +OR +wget https://github.com/istio/istio/releases/download/1.4.3/istio-1.4.3-linux.tar.gz +OR +wget https://github.com/istio/istio/releases/download/1.4.3/istio-1.4.3-win.tar.gz + +#Extract + +tar -xf istio-1.4.3-osx.tar.gz +OR +tar -xf istio-1.4.3-linux.tar.gz +OR +tar -xf istio-1.4.3-win.tar.gz +``` + +``` +#Create istio-system namespace + +kubectl create ns istio-system +``` +``` +#Create k8s secret to be used by Citadel for mTLS cert generation + +kubectl create secret generic cacerts -n istio-system \ + --from-file=istio-1.4.3/samples/certs/ca-cert.pem \ + --from-file=istio-1.4.3/samples/certs/ca-key.pem \ + --from-file=istio-1.4.3/samples/certs/root-cert.pem \ + --from-file=istio-1.4.3/samples/certs/cert-chain.pem +``` +``` +#Generate, install and verify Istio CRDs + +helm template istio-1.4.3/install/kubernetes/helm/istio-init --name istio-init --namespace istio-system | kubectl apply -f - + +#Make sure Istio crds are installed + +kubectl get crds | grep 'istio.io' | wc -l +``` +``` +#Generate & Install Istio + +helm template istio-1.4.3/install/kubernetes/helm/istio --name istio --namespace istio-system \ + -f istio-1.4.3/install/kubernetes/helm/istio/example-values/values-istio-multicluster-gateways.yaml | kubectl apply -f - + +#Verify that istio pods are up + +kubectl get pods -n istio-system +``` + +#### DNS setup +In a k8s cluster, you will have a DNS component that would resolve names. Admiral generates names ending in global (Ex: `stage.greeting.global`) which can be resolved by istiocoredns (as its watching Istio ServiceEntries created by Admiral with those names) installed as part of Istio. +So you have to point DNS resolution for names ending in `global` to point to `ClusterIp` of istiocoredns service. The below step is to point coredns in a k8s cluster to istiocoredns. If you are using kube-dns, you can tweak this script. + +```Note: The below script wipes out existing codedns config map, please manually edit it if you want to try this in a cluster with real services/traffic``` + +``` +#Run the below script for having coredns point to istiocoredns for dns lookups of names ending in global + +./admiral-install-v0.1-beta/scripts/redirect-dns.sh +``` + +#### Remove envoy cluster rewrite filter +Delete Istio's envoy filter for translating `global` to `svc.cluster.local` at istio-ingressgateway because we don't need that as Admiral generates Service Entries for cross cluster communication to just work! +``` +# Delete envoy filter for translating `global` to `svc.cluster.local` +kubectl delete envoyfilter istio-multicluster-ingressgateway -n istio-system +``` + +`Reference:` [K8s cluster installed with Istio_replicated control planes](https://istio.io/docs/setup/install/multicluster/gateways/#deploy-the-istio-control-plane-in-each-cluster) + + +## Example Installations & Demos + +### Single cluster + +#### Install/Run Admiral + +``` +#Download and extract admiral + +wget https://github.com/istio-ecosystem/admiral/releases/download/v0.1-beta/admiral-install-v0.1-beta.tar.gz +tar xvf admiral-install-v0.1-beta.tar.gz +``` + +``` +#Install admiral + +kubectl apply -f ./admiral-install-v0.1-beta/yaml/remotecluster.yaml +kubectl apply -f ./admiral-install-v0.1-beta/yaml/demosinglecluster.yaml + +#Verify admiral is running + +kubectl get pods -n admiral +``` + +``` +#Create the secret for admiral to monitor. + +#Since this is for a single cluster demo the remote and local context are the same +./admiral-install-v0.1-beta/scripts/cluster-secret.sh $KUBECONFIG $KUBECONFIG admiral +``` +``` +#Verify the secret +kubectl get secrets -n admiral +``` + +#### Deploy Sample Services + +``` +#Install test services + +kubectl apply -f ./admiral-install-v0.1-beta/yaml/sample.yaml +``` +``` +#Install the dependency CR (this is optional) + +kubectl apply -f ./admiral-install-v0.1-beta/yaml/sample_dep.yaml + +#Verify that admiral created service names for 'greeting' service + +kubectl get serviceentry -n admiral-sync + +``` + +#### Demo + +Now, run the command below that uses the CNAME generated by Admiral +``` +kubectl exec --namespace=sample -it $(kubectl get pod -l "app=webapp" --namespace=sample -o jsonpath='{.items[0].metadata.name}') -c webapp -- curl -v http://default.greeting.global +``` + +#### Generated configuration + +Admiral generated Istio configuration. + +##### ServiceEntry + +Two service entries were created in the `admiral-sync` namespace. + +```kubectl get ServiceEntry -n admiral-sync``` + +``` +NAME HOSTS LOCATION RESOLUTION AGE +default.greeting.global-se [default.greeting.global] MESH_INTERNAL DNS 76m +default.webapp.global-se [default.webapp.global] MESH_INTERNAL DNS 76m +``` + +```kubectl get ServiceEntry default.greeting.global-se -n admiral-sync -o yaml``` + +Looking in more detail the hostname default.greeting.global is pointing back the default k8s FQDNs + +``` +apiVersion: networking.istio.io/v1alpha3 +kind: ServiceEntry +metadata: + creationTimestamp: "2019-09-20T22:04:59Z" + generation: 1 + labels: + identity: greeting + name: default.greeting.global-se + namespace: admiral-sync + resourceVersion: "452814" + selfLink: /apis/networking.istio.io/v1alpha3/namespaces/admiral-sync/serviceentries/default.greeting.global-se + uid: b02cdbee-dbf2-11e9-9461-0aa9b467cf9c +spec: + addresses: + - 127.0.10.2 + endpoints: + - address: greeting.sample.svc.cluster.local + locality: us-west-2 + ports: + http: 80 + hosts: + - default.greeting.global + location: MESH_INTERNAL + ports: + - name: http + number: 80 + protocol: http + resolution: DNS +``` + + +### Multicluster + +Finish steps from Single Cluster to have Admiral running and ready to watch other clusters (lets call them remote clusters) which we will be setting in the steps below. + +Let's call the cluster used in Single cluster set up `Cluster 1`. Now we will use the steps below to add `Cluster 2` to the mesh and have it monitored by Admiral + +Finish the steps from `Prerequisites` section for `Cluster 2` + +#### Add Cluster 2 to Admiral's watcher +``` +# Set CLUSTER_1 env variable +export CLUSTER_1= + +# Set CLUSTER_2 env variable +export CLUSTER_2= +``` + +``` +# Switch kubectx to Cluster 2 +export KUBECONFIG=$CLUSTER_2 +# Create admiral role and bindings on Cluster 2 +kubectl apply -f ./admiral-install-v0.1-beta/yaml/remotecluster.yaml +``` + +``` +#Switch kubectx to Cluster 1 +export KUBECONFIG=$CLUSTER_1 + +# Create the k8s secret for admiral to monitor Cluster 2. +./admiral-install-v0.1-beta/scripts/cluster-secret.sh $CLUSTER_1 $CLUSTER_2 admiral +``` + +At this point, admiral is watching `Cluster 2` + +#### Deploy Sample Services in Cluster 2 +``` +#Switch kubectx to Cluster 2 +export KUBECONFIG=$CLUSTER_2 + +#Install test services in Cluster 2 + +kubectl apply -f ./admiral-install-v0.1-beta/yaml/remotecluster_sample.yaml +``` + +#### Verify + +``` +#Switch kubectx to Cluster 1 +export KUBECONFIG=$CLUSTER_1 + +# Verify that the ServiceEntry for greeting service in Cluster 1 now has second endpoint (Cluster 2's istio-ingressgateway address) +kubectl get serviceentry default.greeting.global-se -n admiral-sync -o yaml +``` + +#### Demo + +Now run the below request multiple times and see the requests being load balanced between local (Cluster 1) and remote (Cluster 2) instances of greeting service (You can see the response payload change based on which greeting's instance served the request) + +``` +kubectl exec --namespace=sample -it $(kubectl get pod -l "app=webapp" --namespace=sample -o jsonpath='{.items[0].metadata.name}') -c webapp -- curl -v http://default.greeting.global +``` \ No newline at end of file diff --git a/docs/Index.md b/docs/Index.md new file mode 100644 index 00000000..faf0f5ca --- /dev/null +++ b/docs/Index.md @@ -0,0 +1,7 @@ +## Welcome to Admiral Docs! + +### [Architecture](Architecture.md) + +### [Examples](Examples.md) + +### [Compatibility](Compatibility.md) \ No newline at end of file diff --git a/install/scripts/dev_setup.sh b/install/scripts/dev_setup.sh new file mode 100755 index 00000000..f3955c4d --- /dev/null +++ b/install/scripts/dev_setup.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +if [ -z "$ADMIRAL_HOME" ] +then + echo "\$ADMIRAL_HOME is not set" + exit 1 +fi + +if [ -z "$KUBECONFIG" ] +then + echo "\$KUBECONFIG is not set" + exit 1 +fi + +cd $ADMIRAL_HOME + +make gen-yaml + +kubectl apply -f $ADMIRAL_HOME/out/yaml/remotecluster.yaml + +kubectl apply -f $ADMIRAL_HOME/out/yaml/demosinglecluster.yaml + +kubectl delete deployment admiral -n admiral + +$ADMIRAL_HOME/install/scripts/cluster-secret.sh $KUBECONFIG $KUBECONFIG admiral + +kubectl apply -f $ADMIRAL_HOME/out/yaml/sample.yaml + +kubectl apply -f $ADMIRAL_HOME/out/yaml/sample_dep.yaml \ No newline at end of file From 4ff5405a62e4154a346f88b0248a77936eedc25e Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Thu, 12 Mar 2020 14:07:42 -0700 Subject: [PATCH 29/45] Set theme jekyll-theme-leap-day Signed-off-by: Madeline --- docs/_config.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/_config.yml diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 00000000..b8497135 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-leap-day \ No newline at end of file From 6ddb79065cee409137731b172fa695b602fdf9e9 Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Thu, 12 Mar 2020 14:12:12 -0700 Subject: [PATCH 30/45] Rename Index.md to index.md Signed-off-by: Madeline --- docs/{Index.md => index.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename docs/{Index.md => index.md} (71%) diff --git a/docs/Index.md b/docs/index.md similarity index 71% rename from docs/Index.md rename to docs/index.md index faf0f5ca..a62bebc1 100644 --- a/docs/Index.md +++ b/docs/index.md @@ -4,4 +4,4 @@ ### [Examples](Examples.md) -### [Compatibility](Compatibility.md) \ No newline at end of file +### [Compatibility](Compatibility.md) From e2f2918f40dc30af43e392eeff132ab07b14192b Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Thu, 12 Mar 2020 14:22:20 -0700 Subject: [PATCH 31/45] Rename index.md to Index.md Signed-off-by: Madeline --- docs/{index.md => Index.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{index.md => Index.md} (100%) diff --git a/docs/index.md b/docs/Index.md similarity index 100% rename from docs/index.md rename to docs/Index.md From e96577b3e567b7c588906ea76b797d77037186b2 Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Thu, 12 Mar 2020 14:23:13 -0700 Subject: [PATCH 32/45] Update Compatibility.md Signed-off-by: Madeline --- docs/Compatibility.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Compatibility.md b/docs/Compatibility.md index bef97e9f..5454d947 100644 --- a/docs/Compatibility.md +++ b/docs/Compatibility.md @@ -1,13 +1,13 @@ -#Compatibility +# Compatibility -##Version compatibility by Istio & K8s +## Version compatibility by Istio & K8s The below information is based on the testing done, please submit a PR if you have it working for versions outside the ones listed below. | Admiral Version | Min. Istio Version | Max. Istio Version | Min. K8s Version | Max. K8s Version |:-----------------:|:---------------------:|:---------------------:|:-----------------:|:-----------------: v0.1-beta | 1.2.3 | 1.4.6 | 1.13 | 1.14 -##Admiral feature support by Istio Version +## Admiral feature support by Istio Version | Admiral Version | Syncing | Dependency | Global Traffic Policy |:-----------------:|:---------:|:-------------:|:--------------------: From 37e374042f576d4fb7d58ea402375781f057b0a8 Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Fri, 13 Mar 2020 13:20:25 -0700 Subject: [PATCH 33/45] Use exportTo field to filter resources for syncing across clusters (#81) Signed-off-by: Madeline --- admiral/pkg/clusters/handler.go | 53 +++++++++++++++---- admiral/pkg/clusters/handler_test.go | 48 +++++++++++++++++ .../controller/istio/destinationrule_test.go | 27 ++++++++++ .../pkg/controller/istio/serviceentry_test.go | 27 ++++++++++ .../controller/istio/virtualservice_test.go | 27 ++++++++++ admiral/pkg/test/mock.go | 46 ++++++++++++++++ 6 files changed, 218 insertions(+), 10 deletions(-) create mode 100644 admiral/pkg/clusters/handler_test.go create mode 100644 admiral/pkg/controller/istio/destinationrule_test.go create mode 100644 admiral/pkg/controller/istio/serviceentry_test.go create mode 100644 admiral/pkg/controller/istio/virtualservice_test.go diff --git a/admiral/pkg/clusters/handler.go b/admiral/pkg/clusters/handler.go index e60408e6..80c97bc4 100644 --- a/admiral/pkg/clusters/handler.go +++ b/admiral/pkg/clusters/handler.go @@ -161,42 +161,79 @@ func getDestinationRule(host string, locality string, gtpWrapper *v1.GlobalTraff return dr } -func (ic *ServiceEntryHandler) Added(obj *v1alpha3.ServiceEntry) { - //log.Infof("New Pod %s on cluster: %s in namespace: %s", obj.Name, obj.ClusterName, obj.Namespace) +func (se *ServiceEntryHandler) Added(obj *v1alpha3.ServiceEntry) { + if IgnoreIstioResource(obj.Spec.ExportTo) { + return + } } -func (ic *ServiceEntryHandler) Updated(obj *v1alpha3.ServiceEntry) { - // log.Infof("Pod deleted %s on cluster: %s in namespace: %s", obj.Name, obj.ClusterName, obj.Namespace) +func (se *ServiceEntryHandler) Updated(obj *v1alpha3.ServiceEntry) { + if IgnoreIstioResource(obj.Spec.ExportTo) { + return + } } -func (ic *ServiceEntryHandler) Deleted(obj *v1alpha3.ServiceEntry) { - // log.Infof("Pod deleted %s on cluster: %s in namespace: %s", obj.Name, obj.ClusterName, obj.Namespace) +func (se *ServiceEntryHandler) Deleted(obj *v1alpha3.ServiceEntry) { + if IgnoreIstioResource(obj.Spec.ExportTo) { + return + } } func (dh *DestinationRuleHandler) Added(obj *v1alpha3.DestinationRule) { + if IgnoreIstioResource(obj.Spec.ExportTo) { + return + } handleDestinationRuleEvent(obj, dh, common.Add, common.DestinationRule) } func (dh *DestinationRuleHandler) Updated(obj *v1alpha3.DestinationRule) { + if IgnoreIstioResource(obj.Spec.ExportTo) { + return + } handleDestinationRuleEvent(obj, dh, common.Update, common.DestinationRule) } func (dh *DestinationRuleHandler) Deleted(obj *v1alpha3.DestinationRule) { + if IgnoreIstioResource(obj.Spec.ExportTo) { + return + } handleDestinationRuleEvent(obj, dh, common.Delete, common.DestinationRule) } func (vh *VirtualServiceHandler) Added(obj *v1alpha3.VirtualService) { + if IgnoreIstioResource(obj.Spec.ExportTo) { + return + } handleVirtualServiceEvent(obj, vh, common.Add, common.VirtualService) } func (vh *VirtualServiceHandler) Updated(obj *v1alpha3.VirtualService) { + if IgnoreIstioResource(obj.Spec.ExportTo) { + return + } handleVirtualServiceEvent(obj, vh, common.Update, common.VirtualService) } func (vh *VirtualServiceHandler) Deleted(obj *v1alpha3.VirtualService) { + if IgnoreIstioResource(obj.Spec.ExportTo) { + return + } handleVirtualServiceEvent(obj, vh, common.Delete, common.VirtualService) } +func IgnoreIstioResource(exportTo []string) bool { + if exportTo == nil || len(exportTo) == 0 { + return false + } else { + for _, namespace := range exportTo { + if namespace == "*" { + return false + } + } + } + return true +} + func handleDestinationRuleEvent(obj *v1alpha3.DestinationRule, dh *DestinationRuleHandler, event common.Event, resourceType common.ResourceType) { destinationRule := obj.Spec @@ -493,10 +530,6 @@ func addUpdateDestinationRule(obj *v1alpha3.DestinationRule, exist *v1alpha3.Des } } -func createVirtualServiceSkeletion(vs v1alpha32.VirtualService, name string, namespace string) *v1alpha3.VirtualService { - return &v1alpha3.VirtualService{Spec:vs, ObjectMeta: v12.ObjectMeta{Name:name, Namespace: namespace}} -} - func createServiceEntrySkeletion(se v1alpha32.ServiceEntry, name string, namespace string) *v1alpha3.ServiceEntry { return &v1alpha3.ServiceEntry{Spec:se, ObjectMeta: v12.ObjectMeta{Name:name, Namespace: namespace}} } diff --git a/admiral/pkg/clusters/handler_test.go b/admiral/pkg/clusters/handler_test.go new file mode 100644 index 00000000..12fece7d --- /dev/null +++ b/admiral/pkg/clusters/handler_test.go @@ -0,0 +1,48 @@ +package clusters + +import ( + "testing" +) + +func TestIgnoreIstioResource(t *testing.T) { + + //Struct of test case info. Name is required. + testCases := []struct { + name string + exportTo []string + expectedResult bool + }{ + { + name: "Should return false when exportTo is not present", + exportTo: nil, + expectedResult: false, + }, + { + name: "Should return false when its exported to *", + exportTo: []string {"*"}, + expectedResult: false, + }, + { + name: "Should return true when its exported to .", + exportTo: []string {"."}, + expectedResult: true, + }, + { + name: "Should return true when its exported to a handful of namespaces", + exportTo: []string {"namespace1", "namespace2"}, + expectedResult: true, + }, + } + + //Run the test for every provided case + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + result := IgnoreIstioResource(c.exportTo) + if result == c.expectedResult { + //perfect + } else { + t.Errorf("Failed. Got %v, expected %v",result, c.expectedResult) + } + }) + } +} diff --git a/admiral/pkg/controller/istio/destinationrule_test.go b/admiral/pkg/controller/istio/destinationrule_test.go new file mode 100644 index 00000000..553bb8af --- /dev/null +++ b/admiral/pkg/controller/istio/destinationrule_test.go @@ -0,0 +1,27 @@ +package istio + +import ( + "github.com/istio-ecosystem/admiral/admiral/pkg/test" + "k8s.io/client-go/tools/clientcmd" + "testing" + "time" +) + +func TestNewDestinationRuleController(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{}) + handler := test.MockDestinationRuleHandler{} + + destinationRuleController, err := NewDestinationRuleController(stop, &handler, config, time.Duration(1000)) + + if err != nil { + t.Errorf("Unexpected err %v", err) + } + + if destinationRuleController == nil { + t.Errorf("DestinationRule controller should never be nil without an error thrown") + } +} \ No newline at end of file diff --git a/admiral/pkg/controller/istio/serviceentry_test.go b/admiral/pkg/controller/istio/serviceentry_test.go new file mode 100644 index 00000000..ef8ccb23 --- /dev/null +++ b/admiral/pkg/controller/istio/serviceentry_test.go @@ -0,0 +1,27 @@ +package istio + +import ( + "github.com/istio-ecosystem/admiral/admiral/pkg/test" + "k8s.io/client-go/tools/clientcmd" + "testing" + "time" +) + +func TestNewServiceEntryController(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{}) + handler := test.MockServiceEntryHandler{} + + serviceEntryController, err := NewServiceEntryController(stop, &handler, config, time.Duration(1000)) + + if err != nil { + t.Errorf("Unexpected err %v", err) + } + + if serviceEntryController == nil { + t.Errorf("ServiceEntry controller should never be nil without an error thrown") + } +} \ No newline at end of file diff --git a/admiral/pkg/controller/istio/virtualservice_test.go b/admiral/pkg/controller/istio/virtualservice_test.go new file mode 100644 index 00000000..7edbeaea --- /dev/null +++ b/admiral/pkg/controller/istio/virtualservice_test.go @@ -0,0 +1,27 @@ +package istio + +import ( + "github.com/istio-ecosystem/admiral/admiral/pkg/test" + "k8s.io/client-go/tools/clientcmd" + "testing" + "time" +) + +func TestNewVirtualServiceController(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{}) + handler := test.MockVirtualServiceHandler{} + + virtualServiceController, err := NewVirtualServiceController(stop, &handler, config, time.Duration(1000)) + + if err != nil { + t.Errorf("Unexpected err %v", err) + } + + if virtualServiceController == nil { + t.Errorf("VirtualService controller should never be nil without an error thrown") + } +} \ No newline at end of file diff --git a/admiral/pkg/test/mock.go b/admiral/pkg/test/mock.go index 1e489e07..29107b58 100644 --- a/admiral/pkg/test/mock.go +++ b/admiral/pkg/test/mock.go @@ -2,6 +2,7 @@ package test import ( "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" + v1alpha32 "istio.io/client-go/pkg/apis/networking/v1alpha3" k8sAppsV1 "k8s.io/api/apps/v1" k8sCoreV1 "k8s.io/api/core/v1" ) @@ -92,3 +93,48 @@ func (m *MockGlobalTrafficHandler) Updated(obj *v1.GlobalTrafficPolicy) { func (m *MockGlobalTrafficHandler) Deleted(obj *v1.GlobalTrafficPolicy) { } + +type MockServiceEntryHandler struct { +} + +func (m *MockServiceEntryHandler) Added(obj *v1alpha32.ServiceEntry) { + +} + +func (m *MockServiceEntryHandler) Updated(obj *v1alpha32.ServiceEntry) { + +} + +func (m *MockServiceEntryHandler) Deleted(obj *v1alpha32.ServiceEntry) { + +} + +type MockVirtualServiceHandler struct { +} + +func (m *MockVirtualServiceHandler) Added(obj *v1alpha32.VirtualService) { + +} + +func (m *MockVirtualServiceHandler) Updated(obj *v1alpha32.VirtualService) { + +} + +func (m *MockVirtualServiceHandler) Deleted(obj *v1alpha32.VirtualService) { + +} + +type MockDestinationRuleHandler struct { +} + +func (m *MockDestinationRuleHandler) Added(obj *v1alpha32.DestinationRule) { + +} + +func (m *MockDestinationRuleHandler) Updated(obj *v1alpha32.DestinationRule) { + +} + +func (m *MockDestinationRuleHandler) Deleted(obj *v1alpha32.DestinationRule) { + +} From dac24500596a7e69361feb49eaf524d326851c33 Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Tue, 24 Mar 2020 16:31:02 -0700 Subject: [PATCH 34/45] Fix helm commands for newer versions of helm. Signed-off-by: Madeline --- docs/Examples.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Examples.md b/docs/Examples.md index 32ddb499..2ed3345a 100644 --- a/docs/Examples.md +++ b/docs/Examples.md @@ -51,7 +51,7 @@ kubectl create secret generic cacerts -n istio-system \ ``` #Generate, install and verify Istio CRDs -helm template istio-1.4.3/install/kubernetes/helm/istio-init --name istio-init --namespace istio-system | kubectl apply -f - +helm template istio-1.4.3/install/kubernetes/helm/istio-init --namespace istio-system | kubectl apply -f - #Make sure Istio crds are installed @@ -60,7 +60,7 @@ kubectl get crds | grep 'istio.io' | wc -l ``` #Generate & Install Istio -helm template istio-1.4.3/install/kubernetes/helm/istio --name istio --namespace istio-system \ +helm template istio-1.4.3/install/kubernetes/helm/istio --namespace istio-system \ -f istio-1.4.3/install/kubernetes/helm/istio/example-values/values-istio-multicluster-gateways.yaml | kubectl apply -f - #Verify that istio pods are up @@ -262,4 +262,4 @@ Now run the below request multiple times and see the requests being load balance ``` kubectl exec --namespace=sample -it $(kubectl get pod -l "app=webapp" --namespace=sample -o jsonpath='{.items[0].metadata.name}') -c webapp -- curl -v http://default.greeting.global -``` \ No newline at end of file +``` From 35a8404dcf85d2fa57a7682e3064bab86658895a Mon Sep 17 00:00:00 2001 From: vrushalijoshi Date: Tue, 24 Mar 2020 22:46:50 -0700 Subject: [PATCH 35/45] Update sidecar resource in workload namespace (#84) * Update sidecar resource in workload namespace Signed-off-by: vjoshi3 * Updating review comments Signed-off-by: vjoshi3 * Adding tests for sidecar update code Signed-off-by: vjoshi3 * updating tests for sidecar update code Signed-off-by: vjoshi3 Co-authored-by: vjoshi3 Signed-off-by: Madeline --- admiral/cmd/admiral/cmd/root.go | 6 +- admiral/pkg/clusters/handler.go | 39 ++++++-- admiral/pkg/clusters/registry.go | 16 +++- admiral/pkg/clusters/registry_test.go | 85 ++++++++++------- admiral/pkg/clusters/serviceentry.go | 98 +++++++++++++++----- admiral/pkg/clusters/serviceentry_test.go | 82 ++++++++++++++-- admiral/pkg/clusters/types.go | 8 +- admiral/pkg/controller/common/config.go | 11 ++- admiral/pkg/controller/common/types.go | 62 +++++++++++-- admiral/pkg/controller/istio/sidecar.go | 75 +++++++++++++++ admiral/pkg/controller/istio/sidecar_test.go | 27 ++++++ admiral/pkg/test/mock.go | 19 +++- go.sum | 4 + 13 files changed, 435 insertions(+), 97 deletions(-) create mode 100644 admiral/pkg/controller/istio/sidecar.go create mode 100644 admiral/pkg/controller/istio/sidecar_test.go diff --git a/admiral/cmd/admiral/cmd/root.go b/admiral/cmd/admiral/cmd/root.go index 540a2ab1..81e34d76 100644 --- a/admiral/cmd/admiral/cmd/root.go +++ b/admiral/cmd/admiral/cmd/root.go @@ -16,7 +16,7 @@ import ( ) var ( - ctx, cancel = context.WithCancel(context.Background()) + ctx, cancel = context.WithCancel(context.Background()) ) // GetRootCmd returns the root of the cobra command-tree. @@ -85,6 +85,10 @@ func GetRootCmd(args []string) *cobra.Command { "The workload identity key, on deployment which holds identity value used to generate cname by admiral. Default label key will be \"identity\" Admiral will look for a label with this key. If present, that will be used. If not, it will try an annotation (for use cases where an identity is longer than 63 chars)") rootCmd.PersistentFlags().StringVar(¶ms.LabelSet.GlobalTrafficDeploymentLabel, "globaltraffic_deployment_label", "identity", "The label key which will be used to tie globaltrafficpolicy objects to deployments. Configured separately to the workload identity key because this one won't fall back to annotations.") + rootCmd.PersistentFlags().StringVar(¶ms.WorkloadSidecarUpdate, "workload_sidecar_update", "disabled", + "The parameter will be used to decide whether to update workload sidecar resource or not. By default these updates will be disabled.") + rootCmd.PersistentFlags().StringVar(¶ms.WorkloadSidecarName, "workload_sidecar_name", "default", + "Name of the sidecar resource in the workload namespace. By default sidecar resource will be named as \"default\".") return rootCmd } diff --git a/admiral/pkg/clusters/handler.go b/admiral/pkg/clusters/handler.go index 80c97bc4..7248becf 100644 --- a/admiral/pkg/clusters/handler.go +++ b/admiral/pkg/clusters/handler.go @@ -15,22 +15,26 @@ import ( k8sAppsV1 "k8s.io/api/apps/v1" k8sV1 "k8s.io/api/core/v1" - ) type ServiceEntryHandler struct { RemoteRegistry *RemoteRegistry - ClusterID string + ClusterID string } type DestinationRuleHandler struct { RemoteRegistry *RemoteRegistry - ClusterID string + ClusterID string } type VirtualServiceHandler struct { RemoteRegistry *RemoteRegistry - ClusterID string + ClusterID string +} + +type SidecarHandler struct { + RemoteRegistry *RemoteRegistry + ClusterID string } func updateIdentityDependencyCache(sourceIdentity string, identityDependencyCache *common.MapOfMaps, dr *v1.Dependency) { @@ -152,9 +156,9 @@ func getDestinationRule(host string, locality string, gtpWrapper *v1.GlobalTraff loadBalancerSettings.LocalityLbSetting = localityLbSettings dr.TrafficPolicy.LoadBalancer = loadBalancerSettings dr.TrafficPolicy.OutlierDetection = &v1alpha32.OutlierDetection{ - BaseEjectionTime: &types.Duration{Seconds: 120}, + BaseEjectionTime: &types.Duration{Seconds: 120}, ConsecutiveErrors: 10, - Interval: &types.Duration{Seconds: 60}, + Interval: &types.Duration{Seconds: 60}, } } } @@ -221,6 +225,18 @@ func (vh *VirtualServiceHandler) Deleted(obj *v1alpha3.VirtualService) { handleVirtualServiceEvent(obj, vh, common.Delete, common.VirtualService) } +func (dh *SidecarHandler) Added(obj *v1alpha3.Sidecar) { + return +} + +func (dh *SidecarHandler) Updated(obj *v1alpha3.Sidecar) { + return +} + +func (dh *SidecarHandler) Deleted(obj *v1alpha3.Sidecar) { + return +} + func IgnoreIstioResource(exportTo []string) bool { if exportTo == nil || len(exportTo) == 0 { return false @@ -387,7 +403,6 @@ func createDestinationRuleForLocal(remoteController *RemoteController, localDrNa } newDestinationRule := createDestinationRulSkeletion(*destinationRule, localDrName, syncNamespace) - if newDestinationRule != nil { addUpdateDestinationRule(newDestinationRule, existsDestinationRule, syncNamespace, remoteController) } @@ -531,11 +546,15 @@ func addUpdateDestinationRule(obj *v1alpha3.DestinationRule, exist *v1alpha3.Des } func createServiceEntrySkeletion(se v1alpha32.ServiceEntry, name string, namespace string) *v1alpha3.ServiceEntry { - return &v1alpha3.ServiceEntry{Spec:se, ObjectMeta: v12.ObjectMeta{Name:name, Namespace: namespace}} + return &v1alpha3.ServiceEntry{Spec: se, ObjectMeta: v12.ObjectMeta{Name: name, Namespace: namespace}} +} + +func createSidecarSkeletion(sidecar v1alpha32.Sidecar, name string, namespace string) *v1alpha3.Sidecar { + return &v1alpha3.Sidecar{Spec: sidecar, ObjectMeta: v12.ObjectMeta{Name: name, Namespace: namespace}} } func createDestinationRulSkeletion(dr v1alpha32.DestinationRule, name string, namespace string) *v1alpha3.DestinationRule { - return &v1alpha3.DestinationRule{Spec:dr, ObjectMeta: v12.ObjectMeta{Name:name, Namespace: namespace}} + return &v1alpha3.DestinationRule{Spec: dr, ObjectMeta: v12.ObjectMeta{Name: name, Namespace: namespace}} } func getServiceForDeployment(rc *RemoteController, deployment *k8sAppsV1.Deployment) *k8sV1.Service { @@ -597,4 +616,4 @@ func copyEndpoint(e *v1alpha32.ServiceEntry_Endpoint) *v1alpha32.ServiceEntry_En ports := make(map[string]uint32) util.MapCopy(ports, e.Ports) return &v1alpha32.ServiceEntry_Endpoint{Address: e.Address, Ports: ports, Locality: e.Locality, Labels: labels} -} \ No newline at end of file +} diff --git a/admiral/pkg/clusters/registry.go b/admiral/pkg/clusters/registry.go index 5b90aa1a..f9f03fef 100644 --- a/admiral/pkg/clusters/registry.go +++ b/admiral/pkg/clusters/registry.go @@ -54,10 +54,11 @@ func InitAdmiral(ctx context.Context, params common.AdmiralParams) (*RemoteRegis CnameDependentClusterCache: common.NewMapOfMaps(), ClusterLocalityCache: common.NewMapOfMaps(), IdentityDependencyCache: common.NewMapOfMaps(), + DependencyNamespaceCache: common.NewSidecarEgressMap(), CnameIdentityCache: &sync.Map{}, SubsetServiceEntryIdentityCache: &sync.Map{}, ServiceEntryAddressStore: &ServiceEntryAddressStore{EntryAddresses: map[string]string{}, Addresses: []string{}}, - GlobalTrafficCache: gtpCache, + GlobalTrafficCache: gtpCache, } configMapController, err := admiral.NewConfigMapController() @@ -146,26 +147,32 @@ func (r *RemoteRegistry) createCacheController(clientConfig *rest.Config, cluste } log.Infof("starting service entry controller for custerID: %v", clusterID) - rc.ServiceEntryController, err = istio.NewServiceEntryController(stop, &ServiceEntryHandler{RemoteRegistry: r, ClusterID:clusterID}, clientConfig, resyncPeriod) + rc.ServiceEntryController, err = istio.NewServiceEntryController(stop, &ServiceEntryHandler{RemoteRegistry: r, ClusterID: clusterID}, clientConfig, resyncPeriod) if err != nil { return fmt.Errorf(" Error with ServiceEntryController init: %v", err) } log.Infof("starting destination rule controller for custerID: %v", clusterID) - rc.DestinationRuleController, err = istio.NewDestinationRuleController(stop, &DestinationRuleHandler{RemoteRegistry: r, ClusterID:clusterID}, clientConfig, resyncPeriod) + rc.DestinationRuleController, err = istio.NewDestinationRuleController(stop, &DestinationRuleHandler{RemoteRegistry: r, ClusterID: clusterID}, clientConfig, resyncPeriod) if err != nil { return fmt.Errorf(" Error with DestinationRuleController init: %v", err) } log.Infof("starting virtual service controller for custerID: %v", clusterID) - rc.VirtualServiceController, err = istio.NewVirtualServiceController(stop, &VirtualServiceHandler{RemoteRegistry: r, ClusterID:clusterID}, clientConfig, resyncPeriod) + rc.VirtualServiceController, err = istio.NewVirtualServiceController(stop, &VirtualServiceHandler{RemoteRegistry: r, ClusterID: clusterID}, clientConfig, resyncPeriod) if err != nil { return fmt.Errorf(" Error with VirtualServiceController init: %v", err) } + rc.SidecarController, err = istio.NewSidecarController(stop, &SidecarHandler{RemoteRegistry: r, ClusterID: clusterID}, clientConfig, resyncPeriod) + + if err != nil { + return fmt.Errorf(" Error with DestinationRuleController init: %v", err) + } + r.Lock() defer r.Unlock() r.remoteControllers[clusterID] = &rc @@ -190,4 +197,3 @@ func (r *RemoteRegistry) deleteCacheController(clusterID string) error { log.Infof(LogFormat, "Delete", "remote-controller", clusterID, clusterID, "success") return nil } - diff --git a/admiral/pkg/clusters/registry_test.go b/admiral/pkg/clusters/registry_test.go index 01f0067f..8fe48060 100644 --- a/admiral/pkg/clusters/registry_test.go +++ b/admiral/pkg/clusters/registry_test.go @@ -10,6 +10,7 @@ import ( "github.com/istio-ecosystem/admiral/admiral/pkg/test" log "github.com/sirupsen/logrus" networking "istio.io/api/networking/v1alpha3" + "istio.io/client-go/pkg/apis/networking/v1alpha3" k8sAppsV1 "k8s.io/api/apps/v1" k8sCoreV1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -20,26 +21,26 @@ import ( func init() { p := common.AdmiralParams{ - KubeconfigPath: "testdata/fake.config", - LabelSet: &common.LabelSet{}, - EnableSAN: true, - SANPrefix: "prefix", - HostnameSuffix: "mesh", - SyncNamespace: "ns", - CacheRefreshDuration: time.Minute, + KubeconfigPath: "testdata/fake.config", + LabelSet: &common.LabelSet{}, + EnableSAN: true, + SANPrefix: "prefix", + HostnameSuffix: "mesh", + SyncNamespace: "ns", + CacheRefreshDuration: time.Minute, ClusterRegistriesNamespace: "default", - DependenciesNamespace: "default", - SecretResolver: "", - + DependenciesNamespace: "default", + SecretResolver: "", + WorkloadSidecarUpdate: "enabled", + WorkloadSidecarName: "default", } - p.LabelSet.WorkloadIdentityKey="identity" - p.LabelSet.GlobalTrafficDeploymentLabel="identity" + p.LabelSet.WorkloadIdentityKey = "identity" + p.LabelSet.GlobalTrafficDeploymentLabel = "identity" common.InitializeConfig(p) } - func TestDeleteCacheControllerThatDoesntExist(t *testing.T) { w := RemoteRegistry{ @@ -110,6 +111,22 @@ func TestCopyEndpoint(t *testing.T) { } +func TestCopySidecar(t *testing.T) { + spec := networking.Sidecar{ + WorkloadSelector: &networking.WorkloadSelector{ + Labels: map[string]string{"TestLabel": "TestValue"}, + }, + } + + sidecar := v1alpha3.Sidecar{Spec: spec} + + newSidecar := copySidecar(&sidecar) + + if newSidecar.Spec.WorkloadSelector != spec.WorkloadSelector { + t.Fail() + } +} + func TestCreateDestinationRuleForLocalNoDeployLabel(t *testing.T) { config := rest.Config{ @@ -236,10 +253,10 @@ func TestInitAdmiral(t *testing.T) { p := common.AdmiralParams{ KubeconfigPath: "testdata/fake.config", - LabelSet: &common.LabelSet{}, + LabelSet: &common.LabelSet{}, } - p.LabelSet.WorkloadIdentityKey="overridden-key" + p.LabelSet.WorkloadIdentityKey = "overridden-key" rr, err := InitAdmiral(context.Background(), p) @@ -361,43 +378,43 @@ func TestGetServiceForDeployment(t *testing.T) { Port: 8090, }, } - service.Spec.Selector = map[string]string{"under-test":"true"} + service.Spec.Selector = map[string]string{"under-test": "true"} rcWithService.ServiceController.Cache.Put(&service) deploymentWithNoSelector := k8sAppsV1.Deployment{} deploymentWithNoSelector.Name = "dep1" - deploymentWithNoSelector.Namespace ="under-test" + deploymentWithNoSelector.Namespace = "under-test" deploymentWithNoSelector.Spec.Selector = &metav1.LabelSelector{} deploymentWithSelector := k8sAppsV1.Deployment{} deploymentWithSelector.Name = "dep2" deploymentWithSelector.Namespace = "under-test" - deploymentWithSelector.Spec.Selector = &metav1.LabelSelector{MatchLabels: map[string]string{"under-test":"true"}} + deploymentWithSelector.Spec.Selector = &metav1.LabelSelector{MatchLabels: map[string]string{"under-test": "true"}} //Struct of test case info. Name is required. testCases := []struct { - name string - controller *RemoteController - deployment *k8sAppsV1.Deployment + name string + controller *RemoteController + deployment *k8sAppsV1.Deployment expectedService *k8sCoreV1.Service }{ { - name: "Should return nil with nothing in the cache", - controller:baseRc, - deployment:nil, - expectedService:nil, + name: "Should return nil with nothing in the cache", + controller: baseRc, + deployment: nil, + expectedService: nil, }, { - name: "Should not match if selectors don't match", - controller:rcWithService, - deployment:&deploymentWithNoSelector, - expectedService:nil, + name: "Should not match if selectors don't match", + controller: rcWithService, + deployment: &deploymentWithNoSelector, + expectedService: nil, }, { - name: "Should return proper service", - controller:rcWithService, - deployment:&deploymentWithSelector, - expectedService:&service, + name: "Should return proper service", + controller: rcWithService, + deployment: &deploymentWithSelector, + expectedService: &service, }, } @@ -410,7 +427,7 @@ func TestGetServiceForDeployment(t *testing.T) { } else { if !cmp.Equal(resultingService, c.expectedService) { log.Infof("Service diff: %v", cmp.Diff(resultingService, c.expectedService)) - t.Errorf("Service mismatch. Got %v, expected %v",resultingService, c.expectedService) + t.Errorf("Service mismatch. Got %v, expected %v", resultingService, c.expectedService) } } }) diff --git a/admiral/pkg/clusters/serviceentry.go b/admiral/pkg/clusters/serviceentry.go index 54dab051..cf7878e9 100644 --- a/admiral/pkg/clusters/serviceentry.go +++ b/admiral/pkg/clusters/serviceentry.go @@ -9,6 +9,7 @@ import ( log "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" networking "istio.io/api/networking/v1alpha3" + "istio.io/client-go/pkg/apis/networking/v1alpha3" k8sAppsV1 "k8s.io/api/apps/v1" k8sV1 "k8s.io/api/core/v1" v12 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -28,13 +29,13 @@ func createServiceEntry(rc *RemoteController, admiralCache *AdmiralCache, //Handling retries for getting/putting service entries from/in cache //initializations - var err error = nil + var err error = nil maxRetries := 3 counter := 0 address := "" needsCacheUpdate := false - for err==nil && counter Date: Tue, 24 Mar 2020 23:41:32 -0700 Subject: [PATCH 36/45] updating the sidecar controller (#86) Signed-off-by: vjoshi3 Co-authored-by: vjoshi3 Signed-off-by: Madeline --- admiral/pkg/controller/istio/sidecar.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/admiral/pkg/controller/istio/sidecar.go b/admiral/pkg/controller/istio/sidecar.go index 7ac33fbb..fa961ab8 100644 --- a/admiral/pkg/controller/istio/sidecar.go +++ b/admiral/pkg/controller/istio/sidecar.go @@ -46,12 +46,12 @@ func NewSidecarController(stopCh <-chan struct{}, handler SidecarHandler, config ic, err := versioned.NewForConfig(config) if err != nil { - return nil, fmt.Errorf("failed to create destination rule controller k8s client: %v", err) + return nil, fmt.Errorf("failed to create sidecar controller k8s client: %v", err) } sidecarController.IstioClient = ic - sidecarController.informer = informers.NewDestinationRuleInformer(ic, k8sV1.NamespaceAll, resyncPeriod, cache.Indexers{}) + sidecarController.informer = informers.NewSidecarInformer(ic, k8sV1.NamespaceAll, resyncPeriod, cache.Indexers{}) admiral.NewController(stopCh, &sidecarController, sidecarController.informer) From 30b245cb75b952a736a420f6acae56456e35417e Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Fri, 27 Mar 2020 10:21:20 -0700 Subject: [PATCH 37/45] Handle dependency update event, optimize sidecar egress update (#88) Fixes https://github.com/istio-ecosystem/admiral/issues/87 Signed-off-by: Madeline --- admiral/pkg/clusters/serviceentry.go | 27 ++++++++++---------- admiral/pkg/clusters/serviceentry_test.go | 9 +++++-- admiral/pkg/clusters/types.go | 20 ++++++++++++--- admiral/pkg/controller/admiral/dependency.go | 7 +++++ admiral/pkg/controller/common/types.go | 21 +++++++-------- admiral/pkg/test/mock.go | 4 +++ 6 files changed, 58 insertions(+), 30 deletions(-) diff --git a/admiral/pkg/clusters/serviceentry.go b/admiral/pkg/clusters/serviceentry.go index cf7878e9..bd5ffdcb 100644 --- a/admiral/pkg/clusters/serviceentry.go +++ b/admiral/pkg/clusters/serviceentry.go @@ -175,27 +175,25 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem ep.Ports = oldPorts } } + } - for _, val := range dependents.Map() { - remoteRegistry.AdmiralCache.DependencyNamespaceCache.Put(val, serviceInstance.Namespace, localFqdn) - } + for _, val := range dependents.Map() { + remoteRegistry.AdmiralCache.DependencyNamespaceCache.Put(val, serviceInstance.Namespace, localFqdn) + } - if common.GetWorkloadSidecarUpdate() == "enabled" { - for _, sidecarEgress := range remoteRegistry.AdmiralCache.DependencyNamespaceCache.Get(sourceIdentity) { - modifySidecarForLocalClusterCommunication(serviceInstance.Namespace, sidecarEgress.Namespace, sidecarEgress.FQDN, rc) - } - } + if common.GetWorkloadSidecarUpdate() == "enabled" { + modifySidecarForLocalClusterCommunication(serviceInstance.Namespace, remoteRegistry.AdmiralCache.DependencyNamespaceCache.Get(sourceIdentity), rc) } } return serviceEntries } -func modifySidecarForLocalClusterCommunication(sidecarNamespace string, dependencyNamespace string, localFqdn string, rc *RemoteController) { +func modifySidecarForLocalClusterCommunication(sidecarNamespace string, sidecarEgressMap map[string]common.SidecarEgress, rc *RemoteController) { //get existing sidecar from the cluster sidecarConfig := rc.SidecarController - if sidecarConfig == nil { + if sidecarConfig == nil || sidecarEgressMap == nil { return } @@ -208,10 +206,11 @@ func modifySidecarForLocalClusterCommunication(sidecarNamespace string, dependen //copy and add our new local FQDN newSidecar := copySidecar(sidecar) - egressHost := dependencyNamespace + "/" + localFqdn - - if !util.Contains(newSidecar.Spec.Egress[0].Hosts, egressHost) { - newSidecar.Spec.Egress[0].Hosts = append(newSidecar.Spec.Egress[0].Hosts, egressHost) + for _, sidecarEgress := range sidecarEgressMap { + egressHost := sidecarEgress.Namespace + "/" + sidecarEgress.FQDN + if !util.Contains(newSidecar.Spec.Egress[0].Hosts, egressHost) { + newSidecar.Spec.Egress[0].Hosts = append(newSidecar.Spec.Egress[0].Hosts, egressHost) + } } newSidecarConfig := createSidecarSkeletion(newSidecar.Spec, common.GetWorkloadSidecarName(), sidecarNamespace) diff --git a/admiral/pkg/clusters/serviceentry_test.go b/admiral/pkg/clusters/serviceentry_test.go index e875bf60..bd8cc32e 100644 --- a/admiral/pkg/clusters/serviceentry_test.go +++ b/admiral/pkg/clusters/serviceentry_test.go @@ -295,7 +295,10 @@ func TestModifyNonExistingSidecarForLocalClusterCommunication(t *testing.T) { remoteController := &RemoteController{} remoteController.SidecarController = sidecarController - modifySidecarForLocalClusterCommunication("test-sidecar-namespace", "test-dependency-namespace", "test-local-fqdn", remoteController) + sidecarEgressMap := make(map[string]common.SidecarEgress) + sidecarEgressMap["test-dependency-namespace"] = common.SidecarEgress{Namespace: "test-dependency-namespace", FQDN: "test-local-fqdn"} + + modifySidecarForLocalClusterCommunication("test-sidecar-namespace", sidecarEgressMap, remoteController) sidecarObj, _ := sidecarController.IstioClient.NetworkingV1alpha3().Sidecars("test-sidecar-namespace").Get(common.GetWorkloadSidecarName(), v12.GetOptions{}) @@ -328,7 +331,9 @@ func TestModifyExistingSidecarForLocalClusterCommunication(t *testing.T) { if createdSidecar != nil { - modifySidecarForLocalClusterCommunication("test-sidecar-namespace", "test-dependency-namespace", "test-local-fqdn", remoteController) + sidecarEgressMap := make(map[string]common.SidecarEgress) + sidecarEgressMap["test-dependency-namespace"] = common.SidecarEgress{Namespace: "test-dependency-namespace", FQDN: "test-local-fqdn"} + modifySidecarForLocalClusterCommunication("test-sidecar-namespace", sidecarEgressMap, remoteController) updatedSidecar, error := sidecarController.IstioClient.NetworkingV1alpha3().Sidecars("test-sidecar-namespace").Get("default", v12.GetOptions{}) diff --git a/admiral/pkg/clusters/types.go b/admiral/pkg/clusters/types.go index 378b619e..3cc17ee0 100644 --- a/admiral/pkg/clusters/types.go +++ b/admiral/pkg/clusters/types.go @@ -174,18 +174,30 @@ type ServiceHandler struct { func (dh *DependencyHandler) Added(obj *v1.Dependency) { - log.Infof(LogFormat, "Event", "dependency-record", obj.Name, "", "Received=true namespace="+obj.Namespace) + log.Infof(LogFormat, "Add", "dependency-record", obj.Name, "", "Received=true namespace="+obj.Namespace) + HandleDependencyRecord(obj, dh.RemoteRegistry) + +} + +func (dh *DependencyHandler) Updated(obj *v1.Dependency) { + + log.Infof(LogFormat, "Update", "dependency-record", obj.Name, "", "Received=true namespace="+obj.Namespace) + + HandleDependencyRecord(obj, dh.RemoteRegistry) + +} + +func HandleDependencyRecord(obj *v1.Dependency, remoteRegitry *RemoteRegistry) { sourceIdentity := obj.Spec.Source if len(sourceIdentity) == 0 { log.Infof(LogFormat, "Event", "dependency-record", obj.Name, "", "No identity found namespace="+obj.Namespace) } - updateIdentityDependencyCache(sourceIdentity, dh.RemoteRegistry.AdmiralCache.IdentityDependencyCache, obj) - - handleDependencyRecord(sourceIdentity, dh.RemoteRegistry, dh.RemoteRegistry.remoteControllers, obj) + updateIdentityDependencyCache(sourceIdentity, remoteRegitry.AdmiralCache.IdentityDependencyCache, obj) + handleDependencyRecord(sourceIdentity, remoteRegitry, remoteRegitry.remoteControllers, obj) } func (dh *DependencyHandler) Deleted(obj *v1.Dependency) { diff --git a/admiral/pkg/controller/admiral/dependency.go b/admiral/pkg/controller/admiral/dependency.go index 64c01a28..2dd1503a 100644 --- a/admiral/pkg/controller/admiral/dependency.go +++ b/admiral/pkg/controller/admiral/dependency.go @@ -16,6 +16,7 @@ import ( // Handler interface contains the methods that are required type DepHandler interface { Added(obj *v1.Dependency) + Updated(obj *v1.Dependency) Deleted(obj *v1.Dependency) } @@ -107,6 +108,12 @@ func (d *DependencyController) Added(ojb interface{}) { d.DepHandler.Added(dep) } +func (d *DependencyController) Updated(ojb interface{}) { + dep := ojb.(*v1.Dependency) + d.Cache.Put(dep) + d.DepHandler.Added(dep) +} + func (d *DependencyController) Deleted(ojb interface{}) { dep := ojb.(*v1.Dependency) d.DepHandler.Deleted(dep) diff --git a/admiral/pkg/controller/common/types.go b/admiral/pkg/controller/common/types.go index a81f2404..2c375ec2 100644 --- a/admiral/pkg/controller/common/types.go +++ b/admiral/pkg/controller/common/types.go @@ -21,8 +21,9 @@ type SidecarEgress struct { FQDN string } +//maintains a map from workload identity -> map[namespace]SidecarEgress type SidecarEgressMap struct { - cache map[string][]SidecarEgress + cache map[string]map[string]SidecarEgress mutex *sync.Mutex } @@ -64,7 +65,7 @@ type LabelSet struct { func NewSidecarEgressMap() *SidecarEgressMap { n := new(SidecarEgressMap) - n.cache = make(map[string][]SidecarEgress) + n.cache = make(map[string]map[string]SidecarEgress) n.mutex = &sync.Mutex{} return n } @@ -113,8 +114,8 @@ func (s *MapOfMaps) Put(pkey string, key string, value string) { var mapVal = s.cache[pkey] if mapVal == nil { mapVal = NewMap() - mapVal.Put(key, value) } + mapVal.Put(key, value) s.cache[pkey] = mapVal } @@ -132,18 +133,18 @@ func (s *MapOfMaps) Map() map[string]*Map { return s.cache } -func (s *SidecarEgressMap) Put(asset string, namespace string, fqdn string) { +func (s *SidecarEgressMap) Put(identity string, namespace string, fqdn string) { defer s.mutex.Unlock() s.mutex.Lock() - var mapVal = s.cache[asset] + var mapVal = s.cache[identity] if mapVal == nil { - mapVal = make([]SidecarEgress, 0) + mapVal = make(map[string]SidecarEgress, 0) } - mapVal = append(mapVal, SidecarEgress{Namespace: namespace, FQDN: fqdn}) - s.cache[asset] = mapVal + mapVal[namespace] = SidecarEgress{Namespace: namespace, FQDN: fqdn} + s.cache[identity] = mapVal } -func (s *SidecarEgressMap) Get(key string) []SidecarEgress { +func (s *SidecarEgressMap) Get(key string) map[string]SidecarEgress { return s.cache[key] } @@ -153,6 +154,6 @@ func (s *SidecarEgressMap) Delete(key string) { delete(s.cache, key) } -func (s *SidecarEgressMap) Map() map[string][]SidecarEgress { +func (s *SidecarEgressMap) Map() map[string]map[string]SidecarEgress { return s.cache } diff --git a/admiral/pkg/test/mock.go b/admiral/pkg/test/mock.go index 20bc296f..89512186 100644 --- a/admiral/pkg/test/mock.go +++ b/admiral/pkg/test/mock.go @@ -75,6 +75,10 @@ func (m *MockDependencyHandler) Added(obj *v1.Dependency) { } +func (m *MockDependencyHandler) Updated(obj *v1.Dependency) { + +} + func (m *MockDependencyHandler) Deleted(obj *v1.Dependency) { } From a5f69b1fd6e154e6ecdd003f8832656197718a92 Mon Sep 17 00:00:00 2001 From: josephpeacock <51184065+josephpeacock@users.noreply.github.com> Date: Wed, 1 Apr 2020 10:43:41 -0700 Subject: [PATCH 38/45] Adding more unit tests (#89) * Added tests for destination rule creation Signed-off-by: Joe Peacock * Uncommented/fixed a bunch of SE tests Signed-off-by: Joe Peacock * Uncommented/fixed more se tests Signed-off-by: Joe Peacock Co-authored-by: Joe Peacock Signed-off-by: Madeline --- admiral/pkg/clusters/handler.go | 14 +- admiral/pkg/clusters/handler_test.go | 299 +++++++- admiral/pkg/clusters/serviceentry_test.go | 704 ++++++++++-------- .../pkg/controller/istio/destinationrule.go | 2 +- admiral/pkg/controller/istio/serviceentry.go | 2 +- .../pkg/controller/istio/virtualservice.go | 2 +- go.sum | 11 + 7 files changed, 691 insertions(+), 343 deletions(-) diff --git a/admiral/pkg/clusters/handler.go b/admiral/pkg/clusters/handler.go index 7248becf..8d550005 100644 --- a/admiral/pkg/clusters/handler.go +++ b/admiral/pkg/clusters/handler.go @@ -409,7 +409,7 @@ func createDestinationRuleForLocal(remoteController *RemoteController, localDrNa } } -func handleVirtualServiceEvent(obj *v1alpha3.VirtualService, vh *VirtualServiceHandler, event common.Event, resourceType common.ResourceType) { +func handleVirtualServiceEvent(obj *v1alpha3.VirtualService, vh *VirtualServiceHandler, event common.Event, resourceType common.ResourceType) error { log.Infof(LogFormat, "Event", resourceType, obj.Name, vh.ClusterID, "Received event") @@ -423,19 +423,19 @@ func handleVirtualServiceEvent(obj *v1alpha3.VirtualService, vh *VirtualServiceH if obj.Namespace == syncNamespace { log.Infof(LogFormat, "Event", resourceType, obj.Name, clusterId, "Skipping the namespace: "+obj.Namespace) - return + return nil } if len(virtualService.Hosts) > 1 { log.Errorf(LogFormat, "Event", resourceType, obj.Name, clusterId, "Skipping as multiple hosts not supported for virtual service namespace="+obj.Namespace) - return + return nil } dependentClusters := r.AdmiralCache.CnameDependentClusterCache.Get(virtualService.Hosts[0]) if dependentClusters == nil { log.Infof(LogFormat, "Event", resourceType, obj.Name, clusterId, "No dependent clusters found") - return + return nil } log.Infof(LogFormat, "Event", "VirtualService", obj.Name, clusterId, "Processing") @@ -448,7 +448,10 @@ func handleVirtualServiceEvent(obj *v1alpha3.VirtualService, vh *VirtualServiceH if event == common.Delete { log.Infof(LogFormat, "Delete", "VirtualService", obj.Name, clusterId, "Success") - rc.VirtualServiceController.IstioClient.NetworkingV1alpha3().VirtualServices(syncNamespace).Delete(obj.Name, &v12.DeleteOptions{}) + err := rc.VirtualServiceController.IstioClient.NetworkingV1alpha3().VirtualServices(syncNamespace).Delete(obj.Name, &v12.DeleteOptions{}) + if err != nil { + return err + } } else { @@ -474,6 +477,7 @@ func handleVirtualServiceEvent(obj *v1alpha3.VirtualService, vh *VirtualServiceH } } + return nil } func addUpdateVirtualService(obj *v1alpha3.VirtualService, exist *v1alpha3.VirtualService, namespace string, rc *RemoteController) { diff --git a/admiral/pkg/clusters/handler_test.go b/admiral/pkg/clusters/handler_test.go index 12fece7d..bbeba7c4 100644 --- a/admiral/pkg/clusters/handler_test.go +++ b/admiral/pkg/clusters/handler_test.go @@ -1,35 +1,46 @@ package clusters import ( + "github.com/gogo/protobuf/types" + "github.com/google/go-cmp/cmp" + "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/model" + "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/istio" + "istio.io/api/networking/v1alpha3" + v1alpha32 "istio.io/client-go/pkg/apis/networking/v1alpha3" + v12 "k8s.io/apimachinery/pkg/apis/meta/v1" "testing" + + istiofake "istio.io/client-go/pkg/clientset/versioned/fake" ) func TestIgnoreIstioResource(t *testing.T) { //Struct of test case info. Name is required. testCases := []struct { - name string - exportTo []string + name string + exportTo []string expectedResult bool }{ { - name: "Should return false when exportTo is not present", - exportTo: nil, + name: "Should return false when exportTo is not present", + exportTo: nil, expectedResult: false, }, { - name: "Should return false when its exported to *", - exportTo: []string {"*"}, + name: "Should return false when its exported to *", + exportTo: []string{"*"}, expectedResult: false, }, { - name: "Should return true when its exported to .", - exportTo: []string {"."}, + name: "Should return true when its exported to .", + exportTo: []string{"."}, expectedResult: true, }, { - name: "Should return true when its exported to a handful of namespaces", - exportTo: []string {"namespace1", "namespace2"}, + name: "Should return true when its exported to a handful of namespaces", + exportTo: []string{"namespace1", "namespace2"}, expectedResult: true, }, } @@ -41,7 +52,273 @@ func TestIgnoreIstioResource(t *testing.T) { if result == c.expectedResult { //perfect } else { - t.Errorf("Failed. Got %v, expected %v",result, c.expectedResult) + t.Errorf("Failed. Got %v, expected %v", result, c.expectedResult) + } + }) + } +} + +func TestGetDestinationRule(t *testing.T) { + //Do setup here + mTLS := &v1alpha3.TrafficPolicy{Tls: &v1alpha3.TLSSettings{Mode: v1alpha3.TLSSettings_ISTIO_MUTUAL}} + + noGtpDr := v1alpha3.DestinationRule{ + Host: "qa.myservice.global", + TrafficPolicy: mTLS, + } + + basicGtpDr := v1alpha3.DestinationRule{ + Host: "qa.myservice.global", + TrafficPolicy: &v1alpha3.TrafficPolicy{ + Tls: &v1alpha3.TLSSettings{Mode: v1alpha3.TLSSettings_ISTIO_MUTUAL}, + LoadBalancer: &v1alpha3.LoadBalancerSettings{ + LbPolicy: &v1alpha3.LoadBalancerSettings_Simple{Simple: v1alpha3.LoadBalancerSettings_ROUND_ROBIN}, + LocalityLbSetting: &v1alpha3.LocalityLoadBalancerSetting{}, + }, + OutlierDetection: &v1alpha3.OutlierDetection{ + BaseEjectionTime: &types.Duration{Seconds: 120}, + ConsecutiveErrors: 10, + Interval: &types.Duration{Seconds: 60}, + }, + }, + } + + failoverGtpDr := v1alpha3.DestinationRule{ + Host: "qa.myservice.global", + TrafficPolicy: &v1alpha3.TrafficPolicy{ + Tls: &v1alpha3.TLSSettings{Mode: v1alpha3.TLSSettings_ISTIO_MUTUAL}, + LoadBalancer: &v1alpha3.LoadBalancerSettings{ + LbPolicy: &v1alpha3.LoadBalancerSettings_Simple{Simple: v1alpha3.LoadBalancerSettings_ROUND_ROBIN}, + LocalityLbSetting: &v1alpha3.LocalityLoadBalancerSetting{ + Distribute: []*v1alpha3.LocalityLoadBalancerSetting_Distribute{ + { + From: "uswest2/*", + To: map[string]uint32{"us-west-2": 100}, + }, + }, + }, + }, + OutlierDetection: &v1alpha3.OutlierDetection{ + BaseEjectionTime: &types.Duration{Seconds: 120}, + ConsecutiveErrors: 10, + Interval: &types.Duration{Seconds: 60}, + }, + }, + } + + topologyGTPBody := model.GlobalTrafficPolicy{ + Policy: []*model.TrafficPolicy{ + { + LbType: model.TrafficPolicy_TOPOLOGY, + Target: []*model.TrafficGroup{ + { + Region: "us-west-2", + Weight: 100, + }, + }, + }, + }, + } + + topologyGTP := v1.GlobalTrafficPolicy{ + Spec: topologyGTPBody, + } + topologyGTP.Name = "myGTP" + topologyGTP.Namespace = "myNS" + + failoverGTPBody := model.GlobalTrafficPolicy{ + Policy: []*model.TrafficPolicy{ + { + LbType: model.TrafficPolicy_FAILOVER, + Target: []*model.TrafficGroup{ + { + Region: "us-west-2", + Weight: 100, + }, + }, + }, + }, + } + + failoverGTP := v1.GlobalTrafficPolicy{ + Spec: failoverGTPBody, + } + failoverGTP.Name = "myGTP" + failoverGTP.Namespace = "myNS" + + //Struct of test case info. Name is required. + testCases := []struct { + name string + host string + locality string + gtp *v1.GlobalTrafficPolicy + destinationRule *v1alpha3.DestinationRule + }{ + { + name: "Should handle a nil GTP", + host: "qa.myservice.global", + locality: "uswest2", + gtp: nil, + destinationRule: &noGtpDr, + }, + { + name: "Should handle a topology GTP", + host: "qa.myservice.global", + locality: "uswest2", + gtp: &topologyGTP, + destinationRule: &basicGtpDr, + }, + { + name: "Should handle a failover GTP", + host: "qa.myservice.global", + locality: "uswest2", + gtp: &failoverGTP, + destinationRule: &failoverGtpDr, + }, + } + + //Run the test for every provided case + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + result := getDestinationRule(c.host, c.locality, c.gtp) + if !cmp.Equal(result, c.destinationRule) { + t.Fatalf("DestinationRule Mismatch. Diff: %v", cmp.Diff(result, c.destinationRule)) + } + }) + } +} + + +func TestHandleVirtualServiceEvent(t *testing.T) { + //Do setup here + syncNs := v1alpha32.VirtualService{} + syncNs.Namespace = "ns" + + tooManyHosts := v1alpha32.VirtualService{ + Spec: v1alpha3.VirtualService{ + Hosts: []string{"qa.blah.global", "e2e.blah.global"}, + }, + } + tooManyHosts.Namespace = "other-ns" + + happyPath := v1alpha32.VirtualService{ + Spec: v1alpha3.VirtualService{ + Hosts: []string{"e2e.blah.global"}, + }, + } + happyPath.Namespace = "other-ns" + happyPath.Name = "vs-name" + + cnameCache := common.NewMapOfMaps() + noDependencClustersHandler := VirtualServiceHandler{ + RemoteRegistry: &RemoteRegistry{ + remoteControllers: map[string]*RemoteController{}, + AdmiralCache: &AdmiralCache{ + CnameDependentClusterCache: cnameCache, + }, + }, + } + + fakeIstioClient := istiofake.NewSimpleClientset() + goodCnameCache := common.NewMapOfMaps() + goodCnameCache.Put("e2e.blah.global", "cluster.k8s.global", "cluster.k8s.global") + handlerEmptyClient := VirtualServiceHandler{ + RemoteRegistry: &RemoteRegistry{ + remoteControllers: map[string]*RemoteController{ + "cluster.k8s.global": &RemoteController{ + VirtualServiceController: &istio.VirtualServiceController{ + IstioClient: fakeIstioClient, + }, + }, + }, + AdmiralCache: &AdmiralCache{ + CnameDependentClusterCache: goodCnameCache, + }, + }, + } + + fullFakeIstioClient := istiofake.NewSimpleClientset() + fullFakeIstioClient.NetworkingV1alpha3().VirtualServices("ns").Create(&v1alpha32.VirtualService{ + ObjectMeta: v12.ObjectMeta{ + Name: "vs-name", + }, + Spec: v1alpha3.VirtualService{ + Hosts: []string{"e2e.blah.global"}, + }, + }) + handlerFullClient := VirtualServiceHandler{ + RemoteRegistry: &RemoteRegistry{ + remoteControllers: map[string]*RemoteController{ + "cluster.k8s.global": &RemoteController{ + VirtualServiceController: &istio.VirtualServiceController{ + IstioClient: fullFakeIstioClient, + }, + }, + }, + AdmiralCache: &AdmiralCache{ + CnameDependentClusterCache: goodCnameCache, + }, + }, + } + + //Struct of test case info. Name is required. + testCases := []struct { + name string + vs *v1alpha32.VirtualService + handler *VirtualServiceHandler + expectedError error + event common.Event + }{ + { + name: "Virtual Service in sync namespace", + vs: &syncNs, + expectedError: nil, + handler: &noDependencClustersHandler, + event: 0, + }, + { + name: "Virtual Service with multiple hosts", + vs: &tooManyHosts, + expectedError: nil, + handler: &noDependencClustersHandler, + event: 0, + }, + { + name: "No dependent clusters", + vs: &happyPath, + expectedError: nil, + handler: &noDependencClustersHandler, + event: 0, + }, + { + name: "New Virtual Service", + vs: &happyPath, + expectedError: nil, + handler: &handlerEmptyClient, + event: 0, + }, + { + name: "Existing Virtual Service", + vs: &happyPath, + expectedError: nil, + handler: &handlerFullClient, + event: 1, + }, + { + name: "Deleted Virtual Service", + vs: &happyPath, + expectedError: nil, + handler: &handlerFullClient, + event: 2, + }, + } + + //Run the test for every provided case + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + err := handleVirtualServiceEvent(c.vs, c.handler, c.event, common.VirtualService) + if err != c.expectedError { + t.Fatalf("Error mismatch, expected %v but got %v", c.expectedError, err) } }) } diff --git a/admiral/pkg/clusters/serviceentry_test.go b/admiral/pkg/clusters/serviceentry_test.go index bd8cc32e..46cac1a6 100644 --- a/admiral/pkg/clusters/serviceentry_test.go +++ b/admiral/pkg/clusters/serviceentry_test.go @@ -1,281 +1,319 @@ package clusters import ( + "context" + "errors" "github.com/google/go-cmp/cmp" + v13 "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/istio" + "github.com/istio-ecosystem/admiral/admiral/pkg/test" "gopkg.in/yaml.v2" istionetworkingv1alpha3 "istio.io/api/networking/v1alpha3" "istio.io/client-go/pkg/apis/networking/v1alpha3" istiofake "istio.io/client-go/pkg/clientset/versioned/fake" + v14 "k8s.io/api/apps/v1" "k8s.io/api/core/v1" v12 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/rest" + "reflect" + "strconv" + "sync" "testing" + "time" ) -//func TestCreateSeWithDrLabels(t *testing.T) { -// -// se := networking.ServiceEntry{ -// Hosts: []string{"test.com"}, -// Endpoints: []*networking.ServiceEntry_Endpoint{ -// {Address: "127.0.0.1", Ports: map[string]uint32{"https": 80}, Labels: map[string]string{}, Network: "mesh1", Locality: "us-west", Weight: 100}, -// }, -// } -// -// des := networking.DestinationRule{ -// Host: "test.com", -// Subsets: []*networking.Subset{ -// {Name: "subset1", Labels: map[string]string{"foo": "bar"}, TrafficPolicy: nil}, -// }, -// } -// -// cacheWithNoEntry := ServiceEntryAddressStore{ -// EntryAddresses: map[string]string{"test-se": "1.2.3.4"}, -// Addresses: []string{"1.2.3.4"}, -// } -// -// emptyCacheController := test.FakeConfigMapController{ -// GetError: nil, -// PutError: nil, -// ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithNoEntry, "123"), -// } -// -// -// res := createSeWithDrLabels(nil, false, "", "test-se", &se, &des, &cacheWithNoEntry, &emptyCacheController) -// -// if res == nil { -// t.Fail() -// } -// -// newSe := res["test-se"] -// -// value := newSe.Endpoints[0].Labels["foo"] -// -// if value != "bar" { -// t.Fail() -// } -// -// if newSe.Addresses[0] != "1.2.3.4" { -// t.Errorf("Address set incorrectly from cache, expected 1.2.3.4, got %v", newSe.Addresses[0]) -// } -//} -// -//func TestAddServiceEntriesWithDr(t *testing.T) { -// admiralCache := AdmiralCache{} -// -// cnameIdentityCache := sync.Map{} -// cnameIdentityCache.Store("dev.bar.global", "bar") -// admiralCache.CnameIdentityCache = &cnameIdentityCache -// -// se := networking.ServiceEntry{ -// Hosts: []string{"dev.bar.global"}, -// Endpoints: []*networking.ServiceEntry_Endpoint{ -// {Address: "127.0.0.1", Ports: map[string]uint32{"https": 80}, Labels: map[string]string{}, Network: "mesh1", Locality: "us-west", Weight: 100}, -// }, -// } -// -// rc, _ := createMockRemoteController(func(i interface{}) { -// //res := i.(istio.Config) -// //se, ok := res.Spec.(*networking.ServiceEntry) -// //if ok { -// // if se.Hosts[0] != "dev.bar.global" { -// // t.Errorf("Host mismatch. Expected dev.bar.global, got %v", se.Hosts[0]) -// // } -// //} -// }) -// -// seConfig, _ := createIstioConfig(istio.ServiceEntryProto, &se, "se1", "admiral-sync") -// _, err := rc.IstioConfigStore.Create(*seConfig) -// if err != nil { -// t.Errorf("%v", err) -// -// } -// -// AddServiceEntriesWithDr(&admiralCache, map[string]string{"cl1":"cl1"}, map[string]*RemoteController{"cl1":rc}, map[string]*networking.ServiceEntry{"se1": &se}, "admiral-sync") -// } -// -//func TestCreateServiceEntryForNewServiceOrPod(t *testing.T) { -// -// p := AdmiralParams{ -// KubeconfigPath: "testdata/fake.config", -// } -// rr, _ := InitAdmiral(context.Background(), p) -// -// rc, _ := createMockRemoteController(func(i interface{}) { -// res := i.(istio.Config) -// se, ok := res.Spec.(*networking.ServiceEntry) -// if ok { -// if se.Hosts[0] != "dev.bar.global" { -// t.Fail() -// } -// } -// }) -// -// rr.remoteControllers["test.cluster"] = rc -// createServiceEntryForNewServiceOrPod("test", "bar", rr) -// -//} -// -//func TestGetLocalAddressForSe(t *testing.T) { -// t.Parallel() -// cacheWithEntry := ServiceEntryAddressStore{ -// EntryAddresses: map[string]string{"e2e.a.mesh": common.LocalAddressPrefix + ".10.1"}, -// Addresses: []string{common.LocalAddressPrefix + ".10.1"}, -// } -// cacheWithNoEntry := ServiceEntryAddressStore{ -// EntryAddresses: map[string]string{}, -// Addresses: []string{}, -// } -// cacheWith255Entries := ServiceEntryAddressStore{ -// EntryAddresses: map[string]string{}, -// Addresses: []string{}, -// } -// -// for i := 1; i <= 255; i++ { -// address := common.LocalAddressPrefix + ".10." + strconv.Itoa(i) -// cacheWith255Entries.EntryAddresses[strconv.Itoa(i) + ".mesh"] = address -// cacheWith255Entries.Addresses = append(cacheWith255Entries.Addresses, address) -// } -// -// emptyCacheController := test.FakeConfigMapController{ -// GetError: nil, -// PutError: nil, -// ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithNoEntry, "123"), -// } -// -// cacheController := test.FakeConfigMapController{ -// GetError: nil, -// PutError: nil, -// ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithEntry, "123"), -// } -// -// cacheControllerWith255Entries := test.FakeConfigMapController{ -// GetError: nil, -// PutError: nil, -// ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWith255Entries, "123"), -// } -// -// cacheControllerGetError := test.FakeConfigMapController{ -// GetError: errors.New("BAD THINGS HAPPENED"), -// PutError: nil, -// ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithEntry, "123"), -// } -// -// cacheControllerPutError := test.FakeConfigMapController{ -// PutError: errors.New("BAD THINGS HAPPENED"), -// GetError: nil, -// ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithEntry, "123"), -// } -// -// -// testCases := []struct { -// name string -// seName string -// seAddressCache ServiceEntryAddressStore -// wantAddess string -// cacheController admiral.ConfigMapControllerInterface -// expectedCacheUpdate bool -// wantedError error -// }{ -// { -// name: "should return new available address", -// seName: "e2e.a.mesh", -// seAddressCache: cacheWithNoEntry, -// wantAddess: common.LocalAddressPrefix + ".10.1", -// cacheController: &emptyCacheController, -// expectedCacheUpdate: true, -// wantedError: nil, -// }, -// { -// name: "should return address from map", -// seName: "e2e.a.mesh", -// seAddressCache: cacheWithEntry, -// wantAddess: common.LocalAddressPrefix + ".10.1", -// cacheController: &cacheController, -// expectedCacheUpdate: false, -// wantedError: nil, -// }, -// { -// name: "should return new available address", -// seName: "e2e.b.mesh", -// seAddressCache: cacheWithEntry, -// wantAddess: common.LocalAddressPrefix + ".10.2", -// cacheController: &cacheController, -// expectedCacheUpdate: true, -// wantedError: nil, -// }, -// { -// name: "should return new available address in higher subnet", -// seName: "e2e.a.mesh", -// seAddressCache: cacheWith255Entries, -// wantAddess: common.LocalAddressPrefix + ".11.1", -// cacheController: &cacheControllerWith255Entries, -// expectedCacheUpdate: true, -// wantedError: nil, -// }, -// { -// name: "should gracefully propagate get error", -// seName: "e2e.a.mesh", -// seAddressCache: cacheWith255Entries, -// wantAddess: "", -// cacheController: &cacheControllerGetError, -// expectedCacheUpdate: true, -// wantedError: errors.New("BAD THINGS HAPPENED"), -// }, -// { -// name: "Should not return address on put error", -// seName: "e2e.abcdefghijklmnop.mesh", -// seAddressCache: cacheWith255Entries, -// wantAddess: "", -// cacheController: &cacheControllerPutError, -// expectedCacheUpdate: true, -// wantedError: errors.New("BAD THINGS HAPPENED"), -// }, -// } -// -// for _, c := range testCases { -// t.Run(c.name, func(t *testing.T) { -// seAddress, needsCacheUpdate, err := GetLocalAddressForSe(c.seName, &c.seAddressCache, c.cacheController) -// if c.wantAddess != "" { -// if !reflect.DeepEqual(seAddress, c.wantAddess) { -// t.Errorf("Wanted se address: %s, got: %s", c.wantAddess, seAddress) -// } -// if err==nil && c.wantedError==nil { -// //we're fine -// } else if err.Error() != c.wantedError.Error() { -// t.Errorf("Error mismatch. Expected %v but got %v", c.wantedError, err) -// } -// if needsCacheUpdate != c.expectedCacheUpdate { -// t.Errorf("Expected %v, got %v for needs cache update", c.expectedCacheUpdate, needsCacheUpdate) -// } -// } else { -// if seAddress != "" { -// t.Errorf("Unexpectedly found address: %s", seAddress) -// } -// } -// }) -// } -// -//} -// -//func TestMakeRemoteEndpointForServiceEntry(t *testing.T) { -// address := "1.2.3.4" -// locality := "us-west-2" -// portName := "port" -// -// endpoint := makeRemoteEndpointForServiceEntry(address, locality, portName) -// -// if endpoint.Address != address { -// t.Errorf("Address mismatch. Got: %v, expected: %v", endpoint.Address, address) -// } -// if endpoint.Locality != locality { -// t.Errorf("Locality mismatch. Got: %v, expected: %v", endpoint.Locality, locality) -// } -// if endpoint.Ports[portName] != 15443 { -// t.Errorf("Incorrect port found") -// } -//} -// +func TestCreateSeWithDrLabels(t *testing.T) { + + se := istionetworkingv1alpha3.ServiceEntry{ + Hosts: []string{"test.com"}, + Endpoints: []*istionetworkingv1alpha3.ServiceEntry_Endpoint{ + {Address: "127.0.0.1", Ports: map[string]uint32{"https": 80}, Labels: map[string]string{}, Network: "mesh1", Locality: "us-west", Weight: 100}, + }, + } + + des := istionetworkingv1alpha3.DestinationRule{ + Host: "test.com", + Subsets: []*istionetworkingv1alpha3.Subset{ + {Name: "subset1", Labels: map[string]string{"foo": "bar"}, TrafficPolicy: nil}, + }, + } + + cacheWithNoEntry := ServiceEntryAddressStore{ + EntryAddresses: map[string]string{"test-se": "1.2.3.4"}, + Addresses: []string{"1.2.3.4"}, + } + + emptyCacheController := test.FakeConfigMapController{ + GetError: nil, + PutError: nil, + ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithNoEntry, "123"), + } + + + res := createSeWithDrLabels(nil, false, "", "test-se", &se, &des, &cacheWithNoEntry, &emptyCacheController) + + if res == nil { + t.Fail() + } + + newSe := res["test-se"] + + value := newSe.Endpoints[0].Labels["foo"] + + if value != "bar" { + t.Fail() + } + + if newSe.Addresses[0] != "1.2.3.4" { + t.Errorf("Address set incorrectly from cache, expected 1.2.3.4, got %v", newSe.Addresses[0]) + } +} + +func TestAddServiceEntriesWithDr(t *testing.T) { + admiralCache := AdmiralCache{} + + cnameIdentityCache := sync.Map{} + cnameIdentityCache.Store("dev.bar.global", "bar") + admiralCache.CnameIdentityCache = &cnameIdentityCache + + gtpCache := &globalTrafficCache{} + gtpCache.identityCache = make(map[string]*v13.GlobalTrafficPolicy) + gtpCache.dependencyCache = make(map[string]*v14.Deployment) + gtpCache.mutex = &sync.Mutex{} + admiralCache.GlobalTrafficCache = gtpCache + + se := istionetworkingv1alpha3.ServiceEntry{ + Hosts: []string{"dev.bar.global"}, + Endpoints: []*istionetworkingv1alpha3.ServiceEntry_Endpoint{ + {Address: "127.0.0.1", Ports: map[string]uint32{"https": 80}, Labels: map[string]string{}, Network: "mesh1", Locality: "us-west", Weight: 100}, + }, + } + + seConfig := v1alpha3.ServiceEntry{ + Spec: se, + } + seConfig.Name = "se1" + seConfig.Namespace = "admiral-sync" + + fakeIstioClient := istiofake.NewSimpleClientset() + fakeIstioClient.NetworkingV1alpha3().ServiceEntries("admiral-sync").Create(&seConfig) + rc := &RemoteController{ + ServiceEntryController: &istio.ServiceEntryController{ + IstioClient: fakeIstioClient, + }, + DestinationRuleController: &istio.DestinationRuleController{ + IstioClient: fakeIstioClient, + }, + NodeController: &admiral.NodeController{ + Locality: &admiral.Locality{ + Region: "us-west-2", + }, + }, + } + + AddServiceEntriesWithDr(&admiralCache, map[string]string{"cl1":"cl1"}, map[string]*RemoteController{"cl1":rc}, map[string]*istionetworkingv1alpha3.ServiceEntry{"se1": &se}) + } + +func TestCreateServiceEntryForNewServiceOrPod(t *testing.T) { + + p := common.AdmiralParams{ + KubeconfigPath: "testdata/fake.config", + } + rr, _ := InitAdmiral(context.Background(), p) + + config := rest.Config{ + Host: "localhost", + } + + d, e := admiral.NewDeploymentController(make(chan struct{}), &test.MockDeploymentHandler{}, &config, time.Second*time.Duration(300)) + + if e != nil { + t.Fail() + } + + fakeIstioClient := istiofake.NewSimpleClientset() + rc := &RemoteController{ + ServiceEntryController: &istio.ServiceEntryController{ + IstioClient: fakeIstioClient, + }, + DestinationRuleController: &istio.DestinationRuleController{ + IstioClient: fakeIstioClient, + }, + NodeController: &admiral.NodeController{ + Locality: &admiral.Locality{ + Region: "us-west-2", + }, + }, + DeploymentController: d, + } + + rr.remoteControllers["test.cluster"] = rc + createServiceEntryForNewServiceOrPod("test", "bar", rr) + +} + +func TestGetLocalAddressForSe(t *testing.T) { + t.Parallel() + cacheWithEntry := ServiceEntryAddressStore{ + EntryAddresses: map[string]string{"e2e.a.mesh": common.LocalAddressPrefix + ".10.1"}, + Addresses: []string{common.LocalAddressPrefix + ".10.1"}, + } + cacheWithNoEntry := ServiceEntryAddressStore{ + EntryAddresses: map[string]string{}, + Addresses: []string{}, + } + cacheWith255Entries := ServiceEntryAddressStore{ + EntryAddresses: map[string]string{}, + Addresses: []string{}, + } + + for i := 1; i <= 255; i++ { + address := common.LocalAddressPrefix + ".10." + strconv.Itoa(i) + cacheWith255Entries.EntryAddresses[strconv.Itoa(i) + ".mesh"] = address + cacheWith255Entries.Addresses = append(cacheWith255Entries.Addresses, address) + } + + emptyCacheController := test.FakeConfigMapController{ + GetError: nil, + PutError: nil, + ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithNoEntry, "123"), + } + + cacheController := test.FakeConfigMapController{ + GetError: nil, + PutError: nil, + ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithEntry, "123"), + } + + cacheControllerWith255Entries := test.FakeConfigMapController{ + GetError: nil, + PutError: nil, + ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWith255Entries, "123"), + } + + cacheControllerGetError := test.FakeConfigMapController{ + GetError: errors.New("BAD THINGS HAPPENED"), + PutError: nil, + ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithEntry, "123"), + } + + cacheControllerPutError := test.FakeConfigMapController{ + PutError: errors.New("BAD THINGS HAPPENED"), + GetError: nil, + ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithEntry, "123"), + } + + + testCases := []struct { + name string + seName string + seAddressCache ServiceEntryAddressStore + wantAddess string + cacheController admiral.ConfigMapControllerInterface + expectedCacheUpdate bool + wantedError error + }{ + { + name: "should return new available address", + seName: "e2e.a.mesh", + seAddressCache: cacheWithNoEntry, + wantAddess: common.LocalAddressPrefix + ".10.1", + cacheController: &emptyCacheController, + expectedCacheUpdate: true, + wantedError: nil, + }, + { + name: "should return address from map", + seName: "e2e.a.mesh", + seAddressCache: cacheWithEntry, + wantAddess: common.LocalAddressPrefix + ".10.1", + cacheController: &cacheController, + expectedCacheUpdate: false, + wantedError: nil, + }, + { + name: "should return new available address", + seName: "e2e.b.mesh", + seAddressCache: cacheWithEntry, + wantAddess: common.LocalAddressPrefix + ".10.2", + cacheController: &cacheController, + expectedCacheUpdate: true, + wantedError: nil, + }, + { + name: "should return new available address in higher subnet", + seName: "e2e.a.mesh", + seAddressCache: cacheWith255Entries, + wantAddess: common.LocalAddressPrefix + ".11.1", + cacheController: &cacheControllerWith255Entries, + expectedCacheUpdate: true, + wantedError: nil, + }, + { + name: "should gracefully propagate get error", + seName: "e2e.a.mesh", + seAddressCache: cacheWith255Entries, + wantAddess: "", + cacheController: &cacheControllerGetError, + expectedCacheUpdate: true, + wantedError: errors.New("BAD THINGS HAPPENED"), + }, + { + name: "Should not return address on put error", + seName: "e2e.abcdefghijklmnop.mesh", + seAddressCache: cacheWith255Entries, + wantAddess: "", + cacheController: &cacheControllerPutError, + expectedCacheUpdate: true, + wantedError: errors.New("BAD THINGS HAPPENED"), + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + seAddress, needsCacheUpdate, err := GetLocalAddressForSe(c.seName, &c.seAddressCache, c.cacheController) + if c.wantAddess != "" { + if !reflect.DeepEqual(seAddress, c.wantAddess) { + t.Errorf("Wanted se address: %s, got: %s", c.wantAddess, seAddress) + } + if err==nil && c.wantedError==nil { + //we're fine + } else if err.Error() != c.wantedError.Error() { + t.Errorf("Error mismatch. Expected %v but got %v", c.wantedError, err) + } + if needsCacheUpdate != c.expectedCacheUpdate { + t.Errorf("Expected %v, got %v for needs cache update", c.expectedCacheUpdate, needsCacheUpdate) + } + } else { + if seAddress != "" { + t.Errorf("Unexpectedly found address: %s", seAddress) + } + } + }) + } + +} + +func TestMakeRemoteEndpointForServiceEntry(t *testing.T) { + address := "1.2.3.4" + locality := "us-west-2" + portName := "port" + + endpoint := makeRemoteEndpointForServiceEntry(address, locality, portName) + + if endpoint.Address != address { + t.Errorf("Address mismatch. Got: %v, expected: %v", endpoint.Address, address) + } + if endpoint.Locality != locality { + t.Errorf("Locality mismatch. Got: %v, expected: %v", endpoint.Locality, locality) + } + if endpoint.Ports[portName] != 15443 { + t.Errorf("Incorrect port found") + } +} + func buildFakeConfigMapFromAddressStore(addressStore *ServiceEntryAddressStore, resourceVersion string) *v1.ConfigMap { bytes, _ := yaml.Marshal(addressStore) @@ -352,64 +390,82 @@ func TestModifyExistingSidecarForLocalClusterCommunication(t *testing.T) { } } -// -//func TestCreateServiceEntry(t *testing.T) { -// admiralCache := AdmiralCache{} -// -// localAddress := common.LocalAddressPrefix + ".10.1" -// -// cnameIdentityCache := sync.Map{} -// cnameIdentityCache.Store("dev.bar.global", "bar") -// admiralCache.CnameIdentityCache = &cnameIdentityCache -// -// admiralCache.ServiceEntryAddressStore = &ServiceEntryAddressStore{ -// EntryAddresses: map[string]string{"e2e.my-first-service.mesh-se":localAddress}, -// Addresses: []string{localAddress}, -// } -// -// admiralCache.CnameClusterCache = common.NewMapOfMaps() -// -// rc, _ := createMockRemoteController(func(i interface{}) { -// res := i.(istio.Config) -// se, ok := res.Spec.(*networking.ServiceEntry) -// if ok { -// if se.Hosts[0] != "dev.bar.global" { -// t.Errorf("Host mismatch. Expected dev.bar.global, got %v", se.Hosts[0]) -// } -// } -// }) -// -// params := AdmiralParams{ -// EnableSAN: true, -// SANPrefix: "prefix", -// LabelSet:&common.LabelSet{WorkloadIdentityLabel:"identity"}, -// HostnameSuffix: "mesh", -// } -// -// cacheWithEntry := ServiceEntryAddressStore{ -// EntryAddresses: map[string]string{"e2e.my-first-service.mesh": localAddress}, -// Addresses: []string{localAddress}, -// } -// -// cacheController := &test.FakeConfigMapController{ -// GetError: nil, -// PutError: nil, -// ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithEntry, "123"), -// } -// -// admiralCache.ConfigMapController = cacheController -// -// deployment := v12.Deployment{} -// deployment.Spec.Template.Labels = map[string]string{"env":"e2e", "identity":"my-first-service", } -// -// resultingEntry := createServiceEntry(rc, params, &admiralCache, &deployment, map[string]*networking.ServiceEntry{}) -// -// if resultingEntry.Hosts[0] != "e2e.my-first-service.mesh" { -// t.Errorf("Host mismatch. Got: %v, expected: e2e.my-first-service.mesh", resultingEntry.Hosts[0]) -// } -// -// if resultingEntry.Addresses[0] != localAddress { -// t.Errorf("Address mismatch. Got: %v, expected: " + localAddress, resultingEntry.Addresses[0]) -// } -// -//} + +func TestCreateServiceEntry(t *testing.T) { + + config := rest.Config{ + Host: "localhost", + } + stop := make(chan struct{}) + s, e := admiral.NewServiceController(stop, &test.MockServiceHandler{}, &config, time.Second*time.Duration(300)) + + if e != nil { + t.Fatalf("%v", e) + } + + admiralCache := AdmiralCache{} + + localAddress := common.LocalAddressPrefix + ".10.1" + + cnameIdentityCache := sync.Map{} + cnameIdentityCache.Store("dev.bar.global", "bar") + admiralCache.CnameIdentityCache = &cnameIdentityCache + + admiralCache.ServiceEntryAddressStore = &ServiceEntryAddressStore{ + EntryAddresses: map[string]string{"e2e.my-first-service.mesh-se":localAddress}, + Addresses: []string{localAddress}, + } + + admiralCache.CnameClusterCache = common.NewMapOfMaps() + + fakeIstioClient := istiofake.NewSimpleClientset() + rc := &RemoteController{ + ServiceEntryController: &istio.ServiceEntryController{ + IstioClient: fakeIstioClient, + }, + DestinationRuleController: &istio.DestinationRuleController{ + IstioClient: fakeIstioClient, + }, + NodeController: &admiral.NodeController{ + Locality: &admiral.Locality{ + Region: "us-west-2", + }, + }, + ServiceController: s, + } + + cacheWithEntry := ServiceEntryAddressStore{ + EntryAddresses: map[string]string{"e2e.my-first-service.mesh": localAddress}, + Addresses: []string{localAddress}, + } + + cacheController := &test.FakeConfigMapController{ + GetError: nil, + PutError: nil, + ConfigmapToReturn: buildFakeConfigMapFromAddressStore(&cacheWithEntry, "123"), + } + + admiralCache.ConfigMapController = cacheController + + deployment := v14.Deployment{} + deployment.Spec.Template.Labels = map[string]string{"env":"e2e", "identity":"my-first-service", } + + resultingEntry := createServiceEntry(rc, &admiralCache, &deployment, map[string]*istionetworkingv1alpha3.ServiceEntry{}) + + if resultingEntry.Hosts[0] != "e2e.my-first-service.mesh" { + t.Errorf("Host mismatch. Got: %v, expected: e2e.my-first-service.mesh", resultingEntry.Hosts[0]) + } + + if resultingEntry.Addresses[0] != localAddress { + t.Errorf("Address mismatch. Got: %v, expected: " + localAddress, resultingEntry.Addresses[0]) + } + + if resultingEntry.Endpoints[0].Address != "admiral_dummy.com" { + t.Errorf("Endpoint mismatch. Got %v, expected: %v", resultingEntry.Endpoints[0].Address, "admiral_dummy.com") + } + + if resultingEntry.Endpoints[0].Locality != "us-west-2" { + t.Errorf("Locality mismatch. Got %v, expected: %v", resultingEntry.Endpoints[0].Locality, "us-west-2") + } + +} diff --git a/admiral/pkg/controller/istio/destinationrule.go b/admiral/pkg/controller/istio/destinationrule.go index 457aaddd..44209630 100644 --- a/admiral/pkg/controller/istio/destinationrule.go +++ b/admiral/pkg/controller/istio/destinationrule.go @@ -26,7 +26,7 @@ type DestinationRuleEntry struct { } type DestinationRuleController struct { - IstioClient *versioned.Clientset + IstioClient versioned.Interface DestinationRuleHandler DestinationRuleHandler informer cache.SharedIndexInformer ctl *admiral.Controller diff --git a/admiral/pkg/controller/istio/serviceentry.go b/admiral/pkg/controller/istio/serviceentry.go index db769dbb..d1442023 100644 --- a/admiral/pkg/controller/istio/serviceentry.go +++ b/admiral/pkg/controller/istio/serviceentry.go @@ -26,7 +26,7 @@ type ServiceEntryEntry struct { } type ServiceEntryController struct { - IstioClient *versioned.Clientset + IstioClient versioned.Interface ServiceEntryHandler ServiceEntryHandler informer cache.SharedIndexInformer ctl *admiral.Controller diff --git a/admiral/pkg/controller/istio/virtualservice.go b/admiral/pkg/controller/istio/virtualservice.go index 21c7f1b3..081b6fae 100644 --- a/admiral/pkg/controller/istio/virtualservice.go +++ b/admiral/pkg/controller/istio/virtualservice.go @@ -21,7 +21,7 @@ type VirtualServiceHandler interface { } type VirtualServiceController struct { - IstioClient *versioned.Clientset + IstioClient versioned.Interface VirtualServiceHandler VirtualServiceHandler informer cache.SharedIndexInformer ctl *admiral.Controller diff --git a/go.sum b/go.sum index cab1d165..bea750c3 100644 --- a/go.sum +++ b/go.sum @@ -397,6 +397,8 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191108234033-bd318be0434a h1:R/qVym5WAxsZWQqZCwDY/8sdVKV1m1WgU4/S5IRQAzc= golang.org/x/crypto v0.0.0-20191108234033-bd318be0434a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo= +golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -606,10 +608,14 @@ istio.io/api v0.0.0-20191107224652-6818c03d25b2 h1:rIP85xkbLc9Df20/Tnz2LxNck7uhO istio.io/api v0.0.0-20191107224652-6818c03d25b2/go.mod h1:+cyHH83OwC0rFpwk8eXctzPNpiCAbB+r6kmMiAxxBHw= istio.io/api v0.0.0-20200226024546-cca495b82b03 h1:c0Lsnavz2gRslJxqtqiP7VOxr8EJHJJWgfSJabfnZ2s= istio.io/api v0.0.0-20200226024546-cca495b82b03/go.mod h1:bcY3prusO/6vA6zGHz4PNG2v79clPyTw06Xx3fprJSQ= +istio.io/api v0.0.0-20200325005357-8217d7225b6d h1:qOJdOmqJdBZJSrQWgYImY0JTqqRIV3iturAW6gpN4Zs= +istio.io/api v0.0.0-20200325005357-8217d7225b6d/go.mod h1:bcY3prusO/6vA6zGHz4PNG2v79clPyTw06Xx3fprJSQ= istio.io/client-go v0.0.0-20191104174404-7b65e62d85b0 h1:6DWOkG2XZQRAwthO71a0v+AFcONHF6Wo9mtfMNUly24= istio.io/client-go v0.0.0-20191104174404-7b65e62d85b0/go.mod h1:jdpC7NC/WdBGIxcIHPzlurKaCZb+8LnsJW4U4mbX6n0= istio.io/client-go v0.0.0-20200226182959-cde3e69bd9dd h1:U5eWo5yvlRsk4pr5s6ky63G1dJkAgugXzoiKzkMQxGc= istio.io/client-go v0.0.0-20200226182959-cde3e69bd9dd/go.mod h1:wLFtAm266NbVvt1Y8ZwbZXCdp1kgnBuxZTAIaMwUkto= +istio.io/client-go v0.0.0-20200325170329-dc00bbff4229 h1:io9dTYbZm5iOo18iplGH0SCH4ovGfYctUlVzVolWx4I= +istio.io/client-go v0.0.0-20200325170329-dc00bbff4229/go.mod h1:SurC7RfK3Js8hEAPa0xZXxw8xk2d9tZx7YRsvbLlrBo= istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a h1:w7zILua2dnYo9CxImhpNW4NE/8ZxEoc/wfBfHrhUhrE= 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= @@ -627,6 +633,8 @@ k8s.io/api v0.0.0-20191108065827-59e77acf588f/go.mod h1:uQDmBYHoPSuhbg8FGTRzrOda 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.4 h1:HbwOhDapkguO8lTAE8OX3hdF2qp8GtpC9CW/MQATXXo= +k8s.io/api v0.17.4/go.mod h1:5qxx6vjmwUVG2nHQTKGlLts8Tbok8PzHl4vHtVFuZCA= k8s.io/apiextensions-apiserver v0.0.0-20181204003618-e419c5771cdc h1:IOukeE9HtTwpLslbujLDfRpfFU6tsjq28yO0fjnl/hk= k8s.io/apiextensions-apiserver v0.0.0-20181204003618-e419c5771cdc/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE= k8s.io/apiextensions-apiserver v0.0.0-20191014073952-6b6acca910e3 h1:11T1avcLWsxcVNglsvkStUw3GJFjOYxuCb17+88+GI8= @@ -644,12 +652,15 @@ k8s.io/apimachinery v0.17.2 h1:hwDQQFbdRlpnnsR64Asdi55GyCaIP/3WQpMmbNBeWr4= 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.4 h1:UzM+38cPUJnzqSQ+E1PY4YxMHIzQyCg29LOoGfo79Zw= +k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= k8s.io/client-go v0.0.0-20190117233410-4022682532b3 h1:7VVBo3+/iX6dzB8dshNuo6Duds/6AoNP5R59IUnwoxg= k8s.io/client-go v0.0.0-20190117233410-4022682532b3/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= k8s.io/client-go v0.0.0-20191016111102-bec269661e48 h1:C2XVy2z0dV94q9hSSoCuTPp1KOG7IegvbdXuz9VGxoU= k8s.io/client-go v0.0.0-20191016111102-bec269661e48/go.mod h1:hrwktSwYGI4JK+TJA3dMaFyyvHVi/aLarVHpbs8bgCU= 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.4/go.mod h1:ouF6o5pz3is8qU0/qYL2RnoxOPqgfuidYLowytyLJmc= k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o= k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= k8s.io/code-generator v0.0.0-20191108065441-3c1097069dc3 h1:qcl3ardofg2+EAvhYmwX+XMLXyDQ6dOp8yL2ir1JfsM= From f2ced6352a007abb73764d7ecb801da242eed7b8 Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Fri, 10 Apr 2020 14:57:07 -0700 Subject: [PATCH 39/45] Update CONTRIBUTING.md Signed-off-by: Madeline --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 07e1b4f6..924957c4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ We welcome contributions :) ## Submitting PRs * Make sure to check existing issues and file an issue before starting to work on a feature/bug. This will help prevent duplication of work. -Also refer to [Collaboration](./README.md#Collaboration and Communication) for communication channels. +Also refer to [Collaboration](./README.md) for communication channels. ## Setting up for local Development * Clone the repo and set ADMIRAL_HOME env variable @@ -34,4 +34,4 @@ $ADMIRAL_HOME/install/scripts/cluster-secret.sh Date: Tue, 14 Apr 2020 14:38:53 -0700 Subject: [PATCH 40/45] finished first draft for recognizing admiral ignore annotation, ready for review, fixed some typos and 404 link for helm install Signed-off-by: Madeline --- admiral/pkg/controller/admiral/controller.go | 2 +- admiral/pkg/controller/admiral/deployment.go | 18 +++++++++++++++++- admiral/pkg/controller/common/common.go | 1 + admiral/pkg/controller/common/types.go | 2 +- docs/Examples.md | 2 +- install/admiralremote/base/remote.yaml | 2 +- install/sample/base/webapp.yaml | 2 ++ 7 files changed, 24 insertions(+), 5 deletions(-) diff --git a/admiral/pkg/controller/admiral/controller.go b/admiral/pkg/controller/admiral/controller.go index 0213e944..f214cd85 100644 --- a/admiral/pkg/controller/admiral/controller.go +++ b/admiral/pkg/controller/admiral/controller.go @@ -67,7 +67,7 @@ func NewController(stopCh <-chan struct{}, delegator Delegator, informer cache.S return controller } -// Run starts the controller until it receves a message over stopCh +// Run starts the controller until it receives a message over stopCh func (c *Controller) Run(stopCh <-chan struct{}) { defer utilruntime.HandleCrash() defer c.queue.ShutDown() diff --git a/admiral/pkg/controller/admiral/deployment.go b/admiral/pkg/controller/admiral/deployment.go index 3c73b112..2a576881 100644 --- a/admiral/pkg/controller/admiral/deployment.go +++ b/admiral/pkg/controller/admiral/deployment.go @@ -9,6 +9,7 @@ import ( "k8s.io/client-go/rest" "time" + log "github.com/sirupsen/logrus" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" @@ -175,8 +176,23 @@ func (d *DeploymentController) shouldIgnoreBasedOnLabels(deployment *k8sAppsV1.D if deployment.Spec.Template.Labels[d.labelSet.AdmiralIgnoreLabel] == "true" { //if we should ignore, do that and who cares what else is there return true } + if deployment.Spec.Template.Annotations[d.labelSet.DeploymentAnnotation] != "true" { //Not sidecar injected, we don't want to inject - return true + return true + } + + if deployment.Annotations[common.AdmiralIgnoreAnnotation] == "true" { + return true + } + + ns, err := d.K8sClient.CoreV1().Namespaces().Get(deployment.Namespace, meta_v1.GetOptions{}) + if err != nil { + log.Warnf("Failed to get namespace object for deployment with namespace %v, err: %v", deployment.Namespace, err) + return false + } + + if ns.Annotations[common.AdmiralIgnoreAnnotation] == "true" { + return true } return false //labels are fine, we should not ignore } diff --git a/admiral/pkg/controller/common/common.go b/admiral/pkg/controller/common/common.go index 5becd0a9..6c39de71 100644 --- a/admiral/pkg/controller/common/common.go +++ b/admiral/pkg/controller/common/common.go @@ -27,6 +27,7 @@ const ( SpiffePrefix = "spiffe://" SidecarEnabledPorts = "traffic.sidecar.istio.io/includeInboundPorts" Default = "default" + AdmiralIgnoreAnnotation = "admiral.io/ignore" ) type Event int diff --git a/admiral/pkg/controller/common/types.go b/admiral/pkg/controller/common/types.go index 2c375ec2..c6d1f86e 100644 --- a/admiral/pkg/controller/common/types.go +++ b/admiral/pkg/controller/common/types.go @@ -60,7 +60,7 @@ type LabelSet struct { NamespaceSidecarInjectionLabelValue string 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 seperately from the identity key because this one _must_ be a label + GlobalTrafficDeploymentLabel string //label used to tie together deployments and globaltrafficpolicy objects. Configured separately from the identity key because this one _must_ be a label } func NewSidecarEgressMap() *SidecarEgressMap { diff --git a/docs/Examples.md b/docs/Examples.md index 2ed3345a..261fccfc 100644 --- a/docs/Examples.md +++ b/docs/Examples.md @@ -11,7 +11,7 @@ One or more k8s clusters will need the following steps executed * Install [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) * Install [minikube](https://istio.io/docs/setup/platform-setup/minikube/) to bring up a k8s cluster locally (Make sure your `$KUBECONFIG` points to `minikube` before proceeding) -* Install [helm](https://github.com/helm/helm/blob/master/docs/install.md) +* Install [helm](https://helm.sh/docs/intro/install/) * Install [wget](https://www.gnu.org/software/wget/) #### Install Istio diff --git a/install/admiralremote/base/remote.yaml b/install/admiralremote/base/remote.yaml index 772423a7..1c1e2018 100644 --- a/install/admiralremote/base/remote.yaml +++ b/install/admiralremote/base/remote.yaml @@ -18,7 +18,7 @@ metadata: name: admiral-sync-read rules: - apiGroups: ['', 'apps'] - resources: [ 'pods', 'services', 'nodes', 'deployments'] + resources: [ 'pods', 'services', 'nodes', 'deployments', 'namespaces'] verbs: ['get', 'watch', 'list'] - apiGroups: ["networking.istio.io"] resources: ['virtualservices', 'destinationrules', 'serviceentries', 'envoyfilters' ,'gateways', 'sidecars'] diff --git a/install/sample/base/webapp.yaml b/install/sample/base/webapp.yaml index 011262e7..851d3e9b 100644 --- a/install/sample/base/webapp.yaml +++ b/install/sample/base/webapp.yaml @@ -8,6 +8,8 @@ apiVersion: extensions/v1beta1 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 template: From 6f9f8ec32ce8886f0d0b99263f829f91424aad74 Mon Sep 17 00:00:00 2001 From: Mengying-Li <43981707+Mengying-Li@users.noreply.github.com> Date: Wed, 15 Apr 2020 14:05:59 -0700 Subject: [PATCH 41/45] Add ignore annotation to skip deployments/namespaces (#93) * finished first draft for recognizing admiral ignore annotation, ready for review, fixed some typos and 404 link for helm install Signed-off-by: Madeline * added new fake client to pass CI Signed-off-by: Madeline * added two tests for testing new annotation restriction Signed-off-by: Madeline Co-authored-by: Madeline Signed-off-by: Madeline --- .../pkg/controller/admiral/deployment_test.go | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/admiral/pkg/controller/admiral/deployment_test.go b/admiral/pkg/controller/admiral/deployment_test.go index dafa5434..3effdbdb 100644 --- a/admiral/pkg/controller/admiral/deployment_test.go +++ b/admiral/pkg/controller/admiral/deployment_test.go @@ -40,6 +40,14 @@ func TestDeploymentController_Added(t *testing.T) { deploymentWithIgnoreLabels := k8sAppsV1.Deployment{} deploymentWithIgnoreLabels.Spec.Template.Labels = map[string]string{"identity": "id", "istio-injected": "true", "admiral-ignore": "true"} deploymentWithIgnoreLabels.Spec.Template.Annotations = map[string]string{"sidecar.istio.io/inject": "true"} + deploymentWithIgnoreAnnotations := k8sAppsV1.Deployment{} + deploymentWithIgnoreAnnotations.Spec.Template.Labels = map[string]string{"identity": "id"} + deploymentWithIgnoreAnnotations.Annotations = map[string]string{"admiral.io/ignore":"true"} + deploymentWithIgnoreAnnotations.Spec.Template.Annotations = map[string]string{"sidecar.istio.io/inject": "true"} + deploymentWithNsIgnoreAnnotations := k8sAppsV1.Deployment{} + deploymentWithNsIgnoreAnnotations.Spec.Template.Labels = map[string]string{"identity": "id"} + deploymentWithNsIgnoreAnnotations.Spec.Template.Annotations = map[string]string{"sidecar.istio.io/inject": "true"} + deploymentWithNsIgnoreAnnotations.Namespace = "test-ns" testCases := []struct { name string @@ -60,14 +68,33 @@ func TestDeploymentController_Added(t *testing.T) { expectedCacheSize: 0, }, { - name: "Expects ignored deployment to not be added to the cache", + name: "Expects ignored deployment identified by label to not be added to the cache", deployment: &deploymentWithIgnoreLabels, expectedDeployment: nil, expectedCacheSize: 0, }, + { + name: "Expects ignored deployment identified by deployment annotation to not be added to the cache", + deployment: &deploymentWithIgnoreAnnotations, + expectedDeployment: nil, + expectedCacheSize: 0, + }, + { + name: "Expects ignored deployment identified by namespace annotation to not be added to the cache", + deployment: &deploymentWithNsIgnoreAnnotations, + expectedDeployment: nil, + expectedCacheSize: 0, + }, } for _, c := range testCases { t.Run(c.name, func(t *testing.T) { + depController.K8sClient = fake.NewSimpleClientset() + if c.name == "Expects ignored deployment identified by namespace annotation to not be added to the cache" { + ns := coreV1.Namespace{} + ns.Name = "test-ns" + ns.Annotations = map[string]string{"admiral.io/ignore":"true"} + depController.K8sClient.CoreV1().Namespaces().Create(&ns) + } depController.Cache.cache = map[string]*DeploymentClusterEntry{} depController.Added(c.deployment) if c.expectedDeployment == nil { From 54d23f97cbc1c110c869fd677f4bec93c8e9c2a3 Mon Sep 17 00:00:00 2001 From: aattuluri <44482891+aattuluri@users.noreply.github.com> Date: Wed, 15 Apr 2020 14:26:26 -0700 Subject: [PATCH 42/45] Traffic at ingressgateway should always be routed to cluster local service instance (#91) * Route traffic to cluster local service instance from ingress-gateway * Override kustomize admiral image tag for releases with the release tag * Minor documentation edits Fixes https://github.com/istio-ecosystem/admiral/issues/90 Fixes https://github.com/istio-ecosystem/admiral/issues/62 Fixes https://github.com/istio-ecosystem/admiral/issues/39 Signed-off-by: Madeline --- .circleci/config.yml | 1 + Makefile | 3 +- admiral/pkg/clusters/handler.go | 16 +++- admiral/pkg/clusters/registry_test.go | 2 +- admiral/pkg/clusters/serviceentry.go | 16 ++++ admiral/pkg/clusters/serviceentry_test.go | 73 +++++++++++++++- admiral/pkg/clusters/types.go | 27 ------ admiral/pkg/clusters/util.go | 33 ------- admiral/pkg/controller/admiral/controller.go | 57 ++++++++----- admiral/pkg/controller/admiral/dependency.go | 19 +---- .../pkg/controller/admiral/dependency_test.go | 56 ++++++++++++ admiral/pkg/controller/admiral/deployment.go | 26 +++--- .../pkg/controller/admiral/globaltraffic.go | 13 +-- .../controller/admiral/globaltraffic_test.go | 85 +++++++++++++++++++ admiral/pkg/controller/admiral/node.go | 8 +- admiral/pkg/controller/admiral/node_test.go | 44 ++++++++++ admiral/pkg/controller/admiral/pod.go | 9 +- admiral/pkg/controller/admiral/service.go | 18 ++-- admiral/pkg/controller/common/common_test.go | 2 + admiral/pkg/controller/common/config_test.go | 15 ++++ admiral/pkg/controller/common/types_test.go | 46 +++++++++- .../pkg/controller/istio/destinationrule.go | 11 +-- .../controller/istio/destinationrule_test.go | 25 ++++++ admiral/pkg/controller/istio/serviceentry.go | 11 +-- .../pkg/controller/istio/serviceentry_test.go | 25 ++++++ admiral/pkg/controller/istio/sidecar.go | 11 +-- admiral/pkg/controller/istio/sidecar_test.go | 25 ++++++ .../pkg/controller/istio/virtualservice.go | 6 +- .../controller/istio/virtualservice_test.go | 26 ++++++ admiral/pkg/controller/util/util_test.go | 35 ++++++++ admiral/pkg/test/mock.go | 41 +++++---- docs/Compatibility.md | 12 +++ docs/Examples.md | 29 +++++-- install/scripts/cleanup.sh | 15 ++++ 34 files changed, 640 insertions(+), 201 deletions(-) create mode 100755 install/scripts/cleanup.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 7d4e4074..267fd763 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -53,6 +53,7 @@ jobs: make download-kustomize echo "export PATH=/go/pkg/mod/github.com/admiral:$PATH" >> $BASH_ENV source $BASH_ENV + (cd "./install/admiral/overlays/demosinglecluster" && kustomize edit set image "docker.io/admiralproj/admiral=docker.io/admiralproj/admiral:${CIRCLE_TAG}") make gen-yaml mkdir ./artifact cd out && tar -zcvf ../artifact/admiral-install-${CIRCLE_TAG}.tar.gz * && cd .. diff --git a/Makefile b/Makefile index ce90c650..5546090c 100644 --- a/Makefile +++ b/Makefile @@ -133,5 +133,4 @@ gen-yaml: kustomize build ./install/sample/base/ > ./out/yaml/sample.yaml kustomize build ./install/sample/overlays/remote > ./out/yaml/remotecluster_sample.yaml cp ./install/sample/sample_dep.yaml ./out/yaml/sample_dep.yaml - cp ./install/scripts/cluster-secret.sh ./out/scripts/cluster-secret.sh - cp ./install/scripts/redirect-dns.sh ./out/scripts/redirect-dns.sh + cp ./install/scripts/*.sh ./out/scripts/ diff --git a/admiral/pkg/clusters/handler.go b/admiral/pkg/clusters/handler.go index 8d550005..e83e5508 100644 --- a/admiral/pkg/clusters/handler.go +++ b/admiral/pkg/clusters/handler.go @@ -81,7 +81,7 @@ func handleDependencyRecord(sourceIdentity string, r *RemoteRegistry, rcs map[st if deployments == nil || len(deployments.Deployments) == 0 { continue } - //TODO pass deployment + tmpSe := createServiceEntry(rc, r.AdmiralCache, deployment[0], serviceEntries) if tmpSe == nil { @@ -120,9 +120,13 @@ func getIstioResourceName(host string, suffix string) string { return strings.ToLower(host) + suffix } -func makeVirtualService(host string, destination string, port uint32) *v1alpha32.VirtualService { +func makeIngressOnlyVirtualService(host string, destination string, port uint32) *v1alpha32.VirtualService { + return makeVirtualService(host, []string{common.MulticlusterIngressGateway}, destination, port); +} + +func makeVirtualService(host string, gateways [] string, destination string, port uint32) *v1alpha32.VirtualService { return &v1alpha32.VirtualService{Hosts: []string{host}, - Gateways: []string{common.MulticlusterIngressGateway}, + Gateways: gateways, ExportTo: []string{"*"}, Http: []*v1alpha32.HTTPRoute{{Route: []*v1alpha32.HTTPRouteDestination{{Destination: &v1alpha32.Destination{Host: destination, Port: &v1alpha32.PortSelector{Number: port}}}}}}} } @@ -483,7 +487,7 @@ func handleVirtualServiceEvent(obj *v1alpha3.VirtualService, vh *VirtualServiceH func addUpdateVirtualService(obj *v1alpha3.VirtualService, exist *v1alpha3.VirtualService, namespace string, rc *RemoteController) { var err error var op string - if exist == nil { + if exist == nil || len(exist.Spec.Hosts) == 0 { obj.Namespace = namespace obj.ResourceVersion = "" _, err = rc.VirtualServiceController.IstioClient.NetworkingV1alpha3().VirtualServices(namespace).Create(obj) @@ -561,6 +565,10 @@ func createDestinationRulSkeletion(dr v1alpha32.DestinationRule, name string, na return &v1alpha3.DestinationRule{Spec: dr, ObjectMeta: v12.ObjectMeta{Name: name, Namespace: namespace}} } +func createVirtualServiceSkeletion(se v1alpha32.VirtualService, name string, namespace string) *v1alpha3.VirtualService { + return &v1alpha3.VirtualService{Spec: se, ObjectMeta: v12.ObjectMeta{Name: name, Namespace: namespace}} +} + func getServiceForDeployment(rc *RemoteController, deployment *k8sAppsV1.Deployment) *k8sV1.Service { if deployment == nil { diff --git a/admiral/pkg/clusters/registry_test.go b/admiral/pkg/clusters/registry_test.go index 8fe48060..8a14136b 100644 --- a/admiral/pkg/clusters/registry_test.go +++ b/admiral/pkg/clusters/registry_test.go @@ -308,7 +308,7 @@ func TestAdded(t *testing.T) { } func TestMakeVirtualService(t *testing.T) { - vs := makeVirtualService("test.local", "dest", 8080) + vs := makeVirtualService("test.local", []string {common.MulticlusterIngressGateway}, "dest", 8080) if vs.Hosts[0] != "test.local" { t.Fail() } diff --git a/admiral/pkg/clusters/serviceentry.go b/admiral/pkg/clusters/serviceentry.go index bd5ffdcb..5282c2ff 100644 --- a/admiral/pkg/clusters/serviceentry.go +++ b/admiral/pkg/clusters/serviceentry.go @@ -175,6 +175,9 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem ep.Ports = oldPorts } } + //add virtual service for routing locally in within the cluster + createIngressOnlyVirtualService(rc, cname, serviceEntry, localFqdn, meshPorts) + } for _, val := range dependents.Map() { @@ -188,6 +191,19 @@ func createServiceEntryForNewServiceOrPod(env string, sourceIdentity string, rem return serviceEntries } +func createIngressOnlyVirtualService(rc *RemoteController, cname string, serviceEntry *networking.ServiceEntry, localFqdn string, meshPorts map[string]uint32) { + virtualServiceName := getIstioResourceName(cname, "-default-vs") + + oldVirtualService, _ := rc.VirtualServiceController.IstioClient.NetworkingV1alpha3().VirtualServices(common.GetSyncNamespace()).Get(virtualServiceName, v12.GetOptions{}); + + //TODO handle non http ports + virtualService := makeIngressOnlyVirtualService(serviceEntry.Hosts[0], localFqdn, meshPorts[common.Http]) + + newVirtualService := createVirtualServiceSkeletion(*virtualService, virtualServiceName, common.GetSyncNamespace()) + + addUpdateVirtualService(newVirtualService, oldVirtualService, common.GetSyncNamespace(), rc); +} + func modifySidecarForLocalClusterCommunication(sidecarNamespace string, sidecarEgressMap map[string]common.SidecarEgress, rc *RemoteController) { //get existing sidecar from the cluster diff --git a/admiral/pkg/clusters/serviceentry_test.go b/admiral/pkg/clusters/serviceentry_test.go index 46cac1a6..f65fb7a6 100644 --- a/admiral/pkg/clusters/serviceentry_test.go +++ b/admiral/pkg/clusters/serviceentry_test.go @@ -114,7 +114,7 @@ func TestAddServiceEntriesWithDr(t *testing.T) { } AddServiceEntriesWithDr(&admiralCache, map[string]string{"cl1":"cl1"}, map[string]*RemoteController{"cl1":rc}, map[string]*istionetworkingv1alpha3.ServiceEntry{"se1": &se}) - } +} func TestCreateServiceEntryForNewServiceOrPod(t *testing.T) { @@ -469,3 +469,74 @@ func TestCreateServiceEntry(t *testing.T) { } } + +func TestCreateIngressOnlyVirtualService(t *testing.T) { + + fakeIstioClientCreate := istiofake.NewSimpleClientset() + rcCreate := &RemoteController{ + VirtualServiceController: &istio.VirtualServiceController{ + IstioClient: fakeIstioClientCreate, + }, + } + fakeIstioClientUpdate := istiofake.NewSimpleClientset() + rcUpdate:= &RemoteController{ + VirtualServiceController: &istio.VirtualServiceController{ + IstioClient: fakeIstioClientUpdate, + }, + } + + cname := "qa.mysvc.global" + + vsname := cname + "-default-vs" + + localFqdn := "mysvc.newmynamespace.svc.cluster.local" + localFqdn2 := "mysvc.mynamespace.svc.cluster.local" + + vsTobeUpdated := makeVirtualService(cname, []string{common.MulticlusterIngressGateway}, localFqdn, 80) + + rcUpdate.VirtualServiceController.IstioClient.NetworkingV1alpha3().VirtualServices(common.GetSyncNamespace()).Create(&v1alpha3.VirtualService{ + Spec: *vsTobeUpdated, + ObjectMeta: v12.ObjectMeta{Name: vsname, Namespace: common.GetSyncNamespace()}}) + + testCases := []struct { + name string + rc *RemoteController + localFqdn string + expectedResult string + }{ + { + name: "Should return a created virtual service", + rc: rcCreate, + localFqdn: localFqdn, + expectedResult: localFqdn, + }, + { + name: "Should return an updated virtual service", + rc: rcCreate, + localFqdn: localFqdn2, + expectedResult: localFqdn2, + }, + } + + //Run the test for every provided case + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + createIngressOnlyVirtualService(c.rc, cname, &istionetworkingv1alpha3.ServiceEntry{Hosts: []string{"qa.mysvc.global"}}, c.localFqdn, map[string]uint32 {common.Http : 80}) + vs, err := c.rc.VirtualServiceController.IstioClient.NetworkingV1alpha3().VirtualServices(common.GetSyncNamespace()).Get(vsname, v12.GetOptions{}) + if err != nil { + t.Errorf("Test %s failed, expected: %v got %v", c.name, c.expectedResult, err) + } + if vs == nil && vs.Spec.Http[0].Route[0].Destination.Host != c.expectedResult { + if vs != nil { + t.Errorf("Virtual service update failed with expected local fqdn: %v, got: %v", localFqdn2, vs.Spec.Http[0].Route[0].Destination.Host) + } + t.FailNow() + } + if len(vs.Spec.Gateways) > 1 || vs.Spec.Gateways[0] != common.MulticlusterIngressGateway { + t.Errorf("Virtual service gateway expected: %v, got: %v", common.MulticlusterIngressGateway, vs.Spec.Gateways) + } + }) + } +} + + diff --git a/admiral/pkg/clusters/types.go b/admiral/pkg/clusters/types.go index 3cc17ee0..f7cc1d03 100644 --- a/admiral/pkg/clusters/types.go +++ b/admiral/pkg/clusters/types.go @@ -323,33 +323,6 @@ func (pc *PodHandler) Deleted(obj *k8sV1.Pod) { //TODO update subset service entries } -func (nc *NodeHandler) Added(obj *k8sV1.Node) { - //log.Infof("New Pod %s on cluster: %s in namespace: %s", obj.Name, obj.ClusterName, obj.Namespace) -} - -func (pc *NodeHandler) Deleted(obj *k8sV1.Node) { - // log.Infof("Pod deleted %s on cluster: %s in namespace: %s", obj.Name, obj.ClusterName, obj.Namespace) -} - -func (sc *ServiceHandler) Added(obj *k8sV1.Service) { - - log.Infof(LogFormat, "Event", "service", obj.Name, "", "Received, doing nothing") - - //sourceIdentity := common.GetServiceGlobalIdentifier(obj) - // - //if len(sourceIdentity) == 0 { - // log.Infof(LogFormat, "Event", "service", obj.Name, "", "Skipped as '" + common.GlobalIdentifier() + " was not found', namespace=" + obj.Namespace) - // return - //} - // - //createServiceEntryForNewServiceOrPod(obj.Namespace, sourceIdentity, sc.RemoteRegistry, sc.RemoteRegistry.config.SyncNamespace) - -} - -func (sc *ServiceHandler) Deleted(obj *k8sV1.Service) { - -} - func getCacheKey(environment string, identity string) string { return fmt.Sprintf("%s.%s", environment, identity) } diff --git a/admiral/pkg/clusters/util.go b/admiral/pkg/clusters/util.go index d86df48d..a7a0f631 100644 --- a/admiral/pkg/clusters/util.go +++ b/admiral/pkg/clusters/util.go @@ -3,13 +3,11 @@ package clusters import ( "errors" "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" - "github.com/istio-ecosystem/admiral/admiral/pkg/controller/util" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" "strconv" "strings" - networking "istio.io/api/networking/v1alpha3" k8sAppsV1 "k8s.io/api/apps/v1" k8sV1 "k8s.io/api/core/v1" ) @@ -79,34 +77,3 @@ func ValidateConfigmapBeforePutting(cm *k8sV1.ConfigMap) error { } return nil } - -func MakeVirtualService(host string, destination string, port uint32) *networking.VirtualService { - return &networking.VirtualService{Hosts: []string{host}, - Gateways: []string{common.Mesh, common.MulticlusterIngressGateway}, - ExportTo: []string{"*"}, - Http: []*networking.HTTPRoute{{Route: []*networking.HTTPRouteDestination{{Destination: &networking.Destination{Host: destination, Port: &networking.PortSelector{Number: port}}}}}}} -} - -func MakeRemoteEndpointForServiceEntry(address string, locality string, portName string) *networking.ServiceEntry_Endpoint { - return &networking.ServiceEntry_Endpoint{Address: address, - Locality: locality, - Ports: map[string]uint32{portName: common.DefaultMtlsPort}} // -} - -func GetDestinationRule(host string) *networking.DestinationRule { - return &networking.DestinationRule{Host: host, - TrafficPolicy: &networking.TrafficPolicy{Tls: &networking.TLSSettings{Mode: networking.TLSSettings_ISTIO_MUTUAL}}} -} - -func CopyEndpoint(e *networking.ServiceEntry_Endpoint) *networking.ServiceEntry_Endpoint { - labels := make(map[string]string) - util.MapCopy(labels, e.Labels) - ports := make(map[string]uint32) - util.MapCopy(ports, e.Ports) - return &networking.ServiceEntry_Endpoint{Address: e.Address, Ports: ports, Locality: e.Locality, Labels: labels} -} - -func CopyServiceEntry(se *networking.ServiceEntry) *networking.ServiceEntry { - return &networking.ServiceEntry{Ports: se.Ports, Resolution: se.Resolution, Hosts: se.Hosts, Location: se.Location, - SubjectAltNames: se.SubjectAltNames, ExportTo: se.ExportTo, Endpoints: se.Endpoints, Addresses: se.Addresses} -} diff --git a/admiral/pkg/controller/admiral/controller.go b/admiral/pkg/controller/admiral/controller.go index f214cd85..1e015329 100644 --- a/admiral/pkg/controller/admiral/controller.go +++ b/admiral/pkg/controller/admiral/controller.go @@ -19,9 +19,25 @@ const ( // Handler interface contains the methods that are required type Delegator interface { Added(interface{}) + Updated(interface{}, interface{}) Deleted(interface{}) } +type EventType string + +const ( + Add EventType = "Add" + Update EventType = "Update" + Delete EventType = "Delete" +) + +type InformerCacheObj struct { + key string + eventType EventType + obj interface{} + oldObj interface{} +} + type Controller struct { delegator Delegator queue workqueue.RateLimitingInterface @@ -42,7 +58,7 @@ func NewController(stopCh <-chan struct{}, delegator Delegator, informer cache.S key, err := cache.MetaNamespaceKeyFunc(obj) if err == nil { - controller.queue.Add(key) + controller.queue.Add(InformerCacheObj{key:key, eventType: Add, obj: obj}) } }, @@ -50,14 +66,14 @@ func NewController(stopCh <-chan struct{}, delegator Delegator, informer cache.S log.Debugf("Informer Update: %v", newObj) key, err := cache.MetaNamespaceKeyFunc(newObj) if err == nil { - controller.queue.Add(key) + controller.queue.Add(InformerCacheObj{key:key, eventType: Update, obj: newObj, oldObj: oldObj}) } }, DeleteFunc: func(obj interface{}) { log.Debugf("Informer Delete: %v", obj) key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) if err == nil { - controller.queue.Add(key) + controller.queue.Add(InformerCacheObj{key:key, eventType: Delete, obj: obj}) } }, }) @@ -94,40 +110,37 @@ func (c *Controller) runWorker() { } func (c *Controller) processNextItem() bool { - depName, quit := c.queue.Get() + item, quit := c.queue.Get() if quit { return false } - defer c.queue.Done(depName) + defer c.queue.Done(item) - err := c.processItem(depName.(string)) + err := c.processItem(item.(InformerCacheObj)) if err == nil { // No error, reset the ratelimit counters - c.queue.Forget(depName) - } else if c.queue.NumRequeues(depName) < maxRetries { - log.Errorf("Error processing %s (will retry): %v", depName, err) - c.queue.AddRateLimited(depName) + c.queue.Forget(item) + } else if c.queue.NumRequeues(item) < maxRetries { + log.Errorf("Error processing %s (will retry): %v", item, err) + c.queue.AddRateLimited(item) } else { - log.Errorf("Error processing %s (giving up): %v", depName, err) - c.queue.Forget(depName) + log.Errorf("Error processing %s (giving up): %v", item, err) + c.queue.Forget(item) utilruntime.HandleError(err) } return true } -func (c *Controller) processItem(name string) error { - obj, exists, err := c.informer.GetIndexer().GetByKey(name) - if err != nil { - return fmt.Errorf("controller: error fetching object %s error: %v", name, err) - } +func (c *Controller) processItem(informerCacheObj InformerCacheObj) error { - if exists { - c.delegator.Added(obj) - } else { - c.delegator.Deleted(obj) + if informerCacheObj.eventType == Delete { + c.delegator.Deleted(informerCacheObj.obj) + } else if informerCacheObj.eventType == Update { + c.delegator.Updated(informerCacheObj.obj, informerCacheObj.oldObj) + } else if informerCacheObj.eventType == Add { + c.delegator.Added(informerCacheObj.obj) } - return nil } diff --git a/admiral/pkg/controller/admiral/dependency.go b/admiral/pkg/controller/admiral/dependency.go index 2dd1503a..638dd550 100644 --- a/admiral/pkg/controller/admiral/dependency.go +++ b/admiral/pkg/controller/admiral/dependency.go @@ -3,7 +3,6 @@ package admiral import ( "fmt" "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" "sync" @@ -56,17 +55,6 @@ func (d *depCache) Delete(dep *v1.Dependency) { delete(d.cache, d.getKey(dep)) } -func (d *DependencyController) GetDependencies() ([]v1.Dependency, error) { - - deps := d.DepCrdClient.AdmiralV1().Dependencies(meta_v1.NamespaceAll) - dl, err := deps.List(meta_v1.ListOptions{}) - - if err != nil { - return nil, err - } - return dl.Items, err -} - func NewDependencyController(stopCh <-chan struct{}, handler DepHandler, configPath string, namespace string, resyncPeriod time.Duration) (*DependencyController, error) { depController := DependencyController{} @@ -108,13 +96,14 @@ func (d *DependencyController) Added(ojb interface{}) { d.DepHandler.Added(dep) } -func (d *DependencyController) Updated(ojb interface{}) { - dep := ojb.(*v1.Dependency) +func (d *DependencyController) Updated(obj interface{}, oldObj interface{}) { + dep := obj.(*v1.Dependency) d.Cache.Put(dep) - d.DepHandler.Added(dep) + d.DepHandler.Updated(dep) } func (d *DependencyController) Deleted(ojb interface{}) { dep := ojb.(*v1.Dependency) + d.Cache.Delete(dep) d.DepHandler.Deleted(dep) } diff --git a/admiral/pkg/controller/admiral/dependency_test.go b/admiral/pkg/controller/admiral/dependency_test.go index ffbc46b1..afc6c5c0 100644 --- a/admiral/pkg/controller/admiral/dependency_test.go +++ b/admiral/pkg/controller/admiral/dependency_test.go @@ -1,7 +1,11 @@ package admiral import ( + "github.com/google/go-cmp/cmp" + "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/model" + v1 "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" "github.com/istio-ecosystem/admiral/admiral/pkg/test" + v12 "k8s.io/apimachinery/pkg/apis/meta/v1" "testing" "time" ) @@ -20,3 +24,55 @@ func TestNewDependencyController(t *testing.T) { t.Errorf("Dependency controller should never be nil without an error thrown") } } + +func TestDependencyAddUpdateAndDelete(t *testing.T) { + stop := make(chan struct{}) + handler := test.MockDependencyHandler{} + + dependencyController, err := NewDependencyController(stop, &handler, "../../test/resources/admins@fake-cluster.k8s.local", "ns", time.Duration(1000)) + + if err != nil { + t.Errorf("Unexpected err %v", err) + } + + if dependencyController == nil { + t.Errorf("Dependency controller should never be nil without an error thrown") + } + + //test add + depName := "dep1" + dep := model.Dependency{IdentityLabel: "identity", Destinations:[]string{"greeting", "payments"}, Source: "webapp"} + depObj := makeK8sDependencyObj(depName, "namespace1", dep) + dependencyController.Added(depObj) + + newDepObj := dependencyController.Cache.Get(depName) + + if !cmp.Equal(depObj.Spec, newDepObj.Spec) { + t.Errorf("dep update failed, expected: %v got %v", depObj, newDepObj) + } + + //test update + updatedDep := model.Dependency{IdentityLabel: "identity", Destinations:[]string{"greeting", "payments", "newservice"}, Source: "webapp"} + updatedObj := makeK8sDependencyObj(depName, "namespace1", updatedDep) + dependencyController.Updated(makeK8sDependencyObj(depName, "namespace1", updatedDep), depObj) + + updatedDepObj := dependencyController.Cache.Get(depName) + + if !cmp.Equal(updatedObj.Spec, updatedDepObj.Spec) { + t.Errorf("dep update failed, expected: %v got %v", updatedObj, updatedDepObj) + } + + //test delete + dependencyController.Deleted(updatedDepObj) + + deletedDepObj := dependencyController.Cache.Get(depName) + + if deletedDepObj != nil { + t.Errorf("dep delete failed") + } + +} + +func makeK8sDependencyObj (name string, namespace string, dependency model.Dependency) *v1.Dependency { + return &v1.Dependency{Spec: dependency, ObjectMeta: v12.ObjectMeta{Name: name, Namespace: namespace}} +} diff --git a/admiral/pkg/controller/admiral/deployment.go b/admiral/pkg/controller/admiral/deployment.go index 2a576881..f6e57eb3 100644 --- a/admiral/pkg/controller/admiral/deployment.go +++ b/admiral/pkg/controller/admiral/deployment.go @@ -42,13 +42,6 @@ type deploymentCache struct { mutex *sync.Mutex } -func (p *deploymentCache) Put(deploymentEntry *DeploymentClusterEntry) { - defer p.mutex.Unlock() - p.mutex.Lock() - - p.cache[deploymentEntry.Identity] = deploymentEntry -} - func (p *deploymentCache) getKey(deployment *k8sAppsV1.Deployment) string { return common.GetDeploymentGlobalIdentifier(deployment) } @@ -57,12 +50,6 @@ func (p *deploymentCache) Get(key string) *DeploymentClusterEntry { return p.cache[key] } -func (p *deploymentCache) Delete(pod *DeploymentClusterEntry) { - defer p.mutex.Unlock() - p.mutex.Lock() - delete(p.cache, pod.Identity) -} - func (p *deploymentCache) AppendDeploymentToCluster(key string, deployment *k8sAppsV1.Deployment) { defer p.mutex.Unlock() p.mutex.Lock() @@ -158,18 +145,25 @@ func NewDeploymentController(stopCh <-chan struct{}, handler DeploymentHandler, return &deploymentController, nil } -func (d *DeploymentController) Added(ojb interface{}) { +func (d *DeploymentController) Added(obj interface{}) { + HandleAddUpdateDeployment(obj, d) +} + +func (d *DeploymentController) Updated(obj interface{}, oldObj interface{}) { + HandleAddUpdateDeployment(obj, d) +} + +func HandleAddUpdateDeployment(ojb interface{}, d *DeploymentController) { deployment := ojb.(*k8sAppsV1.Deployment) key := d.Cache.getKey(deployment) if len(key) > 0 && !d.shouldIgnoreBasedOnLabels(deployment) { d.Cache.AppendDeploymentToCluster(key, deployment) d.DeploymentHandler.Added(deployment) } - } func (d *DeploymentController) Deleted(ojb interface{}) { - //TODO deal with this + //TODO } func (d *DeploymentController) shouldIgnoreBasedOnLabels(deployment *k8sAppsV1.Deployment) bool { diff --git a/admiral/pkg/controller/admiral/globaltraffic.go b/admiral/pkg/controller/admiral/globaltraffic.go index 650e6703..7d3b3f3f 100644 --- a/admiral/pkg/controller/admiral/globaltraffic.go +++ b/admiral/pkg/controller/admiral/globaltraffic.go @@ -29,17 +29,6 @@ type GlobalTrafficController struct { clusterName string } -func (g *GlobalTrafficController) GetGlobalTrafficPolicies() ([]v1.GlobalTrafficPolicy, error) { - - gtp := g.CrdClient.AdmiralV1().GlobalTrafficPolicies(meta_v1.NamespaceAll) - dl, err := gtp.List(meta_v1.ListOptions{}) - - if err != nil { - return nil, err - } - return dl.Items, err -} - func NewGlobalTrafficController(stopCh <-chan struct{}, handler GlobalTrafficHandler, configPath *rest.Config, resyncPeriod time.Duration) (*GlobalTrafficController, error) { globalTrafficController := GlobalTrafficController{} @@ -70,7 +59,7 @@ func (d *GlobalTrafficController) Added(ojb interface{}) { d.GlobalTrafficHandler.Added(dep) } -func (d *GlobalTrafficController) Updated(ojb interface{}) { +func (d *GlobalTrafficController) Updated(ojb interface{}, oldObj interface{}) { dep := ojb.(*v1.GlobalTrafficPolicy) d.GlobalTrafficHandler.Updated(dep) } diff --git a/admiral/pkg/controller/admiral/globaltraffic_test.go b/admiral/pkg/controller/admiral/globaltraffic_test.go index 0a7165a4..5f80514c 100644 --- a/admiral/pkg/controller/admiral/globaltraffic_test.go +++ b/admiral/pkg/controller/admiral/globaltraffic_test.go @@ -1,7 +1,11 @@ package admiral import ( + "github.com/google/go-cmp/cmp" + "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/model" + v1 "github.com/istio-ecosystem/admiral/admiral/pkg/apis/admiral/v1" "github.com/istio-ecosystem/admiral/admiral/pkg/test" + v12 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/clientcmd" "testing" "time" @@ -25,3 +29,84 @@ func TestNewGlobalTrafficController(t *testing.T) { t.Errorf("GlobalTraffic controller should never be nil without an error thrown") } } + +func TestGlobalTrafficAddUpdateDelete(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{}) + handler := test.MockGlobalTrafficHandler{} + + globalTrafficController, err := NewGlobalTrafficController(stop, &handler, config, time.Duration(1000)) + + if err != nil { + t.Errorf("Unexpected err %v", err) + } + + if globalTrafficController == nil { + t.Errorf("GlobalTraffic controller should never be nil without an error thrown") + } + + gtpName := "gtp1" + gtp := model.GlobalTrafficPolicy{Selector: map[string]string{"identity": "payments", "env": "e2e"}, Policy:[]*model.TrafficPolicy{}} + gtpObj := makeK8sGtpObj(gtpName, "namespace1", gtp) + globalTrafficController.Added(gtpObj) + + if !cmp.Equal(handler.Obj.Spec, gtpObj.Spec) { + t.Errorf("Add should call the handler with the object") + } + + updatedGtp := model.GlobalTrafficPolicy{Selector: map[string]string{"identity": "payments", "env": "qa"}, Policy:[]*model.TrafficPolicy{}} + updatedGtpObj := makeK8sGtpObj(gtpName, "namespace1", updatedGtp) + + globalTrafficController.Updated(updatedGtpObj, gtpObj) + + if !cmp.Equal(handler.Obj.Spec, updatedGtpObj.Spec) { + t.Errorf("Update should call the handler with the updated object") + } + + globalTrafficController.Deleted(updatedGtpObj) + + if handler.Obj != nil { + t.Errorf("Delete should delete the gtp") + } + +} + +func TestGlobalTrafficGetByLabel(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{}) + handler := test.MockGlobalTrafficHandler{} + + globalTrafficController, err := NewGlobalTrafficController(stop, &handler, config, time.Duration(1000)) + + if err != nil { + t.Errorf("Unexpected err %v", err) + } + + if globalTrafficController == nil { + t.Errorf("GlobalTraffic controller should never be nil without an error thrown") + } + + gtps := globalTrafficController.GetGTPByLabel("payments", "namespace1") + + if gtps != nil || len(gtps) > 0 { + t.Errorf("gtps is not empty") + } +} + +func makeK8sGtpObj (name string, namespace string, gtp model.GlobalTrafficPolicy) *v1.GlobalTrafficPolicy { + return &v1.GlobalTrafficPolicy{ + Spec: gtp, + ObjectMeta: v12.ObjectMeta{Name: name, Namespace: namespace}, + TypeMeta: v12.TypeMeta{ + APIVersion: "admiral.io/v1", + Kind: "GlobalTrafficPolicy", + }} +} + + diff --git a/admiral/pkg/controller/admiral/node.go b/admiral/pkg/controller/admiral/node.go index 6a04764d..28388459 100644 --- a/admiral/pkg/controller/admiral/node.go +++ b/admiral/pkg/controller/admiral/node.go @@ -13,8 +13,6 @@ import ( // Handler interface contains the methods that are required type NodeHandler interface { - Added(obj *k8sV1.Node) - Deleted(obj *k8sV1.Node) } type NodeController struct { @@ -58,10 +56,12 @@ func (p *NodeController) Added(obj interface{}) { if p.Locality == nil { p.Locality = &Locality{Region: common.GetNodeLocality(node)} } - p.NodeHandler.Added(node) +} +func (p *NodeController) Updated(obj interface{}, oldObj interface{}) { + //ignore } func (p *NodeController) Deleted(obj interface{}) { - p.NodeHandler.Deleted(nil) + //ignore } diff --git a/admiral/pkg/controller/admiral/node_test.go b/admiral/pkg/controller/admiral/node_test.go index bb496869..2e1547fd 100644 --- a/admiral/pkg/controller/admiral/node_test.go +++ b/admiral/pkg/controller/admiral/node_test.go @@ -1,7 +1,10 @@ package admiral import ( + "github.com/istio-ecosystem/admiral/admiral/pkg/controller/common" "github.com/istio-ecosystem/admiral/admiral/pkg/test" + k8sV1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/clientcmd" "testing" ) @@ -24,3 +27,44 @@ func TestNewNodeController(t *testing.T) { t.Errorf("Node controller should never be nil without an error thrown") } } + +func TestNodeAddUpdateDelete(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{}) + handler := test.MockNodeHandler{} + + nodeController, err := NewNodeController(stop, &handler, config) + + if err != nil { + t.Errorf("Unexpected err %v", err) + } + + if nodeController == nil { + t.Errorf("Node controller should never be nil without an error thrown") + } + region := "us-west-2" + nodeObj := &k8sV1.Node {Spec: k8sV1.NodeSpec{}, ObjectMeta: v1.ObjectMeta{Labels: map[string]string {common.NodeRegionLabel: region}}} + + nodeController.Added(nodeObj) + + locality := nodeController.Locality + + if locality.Region != region { + t.Errorf("region expected %v, got: %v", region, locality.Region) + } + + nodeController.Updated(nodeObj, nodeObj) + //update should make no difference + if locality.Region != region { + t.Errorf("region expected %v, got: %v", region, locality.Region) + } + + nodeController.Deleted(nodeObj) + //delete should make no difference + if locality.Region != region { + t.Errorf("region expected %v, got: %v", region, locality.Region) + } +} diff --git a/admiral/pkg/controller/admiral/pod.go b/admiral/pkg/controller/admiral/pod.go index 003818a8..5ba26ebb 100644 --- a/admiral/pkg/controller/admiral/pod.go +++ b/admiral/pkg/controller/admiral/pod.go @@ -162,16 +162,19 @@ func NewPodController(stopCh <-chan struct{}, handler PodHandler, config *rest.C return &podController, nil } -func (d *PodController) Added(ojb interface{}) { - pod := ojb.(*k8sV1.Pod) +func (d *PodController) Added(obj interface{}) { + pod := obj.(*k8sV1.Pod) key := d.Cache.getKey(pod) if len(key) > 0 && pod.Labels[d.labelSet.DeploymentAnnotation] == "true" { d.Cache.AppendPodToCluster(key, pod) d.PodHandler.Added(pod) } +} +func (d *PodController) Updated(obj interface{}, oldObj interface{}) { + //ignore } func (d *PodController) Deleted(ojb interface{}) { - //TODO deal with this + //TODO } diff --git a/admiral/pkg/controller/admiral/service.go b/admiral/pkg/controller/admiral/service.go index 5b0fde3a..0b4e3a4f 100644 --- a/admiral/pkg/controller/admiral/service.go +++ b/admiral/pkg/controller/admiral/service.go @@ -15,12 +15,10 @@ import ( ) const IstioIngressServiceName = "istio-ingressgateway" -const IstioSystemNameSpace = "istio-system" // Handler interface contains the methods that are required type ServiceHandler interface { - Added(obj *k8sV1.Service) - Deleted(obj *k8sV1.Service) + } type ServiceClusterEntry struct { @@ -134,10 +132,17 @@ func NewServiceController(stopCh <-chan struct{}, handler ServiceHandler, config return &serviceController, nil } -func (s *ServiceController) Added(ojb interface{}) { - service := ojb.(*k8sV1.Service) +func (s *ServiceController) Added(obj interface{}) { + HandleAddUpdateService(obj, s) +} + +func (s *ServiceController) Updated(obj interface{}, oldObj interface{}) { + HandleAddUpdateService(obj, s) +} + +func HandleAddUpdateService(obj interface{}, s *ServiceController) { + service := obj.(*k8sV1.Service) s.Cache.Put(service) - s.ServiceHandler.Added(service) } func (s *ServiceController) Deleted(ojb interface{}) { @@ -145,6 +150,5 @@ func (s *ServiceController) Deleted(ojb interface{}) { //TODO figure this out //d.Cache.Delete(dep) - s.ServiceHandler.Deleted(nil) } diff --git a/admiral/pkg/controller/common/common_test.go b/admiral/pkg/controller/common/common_test.go index b8e02848..c989569f 100644 --- a/admiral/pkg/controller/common/common_test.go +++ b/admiral/pkg/controller/common/common_test.go @@ -26,6 +26,8 @@ func init() { ClusterRegistriesNamespace: "default", DependenciesNamespace: "default", SecretResolver: "", + WorkloadSidecarName: "default", + WorkloadSidecarUpdate: "disabled", } diff --git a/admiral/pkg/controller/common/config_test.go b/admiral/pkg/controller/common/config_test.go index 276004df..069b8c0a 100644 --- a/admiral/pkg/controller/common/config_test.go +++ b/admiral/pkg/controller/common/config_test.go @@ -72,5 +72,20 @@ func TestConfigManagement(t *testing.T) { if GetGlobalTrafficDeploymentLabel() != "identity" { t.Fatalf("GTP Deployment label mismatch. Expected identity, got %v", GetGlobalTrafficDeploymentLabel()) } + if GetGlobalTrafficDeploymentLabel() != "identity" { + t.Fatalf("GTP Deployment label mismatch. Expected identity, got %v", GetGlobalTrafficDeploymentLabel()) + } + if GetWorkloadSidecarName() != "default" { + t.Fatalf("Workload Sidecar Name mismatch. Expected default, got %v", GetWorkloadSidecarName()) + } + if GetWorkloadSidecarUpdate() != "disabled" { + t.Fatalf("Workload Sidecar Update mismatch. Expected disabled, got %v", GetWorkloadSidecarUpdate()) + } + + SetKubeconfigPath("/mypath/custom/kubeconfig") + + if GetKubeconfigPath() != "/mypath/custom/kubeconfig" { + t.Fatalf("Workload Sidecar Name mismatch. Expected /mypath/custom/kubeconfig, got %v", GetKubeconfigPath()) + } } \ No newline at end of file diff --git a/admiral/pkg/controller/common/types_test.go b/admiral/pkg/controller/common/types_test.go index 9357a30b..b6879c21 100644 --- a/admiral/pkg/controller/common/types_test.go +++ b/admiral/pkg/controller/common/types_test.go @@ -1,6 +1,8 @@ package common import ( + "github.com/google/go-cmp/cmp" + "strings" "testing" ) @@ -39,4 +41,46 @@ func TestMapOfMaps(t *testing.T) { if map3 != nil { t.Fail() } -} \ No newline at end of file +} + +func TestEgressMap(t *testing.T) { + egressMap := NewSidecarEgressMap() + payments, orders := "payments", "orders" + paymentsEnv, ordersEnv := "prod", "staging" + paymentsNs, ordersNs := payments + "-" + paymentsEnv, orders + "-" + ordersEnv + paymentsFqdn, ordersFqdn := payments + "." + paymentsNs + "." + "svc.cluster.local", orders + "." + ordersNs + "." + "svc.cluster.local" + paymentsSidecar, ordersSidecar := SidecarEgress{FQDN: paymentsFqdn, Namespace: paymentsNs}, SidecarEgress{FQDN: ordersFqdn, Namespace: ordersNs} + egressMap.Put(payments, paymentsNs, paymentsFqdn) + egressMap.Put(orders, ordersNs, ordersFqdn) + + ordersEgress := egressMap.Get("orders"); + + if !cmp.Equal(ordersEgress[ordersNs], ordersSidecar) { + t.Errorf("Orders egress object should match expected %v, got %v", ordersSidecar, ordersEgress[ordersNs]) + t.FailNow() + } + + egressMap.Delete(orders); + ordersEgress = egressMap.Get("orders"); + + if ordersEgress != nil { + t.Errorf("Delete object should delete the object %v", ordersEgress) + t.FailNow() + } + + egressMapForIter := egressMap.Map() + + if len(egressMapForIter) != 1 { + t.Errorf("Egressmap should contains only one object %v", paymentsSidecar) + t.FailNow() + } +} + +func TestAdmiralParams(t *testing.T) { + admiralParams := AdmiralParams{SANPrefix:"custom.san.prefix"} + admiralParamsStr := admiralParams.String() + expectedContainsStr := "SANPrefix=custom.san.prefix" + if !strings.Contains(admiralParamsStr, expectedContainsStr) { + t.Errorf("AdmiralParams String doesn't have the expected Stringified value expected to contain %v", expectedContainsStr) + } +} diff --git a/admiral/pkg/controller/istio/destinationrule.go b/admiral/pkg/controller/istio/destinationrule.go index 44209630..6cd02c80 100644 --- a/admiral/pkg/controller/istio/destinationrule.go +++ b/admiral/pkg/controller/istio/destinationrule.go @@ -32,11 +32,6 @@ type DestinationRuleController struct { ctl *admiral.Controller } -func (d *ServiceEntryController) GetDestinationRules() ([]*networking.DestinationRule, error) { - //TODO - return nil, nil -} - func NewDestinationRuleController(stopCh <-chan struct{}, handler DestinationRuleHandler, config *rest.Config, resyncPeriod time.Duration) (*DestinationRuleController, error) { drController := DestinationRuleController{} @@ -63,13 +58,13 @@ func (sec *DestinationRuleController) Added(ojb interface{}) { sec.DestinationRuleHandler.Added(dr) } -func (sec *DestinationRuleController) Updated(ojb interface{}) { +func (sec *DestinationRuleController) Updated(ojb interface{}, oldObj interface{}) { dr := ojb.(*networking.DestinationRule) - sec.DestinationRuleHandler.Added(dr) + sec.DestinationRuleHandler.Updated(dr) } func (sec *DestinationRuleController) Deleted(ojb interface{}) { dr := ojb.(*networking.DestinationRule) - sec.DestinationRuleHandler.Added(dr) + sec.DestinationRuleHandler.Deleted(dr) } diff --git a/admiral/pkg/controller/istio/destinationrule_test.go b/admiral/pkg/controller/istio/destinationrule_test.go index 553bb8af..9f30fa3e 100644 --- a/admiral/pkg/controller/istio/destinationrule_test.go +++ b/admiral/pkg/controller/istio/destinationrule_test.go @@ -1,7 +1,11 @@ package istio import ( + "github.com/google/go-cmp/cmp" "github.com/istio-ecosystem/admiral/admiral/pkg/test" + v1alpha32 "istio.io/api/networking/v1alpha3" + "istio.io/client-go/pkg/apis/networking/v1alpha3" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/clientcmd" "testing" "time" @@ -24,4 +28,25 @@ func TestNewDestinationRuleController(t *testing.T) { if destinationRuleController == nil { t.Errorf("DestinationRule controller should never be nil without an error thrown") } + + dstRule := &v1alpha3.DestinationRule{Spec: v1alpha32.DestinationRule{}, ObjectMeta: v1.ObjectMeta{Name: "dr1", Namespace: "namespace1"}} + + destinationRuleController.Added(dstRule) + + if !cmp.Equal(dstRule.Spec, handler.Obj.Spec) { + t.Errorf("Handler should have the added obj") + } + + updatedDstRule := &v1alpha3.DestinationRule{Spec: v1alpha32.DestinationRule{Host: "hello.global"}, ObjectMeta: v1.ObjectMeta{Name: "dr1", Namespace: "namespace1"}} + destinationRuleController.Updated(updatedDstRule, dstRule) + + if !cmp.Equal(updatedDstRule.Spec, handler.Obj.Spec) { + t.Errorf("Handler should have the updated obj") + } + + destinationRuleController.Deleted(dstRule) + + if handler.Obj != nil { + t.Errorf("Handler should have no obj") + } } \ No newline at end of file diff --git a/admiral/pkg/controller/istio/serviceentry.go b/admiral/pkg/controller/istio/serviceentry.go index d1442023..3cc45270 100644 --- a/admiral/pkg/controller/istio/serviceentry.go +++ b/admiral/pkg/controller/istio/serviceentry.go @@ -32,11 +32,6 @@ type ServiceEntryController struct { ctl *admiral.Controller } -func (d *ServiceEntryController) GetServiceEntries() ([]*networking.ServiceEntry, error) { - //TODO - return nil, nil -} - func NewServiceEntryController(stopCh <-chan struct{}, handler ServiceEntryHandler, config *rest.Config, resyncPeriod time.Duration) (*ServiceEntryController, error) { seController := ServiceEntryController{} @@ -63,13 +58,13 @@ func (sec *ServiceEntryController) Added(ojb interface{}) { sec.ServiceEntryHandler.Added(se) } -func (sec *ServiceEntryController) Updated(ojb interface{}) { +func (sec *ServiceEntryController) Updated(ojb interface{}, oldObj interface{}) { se := ojb.(*networking.ServiceEntry) - sec.ServiceEntryHandler.Added(se) + sec.ServiceEntryHandler.Updated(se) } func (sec *ServiceEntryController) Deleted(ojb interface{}) { se := ojb.(*networking.ServiceEntry) - sec.ServiceEntryHandler.Added(se) + sec.ServiceEntryHandler.Deleted(se) } diff --git a/admiral/pkg/controller/istio/serviceentry_test.go b/admiral/pkg/controller/istio/serviceentry_test.go index ef8ccb23..f0722da8 100644 --- a/admiral/pkg/controller/istio/serviceentry_test.go +++ b/admiral/pkg/controller/istio/serviceentry_test.go @@ -1,7 +1,11 @@ package istio import ( + "github.com/google/go-cmp/cmp" "github.com/istio-ecosystem/admiral/admiral/pkg/test" + "istio.io/api/networking/v1alpha3" + v1alpha32 "istio.io/client-go/pkg/apis/networking/v1alpha3" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/clientcmd" "testing" "time" @@ -24,4 +28,25 @@ func TestNewServiceEntryController(t *testing.T) { if serviceEntryController == nil { t.Errorf("ServiceEntry controller should never be nil without an error thrown") } + + serviceEntry := &v1alpha32.ServiceEntry{Spec: v1alpha3.ServiceEntry{}, ObjectMeta: v1.ObjectMeta{Name: "se1", Namespace: "namespace1"}} + + serviceEntryController.Added(serviceEntry) + + if !cmp.Equal(serviceEntry.Spec, handler.Obj.Spec) { + t.Errorf("Handler should have the added obj") + } + + updatedServiceEntry := &v1alpha32.ServiceEntry{Spec: v1alpha3.ServiceEntry{Hosts:[]string{"hello.global"}}, ObjectMeta: v1.ObjectMeta{Name: "se1", Namespace: "namespace1"}} + serviceEntryController.Updated(updatedServiceEntry, serviceEntry) + + if !cmp.Equal(updatedServiceEntry.Spec, handler.Obj.Spec) { + t.Errorf("Handler should have the updated obj") + } + + serviceEntryController.Deleted(serviceEntry) + + if handler.Obj != nil { + t.Errorf("Handler should have no obj") + } } \ No newline at end of file diff --git a/admiral/pkg/controller/istio/sidecar.go b/admiral/pkg/controller/istio/sidecar.go index fa961ab8..6c27539c 100644 --- a/admiral/pkg/controller/istio/sidecar.go +++ b/admiral/pkg/controller/istio/sidecar.go @@ -32,11 +32,6 @@ type SidecarController struct { ctl *admiral.Controller } -func (d *SidecarController) GetSidecars() ([]*networking.Sidecar, error) { - //TODO - return nil, nil -} - func NewSidecarController(stopCh <-chan struct{}, handler SidecarHandler, config *rest.Config, resyncPeriod time.Duration) (*SidecarController, error) { sidecarController := SidecarController{} @@ -63,13 +58,13 @@ func (sec *SidecarController) Added(ojb interface{}) { sec.SidecarHandler.Added(sidecar) } -func (sec *SidecarController) Updated(ojb interface{}) { +func (sec *SidecarController) Updated(ojb interface{}, oldObj interface{}) { sidecar := ojb.(*networking.Sidecar) - sec.SidecarHandler.Added(sidecar) + sec.SidecarHandler.Updated(sidecar) } func (sec *SidecarController) Deleted(ojb interface{}) { sidecar := ojb.(*networking.Sidecar) - sec.SidecarHandler.Added(sidecar) + sec.SidecarHandler.Deleted(sidecar) } diff --git a/admiral/pkg/controller/istio/sidecar_test.go b/admiral/pkg/controller/istio/sidecar_test.go index 81f462ee..8a38666f 100644 --- a/admiral/pkg/controller/istio/sidecar_test.go +++ b/admiral/pkg/controller/istio/sidecar_test.go @@ -1,7 +1,11 @@ package istio import ( + "github.com/google/go-cmp/cmp" "github.com/istio-ecosystem/admiral/admiral/pkg/test" + v1alpha32 "istio.io/api/networking/v1alpha3" + "istio.io/client-go/pkg/apis/networking/v1alpha3" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/clientcmd" "testing" "time" @@ -24,4 +28,25 @@ func TestNewSidecarController(t *testing.T) { if sidecarController == nil { t.Errorf("Sidecar controller should never be nil without an error thrown") } + + sc := &v1alpha3.Sidecar{Spec: v1alpha32.Sidecar{}, ObjectMeta: v1.ObjectMeta{Name: "sc1", Namespace: "namespace1"}} + + sidecarController.Added(sc) + + if !cmp.Equal(sc.Spec, handler.Obj.Spec) { + t.Errorf("Handler should have the added obj") + } + + updatedSc := &v1alpha3.Sidecar{Spec: v1alpha32.Sidecar{WorkloadSelector: &v1alpha32.WorkloadSelector{Labels:map[string]string{"this": "that"}}}, ObjectMeta: v1.ObjectMeta{Name: "sc1", Namespace: "namespace1"}} + sidecarController.Updated(updatedSc, sc) + + if !cmp.Equal(updatedSc.Spec, handler.Obj.Spec) { + t.Errorf("Handler should have the updated obj") + } + + sidecarController.Deleted(sc) + + if handler.Obj != nil { + t.Errorf("Handler should have no obj") + } } diff --git a/admiral/pkg/controller/istio/virtualservice.go b/admiral/pkg/controller/istio/virtualservice.go index 081b6fae..c97c747b 100644 --- a/admiral/pkg/controller/istio/virtualservice.go +++ b/admiral/pkg/controller/istio/virtualservice.go @@ -53,13 +53,13 @@ func (sec *VirtualServiceController) Added(ojb interface{}) { sec.VirtualServiceHandler.Added(dr) } -func (sec *VirtualServiceController) Updated(ojb interface{}) { +func (sec *VirtualServiceController) Updated(ojb interface{}, oldObj interface{}) { dr := ojb.(*networking.VirtualService) - sec.VirtualServiceHandler.Added(dr) + sec.VirtualServiceHandler.Updated(dr) } func (sec *VirtualServiceController) Deleted(ojb interface{}) { dr := ojb.(*networking.VirtualService) - sec.VirtualServiceHandler.Added(dr) + sec.VirtualServiceHandler.Deleted(dr) } diff --git a/admiral/pkg/controller/istio/virtualservice_test.go b/admiral/pkg/controller/istio/virtualservice_test.go index 7edbeaea..9741c53c 100644 --- a/admiral/pkg/controller/istio/virtualservice_test.go +++ b/admiral/pkg/controller/istio/virtualservice_test.go @@ -1,7 +1,11 @@ package istio import ( + "github.com/google/go-cmp/cmp" "github.com/istio-ecosystem/admiral/admiral/pkg/test" + v1alpha32 "istio.io/api/networking/v1alpha3" + "istio.io/client-go/pkg/apis/networking/v1alpha3" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/clientcmd" "testing" "time" @@ -24,4 +28,26 @@ func TestNewVirtualServiceController(t *testing.T) { if virtualServiceController == nil { t.Errorf("VirtualService controller should never be nil without an error thrown") } + + vs := &v1alpha3.VirtualService{Spec: v1alpha32.VirtualService{}, ObjectMeta: v1.ObjectMeta{Name: "vs1", Namespace: "namespace1"}} + + virtualServiceController.Added(vs) + + if !cmp.Equal(vs.Spec, handler.Obj.Spec) { + t.Errorf("Handler should have the added obj") + } + + updatedVs := &v1alpha3.VirtualService{Spec: v1alpha32.VirtualService{Hosts:[]string{"hello.global"}}, ObjectMeta: v1.ObjectMeta{Name: "vs1", Namespace: "namespace1"}} + + virtualServiceController.Updated(updatedVs, vs) + + if !cmp.Equal(updatedVs.Spec, handler.Obj.Spec) { + t.Errorf("Handler should have the updated obj") + } + + virtualServiceController.Deleted(vs) + + if handler.Obj != nil { + t.Errorf("Handler should have no obj") + } } \ No newline at end of file diff --git a/admiral/pkg/controller/util/util_test.go b/admiral/pkg/controller/util/util_test.go index 23bbe4c8..8727ffa1 100644 --- a/admiral/pkg/controller/util/util_test.go +++ b/admiral/pkg/controller/util/util_test.go @@ -94,3 +94,38 @@ func TestSubset(t *testing.T) { } } + +func TestContains(t *testing.T) { + t.Parallel() + var a1 = []string {"env", "stage", "version"} + var a2 = []string {"hello"} + + testCases := []struct { + name string + array []string + str string + result bool + }{ + { + name: "a1 contains stage", + array: a1, + str: "stage", + result: true, + }, + { + name: "a2 doesn't contain stage", + array: a2, + str: "stage", + result: false, + }, + } + + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + result := Contains(c.array, c.str) + if c.result != result { + t.Errorf("Wanted result: %v, got: %v", c.result, result) + } + }) + } +} diff --git a/admiral/pkg/test/mock.go b/admiral/pkg/test/mock.go index 89512186..b1dff86c 100644 --- a/admiral/pkg/test/mock.go +++ b/admiral/pkg/test/mock.go @@ -58,17 +58,19 @@ func (m MockPodHandler) Deleted(obj *k8sCoreV1.Pod) { } type MockNodeHandler struct { + Obj *k8sCoreV1.Node } func (m *MockNodeHandler) Added(obj *k8sCoreV1.Node) { - + m.Obj = obj } func (m *MockNodeHandler) Deleted(obj *k8sCoreV1.Node) { - + m.Obj = nil } type MockDependencyHandler struct { + } func (m *MockDependencyHandler) Added(obj *v1.Dependency) { @@ -84,76 +86,81 @@ func (m *MockDependencyHandler) Deleted(obj *v1.Dependency) { } type MockGlobalTrafficHandler struct { + Obj *v1.GlobalTrafficPolicy } func (m *MockGlobalTrafficHandler) Added(obj *v1.GlobalTrafficPolicy) { - + m.Obj = obj } func (m *MockGlobalTrafficHandler) Updated(obj *v1.GlobalTrafficPolicy) { - + m.Obj = obj } func (m *MockGlobalTrafficHandler) Deleted(obj *v1.GlobalTrafficPolicy) { - + m.Obj = nil } type MockServiceEntryHandler struct { + Obj *v1alpha32.ServiceEntry } func (m *MockServiceEntryHandler) Added(obj *v1alpha32.ServiceEntry) { - + m.Obj = obj } func (m *MockServiceEntryHandler) Updated(obj *v1alpha32.ServiceEntry) { - + m.Obj = obj } func (m *MockServiceEntryHandler) Deleted(obj *v1alpha32.ServiceEntry) { - + m.Obj = nil } type MockVirtualServiceHandler struct { + Obj *v1alpha32.VirtualService } func (m *MockVirtualServiceHandler) Added(obj *v1alpha32.VirtualService) { - + m.Obj = obj } func (m *MockVirtualServiceHandler) Updated(obj *v1alpha32.VirtualService) { - + m.Obj = obj } func (m *MockVirtualServiceHandler) Deleted(obj *v1alpha32.VirtualService) { - + m.Obj = nil } type MockDestinationRuleHandler struct { + Obj *v1alpha32.DestinationRule } func (m *MockDestinationRuleHandler) Added(obj *v1alpha32.DestinationRule) { - + m.Obj = obj } func (m *MockDestinationRuleHandler) Updated(obj *v1alpha32.DestinationRule) { - + m.Obj = obj } func (m *MockDestinationRuleHandler) Deleted(obj *v1alpha32.DestinationRule) { - + m.Obj = nil } type MockSidecarHandler struct { + Obj *v1alpha32.Sidecar } func (m *MockSidecarHandler) Added(obj *v1alpha32.Sidecar) { - + m.Obj = obj } func (m *MockSidecarHandler) Updated(obj *v1alpha32.Sidecar) { - + m.Obj = obj } func (m *MockSidecarHandler) Deleted(obj *v1alpha32.Sidecar) { - + m.Obj = nil } diff --git a/docs/Compatibility.md b/docs/Compatibility.md index 5454d947..4187abc5 100644 --- a/docs/Compatibility.md +++ b/docs/Compatibility.md @@ -6,6 +6,8 @@ The below information is based on the testing done, please submit a PR if you ha | Admiral Version | Min. Istio Version | Max. Istio Version | Min. K8s Version | Max. K8s Version |:-----------------:|:---------------------:|:---------------------:|:-----------------:|:-----------------: v0.1-beta | 1.2.3 | 1.4.6 | 1.13 | 1.14 +v0.9 | 1.2.3 | 1.5.1 | 1.13 | 1.16 + ## Admiral feature support by Istio Version @@ -13,3 +15,13 @@ v0.1-beta | 1.2.3 | 1.4.6 | 1.13 |:-----------------:|:---------:|:-------------:|:--------------------: v0.1-beta | Yes | Yes | No v0.9 | Yes | Yes | Yes + + +## Tested cloud vendors + +| Admiral Version | Cloud vendor +|:-----------------:|:---------: +v0.1-beta | AWS +v0.9 | AWS + +`Note`: Please submit a PR if admiral was tested on other cloud vendors \ No newline at end of file diff --git a/docs/Examples.md b/docs/Examples.md index 261fccfc..ee0c6e64 100644 --- a/docs/Examples.md +++ b/docs/Examples.md @@ -77,7 +77,7 @@ So you have to point DNS resolution for names ending in `global` to point to `Cl ``` #Run the below script for having coredns point to istiocoredns for dns lookups of names ending in global -./admiral-install-v0.1-beta/scripts/redirect-dns.sh +$ADMIRAL_HOME/scripts/redirect-dns.sh ``` #### Remove envoy cluster rewrite filter @@ -101,13 +101,15 @@ kubectl delete envoyfilter istio-multicluster-ingressgateway -n istio-system wget https://github.com/istio-ecosystem/admiral/releases/download/v0.1-beta/admiral-install-v0.1-beta.tar.gz tar xvf admiral-install-v0.1-beta.tar.gz + +export ADMIRAL_HOME=./admiral-install-v0.1-beta ``` ``` #Install admiral -kubectl apply -f ./admiral-install-v0.1-beta/yaml/remotecluster.yaml -kubectl apply -f ./admiral-install-v0.1-beta/yaml/demosinglecluster.yaml +kubectl apply -f $ADMIRAL_HOME/yaml/remotecluster.yaml +kubectl apply -f $ADMIRAL_HOME/yaml/demosinglecluster.yaml #Verify admiral is running @@ -118,7 +120,7 @@ kubectl get pods -n admiral #Create the secret for admiral to monitor. #Since this is for a single cluster demo the remote and local context are the same -./admiral-install-v0.1-beta/scripts/cluster-secret.sh $KUBECONFIG $KUBECONFIG admiral +$ADMIRAL_HOME/scripts/cluster-secret.sh $KUBECONFIG $KUBECONFIG admiral ``` ``` #Verify the secret @@ -130,12 +132,12 @@ kubectl get secrets -n admiral ``` #Install test services -kubectl apply -f ./admiral-install-v0.1-beta/yaml/sample.yaml +kubectl apply -f $ADMIRAL_HOME/yaml/sample.yaml ``` ``` #Install the dependency CR (this is optional) -kubectl apply -f ./admiral-install-v0.1-beta/yaml/sample_dep.yaml +kubectl apply -f $ADMIRAL_HOME/yaml/sample_dep.yaml #Verify that admiral created service names for 'greeting' service @@ -223,7 +225,7 @@ export CLUSTER_2= # Switch kubectx to Cluster 2 export KUBECONFIG=$CLUSTER_2 # Create admiral role and bindings on Cluster 2 -kubectl apply -f ./admiral-install-v0.1-beta/yaml/remotecluster.yaml +kubectl apply -f $ADMIRAL_HOME/yaml/remotecluster.yaml ``` ``` @@ -231,7 +233,7 @@ kubectl apply -f ./admiral-install-v0.1-beta/yaml/remotecluster.yaml export KUBECONFIG=$CLUSTER_1 # Create the k8s secret for admiral to monitor Cluster 2. -./admiral-install-v0.1-beta/scripts/cluster-secret.sh $CLUSTER_1 $CLUSTER_2 admiral +$ADMIRAL_HOME/scripts/cluster-secret.sh $CLUSTER_1 $CLUSTER_2 admiral ``` At this point, admiral is watching `Cluster 2` @@ -243,7 +245,7 @@ export KUBECONFIG=$CLUSTER_2 #Install test services in Cluster 2 -kubectl apply -f ./admiral-install-v0.1-beta/yaml/remotecluster_sample.yaml +kubectl apply -f $ADMIRAL_HOME/yaml/remotecluster_sample.yaml ``` #### Verify @@ -263,3 +265,12 @@ Now run the below request multiple times and see the requests being load balance ``` kubectl exec --namespace=sample -it $(kubectl get pod -l "app=webapp" --namespace=sample -o jsonpath='{.items[0].metadata.name}') -c webapp -- curl -v http://default.greeting.global ``` + +### Cleanup + +Run the following script to cleanup admiral and its associated resources + +```bash +$ADMIRAL_HOME/scripts/cleanup.sh +``` + diff --git a/install/scripts/cleanup.sh b/install/scripts/cleanup.sh new file mode 100755 index 00000000..f9d205bf --- /dev/null +++ b/install/scripts/cleanup.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +while true; do + clustername=$(kubectl config current-context) + printf "k8s cluster: %s\n" "$clustername" + printf "Namespaces ['admiral','admiral-sync'] will be deleted.\nDo you wish to proceed?\n" + options="Please enter yes/Y/y or no/N/n" + echo $options + read -p "" yn + case $yn in + [Yy]* ) kubectl delete namespace admiral; kubectl delete namespace admiral-sync; break;; + [Nn]* ) exit;; + * ) echo $options;; + esac +done From b521a9938c037acbc0dd13e6353c2d97f1b0c757 Mon Sep 17 00:00:00 2001 From: Madeline Date: Mon, 20 Apr 2020 12:14:34 -0700 Subject: [PATCH 43/45] changed test Signed-off-by: Madeline --- .../secret/secretcontroller_test.go | 152 +++++++++++++++++- go.mod | 1 + 2 files changed, 152 insertions(+), 1 deletion(-) diff --git a/admiral/pkg/controller/secret/secretcontroller_test.go b/admiral/pkg/controller/secret/secretcontroller_test.go index 6a66cc4b..5c112e2b 100644 --- a/admiral/pkg/controller/secret/secretcontroller_test.go +++ b/admiral/pkg/controller/secret/secretcontroller_test.go @@ -16,10 +16,14 @@ package secret import ( "context" + "fmt" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "sync" "testing" "time" + . "github.com/onsi/gomega" "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" @@ -34,6 +38,13 @@ const secretNameSpace string = "istio-system" var testCreateControllerCalled bool var testDeleteControllerCalled bool +var ( + mu sync.Mutex + added string + updated string + deleted string +) + func testCreateController(clientConfig *rest.Config, clusterID string, resyncPeriod time.Duration) error { testCreateControllerCalled = true return nil @@ -71,7 +82,15 @@ func deleteMultiClusterSecret(k8s *fake.Clientset) error { } func mockLoadKubeConfig(kubeconfig []byte) (*clientcmdapi.Config, error) { - return &clientcmdapi.Config{}, nil + config := clientcmdapi.NewConfig() + config.Clusters["clean"] = &clientcmdapi.Cluster{ + Server: "https://anything.com:8080", + } + config.Contexts["clean"] = &clientcmdapi.Context{ + Cluster: "clean", + } + config.CurrentContext = "clean" + return config, nil } func verifyControllerDeleted(t *testing.T, timeoutName string) { @@ -86,6 +105,7 @@ func verifyControllerCreated(t *testing.T, timeoutName string) { }) } +/* func Test_SecretController(t *testing.T) { LoadKubeConfig = mockLoadKubeConfig @@ -127,4 +147,134 @@ func Test_SecretController(t *testing.T) { if testCreateControllerCalled != false { t.Fatalf("Test failed on delete secret, create callback function called") } +}*/ + +func resetCallbackData() { + added = "" + updated = "" + deleted = "" +} + +func addCallback(config *rest.Config, id string, resyncPeriod time.Duration) error { + mu.Lock() + defer mu.Unlock() + added = id + return nil +} + +func deleteCallback(id string) error { + mu.Lock() + defer mu.Unlock() + deleted = id + return nil +} + +func TestMock(t *testing.T) { + config := clientcmdapi.NewConfig() + config.Clusters["clean"] = &clientcmdapi.Cluster{ + Server: "https://anything.com:8080", + } + /* + config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{ + Token: "the-token", + }*/ + config.Contexts["clean"] = &clientcmdapi.Context{ + Cluster: "clean", + //AuthInfo: "clean", + } + config.CurrentContext = "clean" + clientConfig := NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{}) + fmt.Println(clientConfig) + restConfig, err := clientConfig.ClientConfig() + fmt.Println(err) + fmt.Println(restConfig) +} + +func makeSecret(secret, clusterID string, kubeconfig []byte) *v1.Secret { + return &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secret, + Namespace: secretNameSpace, + Labels: map[string]string{ + filterLabel: "true", + }, + }, + Data: map[string][]byte{ + clusterID: kubeconfig, + }, + } +} + +func Test_SecretController(t *testing.T) { + g := NewWithT(t) + + LoadKubeConfig = mockLoadKubeConfig + //NewDefaultClientConfig = mockNewDefaultClientConfig + + clientset := fake.NewSimpleClientset() + + var ( + secret0 = makeSecret("s0", "c0", []byte("kubeconfig0-0")) + //secret0UpdateKubeconfigSame = makeSecret("s0", "c0", []byte("kubeconfig0-1")) + secret1 = makeSecret("s1", "c1", []byte("kubeconfig1-0")) + ) + //secret0UpdateKubeconfigSame.Annotations = map[string]string{"foo": "bar"} + + steps := []struct { + // only set one of these per step. The others should be nil. + add *v1.Secret + delete *v1.Secret + + // only set one of these per step. The others should be empty. + wantAdded string + wantDeleted string + }{ + {add: secret0, wantAdded: "c0"}, + {add: secret1, wantAdded: "c1"}, + {delete: secret0, wantDeleted: "c0"}, + {delete: secret1, wantDeleted: "c1"}, + } + + // Start the secret controller and sleep to allow secret process to start. + g.Expect( + StartSecretController(clientset, addCallback, deleteCallback, secretNameSpace, context.TODO(), "")). + Should(Succeed()) + + for i, step := range steps { + resetCallbackData() + + t.Run(fmt.Sprintf("[%v]", i), func(t *testing.T) { + g := NewWithT(t) + + switch { + case step.add != nil: + _, err := clientset.CoreV1().Secrets(secretNameSpace).Create(step.add) + g.Expect(err).Should(BeNil()) + case step.delete != nil: + g.Expect(clientset.CoreV1().Secrets(secretNameSpace).Delete(step.delete.Name, &metav1.DeleteOptions{})). + Should(Succeed()) + } + + switch { + case step.wantAdded != "": + g.Eventually(func() string { + mu.Lock() + defer mu.Unlock() + return added + }, 10*time.Second).Should(Equal(step.wantAdded)) + case step.wantDeleted != "": + g.Eventually(func() string { + mu.Lock() + defer mu.Unlock() + return deleted + }, 10*time.Second).Should(Equal(step.wantDeleted)) + default: + g.Consistently(func() bool { + mu.Lock() + defer mu.Unlock() + return added == "" && updated == "" && deleted == "" + }).Should(Equal(true)) + } + }) + } } diff --git a/go.mod b/go.mod index e928bea7..4af7a5ec 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/mailru/easyjson v0.7.1 // indirect github.com/natefinch/lumberjack v0.0.0-20170531160350-a96e63847dc3 // indirect github.com/onsi/ginkgo v1.10.2 // indirect + github.com/onsi/gomega v1.7.0 github.com/prometheus/common v0.7.0 // indirect github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v0.0.5 From 6d93deeaa80bbc6296e139d29a27f3b31213021c Mon Sep 17 00:00:00 2001 From: Madeline Date: Mon, 20 Apr 2020 15:40:26 -0700 Subject: [PATCH 44/45] local test passed, need to double check in cluyster Signed-off-by: Madeline --- .../pkg/controller/secret/secretcontroller.go | 129 ++++++++++++------ .../secret/secretcontroller_test.go | 118 ++++++++-------- 2 files changed, 145 insertions(+), 102 deletions(-) diff --git a/admiral/pkg/controller/secret/secretcontroller.go b/admiral/pkg/controller/secret/secretcontroller.go index 43c0ab99..a3954c25 100644 --- a/admiral/pkg/controller/secret/secretcontroller.go +++ b/admiral/pkg/controller/secret/secretcontroller.go @@ -47,6 +47,9 @@ var LoadKubeConfig = clientcmd.Load // addSecretCallback prototype for the add secret callback function. type addSecretCallback func(config *rest.Config, dataKey string, resyncPeriod time.Duration) error +// updateSecretCallback prototype for the update secret callback function. +type updateSecretCallback func(config *rest.Config, dataKey string, resyncPeriod time.Duration) error + // removeSecretCallback prototype for the remove secret callback function. type removeSecretCallback func(dataKey string) error @@ -58,6 +61,7 @@ type Controller struct { queue workqueue.RateLimitingInterface informer cache.SharedIndexInformer addCallback addSecretCallback + updateCallback updateSecretCallback removeCallback removeSecretCallback secretResolver resolver.SecretResolver } @@ -86,6 +90,7 @@ func NewController( namespace string, cs *ClusterStore, addCallback addSecretCallback, + updateCallback updateSecretCallback, removeCallback removeSecretCallback, secretResolverType string) *Controller { @@ -126,6 +131,7 @@ func NewController( informer: secretsInformer, queue: queue, addCallback: addCallback, + updateCallback: updateCallback, removeCallback: removeCallback, secretResolver: secretResolver, } @@ -179,10 +185,17 @@ func (c *Controller) Run(stopCh <-chan struct{}) { } // StartSecretController creates the secret controller. -func StartSecretController(k8s kubernetes.Interface, addCallback addSecretCallback, removeCallback removeSecretCallback, namespace string, ctx context.Context, secretResolverType string) error { +func StartSecretController( + k8s kubernetes.Interface, + addCallback addSecretCallback, + updateCallback updateSecretCallback, + removeCallback removeSecretCallback, + namespace string, + ctx context.Context, + secretResolverType string) error { clusterStore := newClustersStore() - controller := NewController(k8s, namespace, clusterStore, addCallback, removeCallback, secretResolverType) + controller := NewController(k8s, namespace, clusterStore, addCallback, updateCallback, removeCallback, secretResolverType) go controller.Run(ctx.Done()) @@ -234,59 +247,91 @@ func (c *Controller) processItem(secretName string) error { return nil } +func (c *Controller) createRemoteCluster(kubeConfig []byte, secretName string, clusterID string, namespace string) (*RemoteCluster, *rest.Config, error) { + if len(kubeConfig) == 0 { + log.Infof("Data '%s' in the secret %s in namespace %s is empty, and disregarded ", + clusterID, secretName, namespace) + return nil, nil, errors.New("kubeconfig is empty") + } + + kubeConfig, err := c.secretResolver.FetchKubeConfig(clusterID, kubeConfig) + + if err != nil { + log.Errorf("Failed to fetch kubeconfig for cluster '%s' using secret resolver: %v, err: %v", + clusterID, c.secretResolver, err) + return nil, nil, errors.New("kubeconfig cannot be fetched") + } + + clusterConfig, err := LoadKubeConfig(kubeConfig) + + if err != nil { + log.Infof("Data '%s' in the secret %s in namespace %s is not a kubeconfig: %v", + clusterID, secretName, namespace, err) + log.Infof("KubeConfig: '%s'", string(kubeConfig)) + return nil, nil, errors.New("clusterConfig cannot be loaded") + } + + clientConfig := clientcmd.NewDefaultClientConfig(*clusterConfig, &clientcmd.ConfigOverrides{}) + + var restConfig *rest.Config + restConfig, err = clientConfig.ClientConfig() + + if err != nil { + log.Errorf("error during conversion of secret to client config: %v", err) + return nil, nil, errors.New("restConfig cannot be built") + } + + return &RemoteCluster{ + secretName: secretName, + }, restConfig, nil +} + func (c *Controller) addMemberCluster(secretName string, s *corev1.Secret) { for clusterID, kubeConfig := range s.Data { // clusterID must be unique even across multiple secrets - if _, ok := c.cs.remoteClusters[clusterID]; !ok { - log.Infof("Adding new cluster member: %s", clusterID) - c.cs.remoteClusters[clusterID] = &RemoteCluster{} - c.cs.remoteClusters[clusterID].secretName = secretName - } else { - log.Infof("Cluster %s in the secret %s in namespace %s already exists. Reloading secret...", - clusterID, c.cs.remoteClusters[clusterID].secretName, s.ObjectMeta.Namespace) - } - - if len(kubeConfig) == 0 { - log.Infof("Data '%s' in the secret %s in namespace %s is empty, and disregarded ", - clusterID, secretName, s.ObjectMeta.Namespace) - continue - } + if prev, ok := c.cs.remoteClusters[clusterID]; !ok { + log.Infof("Adding cluster_id=%v from secret=%v", clusterID, secretName) - kubeConfig, err := c.secretResolver.FetchKubeConfig(clusterID, kubeConfig) + remoteCluster, restConfig, err := c.createRemoteCluster(kubeConfig, secretName, clusterID, s.ObjectMeta.Namespace) - if err != nil { - log.Errorf("Failed to fetch kubeconfig for cluster '%s' using secret resolver: %v, err: %v", - clusterID, c.secretResolver, err) - continue - } + if err != nil { + log.Errorf("Failed to add remote cluster from secret=%v for cluster_id=%v: %v", + secretName, clusterID, err) + continue + } - clusterConfig, err := LoadKubeConfig(kubeConfig) + c.cs.remoteClusters[clusterID] = remoteCluster - if err != nil { - log.Infof("Data '%s' in the secret %s in namespace %s is not a kubeconfig: %v", - clusterID, secretName, s.ObjectMeta.Namespace, err) - log.Infof("KubeConfig: '%s'", string(kubeConfig)) - continue - } + if err := c.addCallback(restConfig, clusterID, 2 * time.Minute); err != nil { + log.Errorf("error during secret loading for clusterID: %s %v", clusterID, err) + continue + } - clientConfig := clientcmd.NewDefaultClientConfig(*clusterConfig, &clientcmd.ConfigOverrides{}) + log.Infof("Secret loaded for cluster %s in the secret %s in namespace %s.",clusterID,c.cs.remoteClusters[clusterID].secretName, s.ObjectMeta.Namespace) - var restConfig *rest.Config - restConfig, err = clientConfig.ClientConfig() + } else { + if prev.secretName != secretName { + log.Errorf("ClusterID reused in two different secrets: %v and %v. ClusterID "+ + "must be unique across all secrets", prev.secretName, secretName) + continue + } - if err != nil { - log.Errorf("error during conversion of secret to client config: %v", err) - continue - } + log.Infof("Updating cluster %v from secret %v", clusterID, secretName) - err = c.addCallback(restConfig, clusterID, 2 * time.Minute) + remoteCluster, restConfig, err := c.createRemoteCluster(kubeConfig, secretName, clusterID, s.ObjectMeta.Namespace) + if err != nil { + log.Errorf("Error updating cluster_id=%v from secret=%v: %v", + clusterID, secretName, err) + continue + } - if err != nil { - log.Errorf("error during secret loading for clusterID: %s %v", clusterID, err) - continue - }else{ - log.Infof("Secret loaded for cluster %s in the secret %s in namespace %s.",clusterID,c.cs.remoteClusters[clusterID].secretName, s.ObjectMeta.Namespace) + c.cs.remoteClusters[clusterID] = remoteCluster + if err := c.updateCallback(restConfig, clusterID, 2 * time.Minute); err != nil { + log.Errorf("Error updating cluster_id from secret=%v: %s %v", + clusterID, secretName, err) + } } + } log.Infof("Number of remote clusters: %d", len(c.cs.remoteClusters)) } diff --git a/admiral/pkg/controller/secret/secretcontroller_test.go b/admiral/pkg/controller/secret/secretcontroller_test.go index 5c112e2b..61f17bba 100644 --- a/admiral/pkg/controller/secret/secretcontroller_test.go +++ b/admiral/pkg/controller/secret/secretcontroller_test.go @@ -18,7 +18,6 @@ import ( "context" "fmt" "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" "sync" "testing" "time" @@ -38,6 +37,22 @@ const secretNameSpace string = "istio-system" var testCreateControllerCalled bool var testDeleteControllerCalled bool + +func makeSecret(secret, clusterID string, kubeconfig []byte) *v1.Secret { + return &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secret, + Namespace: secretNameSpace, + Labels: map[string]string{ + filterLabel: "true", + }, + }, + Data: map[string][]byte{ + clusterID: kubeconfig, + }, + } +} + var ( mu sync.Mutex added string @@ -45,6 +60,34 @@ var ( deleted string ) +func addCallback(config *rest.Config, id string, resyncPeriod time.Duration) error { + mu.Lock() + defer mu.Unlock() + added = id + return nil +} + +func updateCallback(config *rest.Config, id string, resyncPeriod time.Duration) error { + mu.Lock() + defer mu.Unlock() + updated = id + return nil +} + +func deleteCallback(id string) error { + mu.Lock() + defer mu.Unlock() + deleted = id + return nil +} + +func resetCallbackData() { + added = "" + updated = "" + deleted = "" +} + + func testCreateController(clientConfig *rest.Config, clusterID string, resyncPeriod time.Duration) error { testCreateControllerCalled = true return nil @@ -149,87 +192,33 @@ func Test_SecretController(t *testing.T) { } }*/ -func resetCallbackData() { - added = "" - updated = "" - deleted = "" -} - -func addCallback(config *rest.Config, id string, resyncPeriod time.Duration) error { - mu.Lock() - defer mu.Unlock() - added = id - return nil -} - -func deleteCallback(id string) error { - mu.Lock() - defer mu.Unlock() - deleted = id - return nil -} - -func TestMock(t *testing.T) { - config := clientcmdapi.NewConfig() - config.Clusters["clean"] = &clientcmdapi.Cluster{ - Server: "https://anything.com:8080", - } - /* - config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{ - Token: "the-token", - }*/ - config.Contexts["clean"] = &clientcmdapi.Context{ - Cluster: "clean", - //AuthInfo: "clean", - } - config.CurrentContext = "clean" - clientConfig := NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{}) - fmt.Println(clientConfig) - restConfig, err := clientConfig.ClientConfig() - fmt.Println(err) - fmt.Println(restConfig) -} - -func makeSecret(secret, clusterID string, kubeconfig []byte) *v1.Secret { - return &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: secret, - Namespace: secretNameSpace, - Labels: map[string]string{ - filterLabel: "true", - }, - }, - Data: map[string][]byte{ - clusterID: kubeconfig, - }, - } -} func Test_SecretController(t *testing.T) { g := NewWithT(t) LoadKubeConfig = mockLoadKubeConfig - //NewDefaultClientConfig = mockNewDefaultClientConfig clientset := fake.NewSimpleClientset() var ( secret0 = makeSecret("s0", "c0", []byte("kubeconfig0-0")) - //secret0UpdateKubeconfigSame = makeSecret("s0", "c0", []byte("kubeconfig0-1")) + secret0UpdateKubeconfigChanged = makeSecret("s0", "c0", []byte("kubeconfig0-1")) secret1 = makeSecret("s1", "c1", []byte("kubeconfig1-0")) ) - //secret0UpdateKubeconfigSame.Annotations = map[string]string{"foo": "bar"} steps := []struct { // only set one of these per step. The others should be nil. add *v1.Secret + update *v1.Secret delete *v1.Secret // only set one of these per step. The others should be empty. wantAdded string + wantUpdated string wantDeleted string }{ {add: secret0, wantAdded: "c0"}, + {update: secret0UpdateKubeconfigChanged, wantUpdated: "c0"}, {add: secret1, wantAdded: "c1"}, {delete: secret0, wantDeleted: "c0"}, {delete: secret1, wantDeleted: "c1"}, @@ -237,7 +226,7 @@ func Test_SecretController(t *testing.T) { // Start the secret controller and sleep to allow secret process to start. g.Expect( - StartSecretController(clientset, addCallback, deleteCallback, secretNameSpace, context.TODO(), "")). + StartSecretController(clientset, addCallback, updateCallback, deleteCallback, secretNameSpace, context.TODO(), "")). Should(Succeed()) for i, step := range steps { @@ -250,6 +239,9 @@ func Test_SecretController(t *testing.T) { case step.add != nil: _, err := clientset.CoreV1().Secrets(secretNameSpace).Create(step.add) g.Expect(err).Should(BeNil()) + case step.update != nil: + _, err := clientset.CoreV1().Secrets(secretNameSpace).Update(step.update) + g.Expect(err).Should(BeNil()) case step.delete != nil: g.Expect(clientset.CoreV1().Secrets(secretNameSpace).Delete(step.delete.Name, &metav1.DeleteOptions{})). Should(Succeed()) @@ -262,6 +254,12 @@ func Test_SecretController(t *testing.T) { defer mu.Unlock() return added }, 10*time.Second).Should(Equal(step.wantAdded)) + case step.wantUpdated != "": + g.Eventually(func() string { + mu.Lock() + defer mu.Unlock() + return updated + }, 10*time.Second).Should(Equal(step.wantUpdated)) case step.wantDeleted != "": g.Eventually(func() string { mu.Lock() From c3f534b7f024adcdd56b523e253aeb9b7221113b Mon Sep 17 00:00:00 2001 From: Madeline Date: Mon, 20 Apr 2020 16:24:15 -0700 Subject: [PATCH 45/45] finish adding call back function for update Signed-off-by: Madeline --- admiral/pkg/clusters/registry.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/admiral/pkg/clusters/registry.go b/admiral/pkg/clusters/registry.go index f9f03fef..986bf1ec 100644 --- a/admiral/pkg/clusters/registry.go +++ b/admiral/pkg/clusters/registry.go @@ -88,6 +88,7 @@ func createSecretController(ctx context.Context, w *RemoteRegistry) error { err = secret.StartSecretController(w.secretClient, w.createCacheController, + w.updateCacheController, w.deleteCacheController, common.GetClusterRegistriesNamespace(), ctx, common.GetSecretResolver()) @@ -182,6 +183,13 @@ func (r *RemoteRegistry) createCacheController(clientConfig *rest.Config, cluste return nil } +func (r *RemoteRegistry) updateCacheController(clientConfig *rest.Config, clusterID string, resyncPeriod time.Duration) error { + if err := r.deleteCacheController(clusterID); err != nil { + return err + } + return r.createCacheController(clientConfig, clusterID, resyncPeriod) +} + func (r *RemoteRegistry) deleteCacheController(clusterID string) error { controller, ok := r.remoteControllers[clusterID]