From 8e22bef255ae3c92760b906e01886704dfbe6c4d Mon Sep 17 00:00:00 2001 From: Kalya Subramanian <42158129+ksubrmnn@users.noreply.github.com> Date: Mon, 19 Oct 2020 15:04:41 -0700 Subject: [PATCH] cli: Add enforce single mesh functionality to osm install (#1854) --- charts/osm/README.md | 2 +- charts/osm/templates/osm-deployment.yaml | 1 + charts/osm/values.yaml | 1 + cmd/cli/install.go | 26 ++++ cmd/cli/install_test.go | 159 +++++++++++++++++++++++ 5 files changed, 188 insertions(+), 1 deletion(-) diff --git a/charts/osm/README.md b/charts/osm/README.md index 389a242917..12f99f2df5 100644 --- a/charts/osm/README.md +++ b/charts/osm/README.md @@ -41,4 +41,4 @@ A Helm chart to install the OSM control plane on Kubernetes | OpenServiceMesh.vault.protocol | string | `"http"` | | | OpenServiceMesh.vault.role | string | `"openservicemesh"` | | | OpenServiceMesh.vault.token | string | `nil` | | - +| OpenServiceMesh.enforceSingleMesh | bool | `"false"` | | diff --git a/charts/osm/templates/osm-deployment.yaml b/charts/osm/templates/osm-deployment.yaml index f6a8e7cafc..8de7615e32 100644 --- a/charts/osm/templates/osm-deployment.yaml +++ b/charts/osm/templates/osm-deployment.yaml @@ -5,6 +5,7 @@ metadata: labels: app: osm-controller meshName: {{ .Values.OpenServiceMesh.meshName }} + {{ if .Values.OpenServiceMesh.enforceSingleMesh }}enforceSingleMesh: "true"{{ end }} spec: replicas: {{ .Values.OpenServiceMesh.replicaCount }} selector: diff --git a/charts/osm/values.yaml b/charts/osm/values.yaml index bef160a198..5b2967496e 100644 --- a/charts/osm/values.yaml +++ b/charts/osm/values.yaml @@ -37,6 +37,7 @@ OpenServiceMesh: meshName: osm useHTTPSIngress: false envoyLogLevel: error + enforceSingleMesh: false # Set deployJaeger to true to deploy a Jaeger cluster in the # namespace where OSM resides. diff --git a/cmd/cli/install.go b/cmd/cli/install.go index 6b597a93c0..bd2b554856 100644 --- a/cmd/cli/install.go +++ b/cmd/cli/install.go @@ -101,6 +101,9 @@ type installCmd struct { // Toggle this to enable/disable the automatic deployment of Jaeger deployJaeger bool + + // Toggle this to enforce only one mesh in this cluster + enforceSingleMesh bool } func newInstallCmd(config *helm.Configuration, out io.Writer) *cobra.Command { @@ -152,6 +155,7 @@ func newInstallCmd(config *helm.Configuration, out io.Writer) *cobra.Command { f.StringVar(&inst.meshName, "mesh-name", defaultMeshName, "name for the new control plane instance") f.BoolVar(&inst.deployJaeger, "deploy-jaeger", true, "Deploy Jaeger in the namespace of the OSM controller") f.StringVar(&inst.envoyLogLevel, "envoy-log-level", "error", "Envoy log level is used to specify the level of logs collected from envoy and needs to be one of these (trace, debug, info, warning, warn, error, critical, off)") + f.BoolVar(&inst.enforceSingleMesh, "enforce-single-mesh", false, "Enforce only deploying one mesh in the cluster") return cmd } @@ -218,6 +222,7 @@ func (i *installCmd) resolveValues() (map[string]interface{}, error) { fmt.Sprintf("OpenServiceMesh.enableEgress=%t", i.enableEgress), fmt.Sprintf("OpenServiceMesh.deployJaeger=%t", i.deployJaeger), fmt.Sprintf("OpenServiceMesh.envoyLogLevel=%s", strings.ToLower(i.envoyLogLevel)), + fmt.Sprintf("OpenServiceMesh.enforceSingleMesh=%t", i.enforceSingleMesh), } if i.containerRegistrySecret != "" { @@ -293,6 +298,27 @@ func (i *installCmd) validateOptions() error { return err } + list, err = getControllerDeployments(i.clientSet) + if err != nil { + return err + } + + // Check if single mesh cluster is already specified + for _, deployment := range list.Items { + singleMeshEnforced := deployment.ObjectMeta.Labels["enforceSingleMesh"] == "true" + name := deployment.ObjectMeta.Labels["meshName"] + if singleMeshEnforced { + return errors.Errorf("Cannot install mesh [%s]. Existing mesh [%s] enforces single mesh cluster.", i.meshName, name) + } + } + + // Enforce single mesh cluster if needed + if i.enforceSingleMesh { + if len(list.Items) != 0 { + return errors.Errorf("Meshes already exist in cluster. Cannot enforce single mesh cluster. ") + } + } + return nil } diff --git a/cmd/cli/install_test.go b/cmd/cli/install_test.go index 4b4fe3b007..6ba870aa77 100644 --- a/cmd/cli/install_test.go +++ b/cmd/cli/install_test.go @@ -4,9 +4,12 @@ import ( "bytes" "context" "io/ioutil" + "strings" + "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/stretchr/testify/assert" helm "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chartutil" kubefake "helm.sh/helm/v3/pkg/kube/fake" @@ -143,6 +146,7 @@ var _ = Describe("Running the install command", func() { "enableGrafana": true, "deployJaeger": false, "envoyLogLevel": testEnvoyLogLevel, + "enforceSingleMesh": false, }})) }) @@ -260,6 +264,7 @@ var _ = Describe("Running the install command", func() { "enableGrafana": true, "deployJaeger": false, "envoyLogLevel": testEnvoyLogLevel, + "enforceSingleMesh": false, }})) }) @@ -385,6 +390,7 @@ var _ = Describe("Running the install command", func() { "enableGrafana": true, "deployJaeger": false, "envoyLogLevel": testEnvoyLogLevel, + "enforceSingleMesh": false, }})) }) @@ -551,6 +557,7 @@ var _ = Describe("Running the install command", func() { "enableGrafana": true, "deployJaeger": false, "envoyLogLevel": testEnvoyLogLevel, + "enforceSingleMesh": false, }})) }) @@ -819,6 +826,7 @@ var _ = Describe("Resolving values for install command with vault parameters", f "enableGrafana": true, "deployJaeger": false, "envoyLogLevel": testEnvoyLogLevel, + "enforceSingleMesh": false, }})) }) }) @@ -899,6 +907,7 @@ var _ = Describe("Ensure that grafana is disabled when flag is set to false", fu "enableGrafana": false, "deployJaeger": false, "envoyLogLevel": testEnvoyLogLevel, + "enforceSingleMesh": false, }})) }) @@ -980,6 +989,7 @@ var _ = Describe("Ensure that grafana is enabled when flag is set to true", func "enableGrafana": true, "deployJaeger": false, "envoyLogLevel": testEnvoyLogLevel, + "enforceSingleMesh": false, }})) }) @@ -1061,6 +1071,7 @@ var _ = Describe("Ensure that prometheus is disabled when flag is set to false", "enableGrafana": true, "deployJaeger": false, "envoyLogLevel": testEnvoyLogLevel, + "enforceSingleMesh": false, }})) }) }) @@ -1141,6 +1152,7 @@ var _ = Describe("Resolving values for install command with cert-manager paramet "enableGrafana": true, "deployJaeger": false, "envoyLogLevel": testEnvoyLogLevel, + "enforceSingleMesh": false, }})) }) }) @@ -1262,6 +1274,153 @@ var _ = Describe("Resolving values for service cert validity duration", func() { }) }) +func TestEnforceSingleMesh(t *testing.T) { + assert := assert.New(t) + + out := new(bytes.Buffer) + store := storage.Init(driver.NewMemory()) + if mem, ok := store.Driver.(*driver.Memory); ok { + mem.SetNamespace(settings.Namespace()) + } + + config := &helm.Configuration{ + Releases: store, + KubeClient: &kubefake.PrintingKubeClient{ + Out: ioutil.Discard, + }, + Capabilities: chartutil.DefaultCapabilities, + Log: func(format string, v ...interface{}) {}, + } + + fakeClientSet := fake.NewSimpleClientset() + + install := &installCmd{ + out: out, + chartPath: "testdata/test-chart", + containerRegistry: testRegistry, + osmImageTag: testOsmImageTag, + osmImagePullPolicy: defaultOsmImagePullPolicy, + certificateManager: "tresor", + serviceCertValidityDuration: "24h", + prometheusRetentionTime: testRetentionTime, + meshName: defaultMeshName, + enableEgress: true, + enablePrometheus: true, + enableGrafana: true, + clientSet: fakeClientSet, + envoyLogLevel: testEnvoyLogLevel, + enforceSingleMesh: true, + } + + err := install.run(config) + assert.Nil(err) + assert.Equal(out.String(), "OSM installed successfully in namespace [osm-system] with mesh name [osm]\n") +} + +func TestEnforceSingleMeshRejectsNewMesh(t *testing.T) { + assert := assert.New(t) + + out := new(bytes.Buffer) + store := storage.Init(driver.NewMemory()) + if mem, ok := store.Driver.(*driver.Memory); ok { + mem.SetNamespace(settings.Namespace()) + } + + config := &helm.Configuration{ + Releases: store, + KubeClient: &kubefake.PrintingKubeClient{ + Out: ioutil.Discard, + }, + Capabilities: chartutil.DefaultCapabilities, + Log: func(format string, v ...interface{}) {}, + } + + fakeClientSet := fake.NewSimpleClientset() + + labelMap := make(map[string]string) + labelMap["meshName"] = defaultMeshName + labelMap["app"] = constants.OSMControllerName + labelMap["enforceSingleMesh"] = "true" + + deploymentSpec := &v1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: constants.OSMControllerName, + Namespace: settings.Namespace() + "-existing", + Labels: labelMap, + }, + } + _, err := fakeClientSet.AppsV1().Deployments(settings.Namespace()+"-existing").Create(context.TODO(), deploymentSpec, metav1.CreateOptions{}) + assert.Nil(err) + + install := &installCmd{ + out: out, + chartPath: "testdata/test-chart", + containerRegistry: testRegistry, + osmImageTag: testOsmImageTag, + osmImagePullPolicy: defaultOsmImagePullPolicy, + certificateManager: "tresor", + serviceCertValidityDuration: "24h", + prometheusRetentionTime: testRetentionTime, + meshName: defaultMeshName + "-2", + enableEgress: true, + enablePrometheus: true, + enableGrafana: true, + clientSet: fakeClientSet, + envoyLogLevel: testEnvoyLogLevel, + } + + err = install.run(config) + assert.NotNil(err) + assert.True(strings.Contains(err.Error(), "Cannot install mesh [osm-2]. Existing mesh [osm] enforces single mesh cluster")) +} + +func TestEnforceSingleMeshWithExistingMesh(t *testing.T) { + assert := assert.New(t) + + out := new(bytes.Buffer) + store := storage.Init(driver.NewMemory()) + if mem, ok := store.Driver.(*driver.Memory); ok { + mem.SetNamespace(settings.Namespace()) + } + + config := &helm.Configuration{ + Releases: store, + KubeClient: &kubefake.PrintingKubeClient{ + Out: ioutil.Discard, + }, + Capabilities: chartutil.DefaultCapabilities, + Log: func(format string, v ...interface{}) {}, + } + + fakeClientSet := fake.NewSimpleClientset() + + deploymentSpec := createDeploymentSpec(settings.Namespace()+"-existing", defaultMeshName) + _, err := fakeClientSet.AppsV1().Deployments(settings.Namespace()+"-existing").Create(context.TODO(), deploymentSpec, metav1.CreateOptions{}) + assert.Nil(err) + + install := &installCmd{ + out: out, + chartPath: "testdata/test-chart", + containerRegistry: testRegistry, + osmImageTag: testOsmImageTag, + osmImagePullPolicy: defaultOsmImagePullPolicy, + certificateManager: "tresor", + serviceCertValidityDuration: "24h", + prometheusRetentionTime: testRetentionTime, + meshName: defaultMeshName + "-2", + enableEgress: true, + enablePrometheus: true, + enableGrafana: true, + clientSet: fakeClientSet, + envoyLogLevel: testEnvoyLogLevel, + enforceSingleMesh: true, + } + + err = install.run(config) + assert.NotNil(err) + assert.True(strings.Contains(err.Error(), "Meshes already exist in cluster. Cannot enforce single mesh cluster")) +} + func createDeploymentSpec(namespace, meshName string) *v1.Deployment { labelMap := make(map[string]string) if meshName != "" {