From 3b4e1ed614c0151d6e97a6df43f389726d13f89d Mon Sep 17 00:00:00 2001 From: jaellio Date: Tue, 10 May 2022 16:33:47 -0700 Subject: [PATCH 1/6] feat(certs): create MRC on install Updates the osm-bootstrap to create a default MeshRootCertificate on osm install. Adds a preset-mesh-root-certificate ConfigMap to the Helm templates. The osm-bootstrap will obtain the MRC spec from the ConfigMap and attempt to create a default MRC. If an MRC already exists with complete state and issuing rotationStage then the osm-bootstrap will not create the default MRC. Additional changes: - adds MeshRootCertificate CRD to uninstall list - adds a tokenSecretName value to the osm.vault values Signed-off-by: jaellio --- charts/osm/README.md | 1 + charts/osm/templates/cleanup-hook.yaml | 1 + .../preset-mesh-root-certificate.yaml | 41 +++ charts/osm/values.schema.json | 6 + charts/osm/values.yaml | 2 + cmd/cli/uninstall_mesh.go | 1 + cmd/osm-bootstrap/osm-bootstrap.go | 112 +++++++- cmd/osm-bootstrap/osm-bootstrap_test.go | 262 +++++++++++++++--- 8 files changed, 377 insertions(+), 49 deletions(-) create mode 100644 charts/osm/templates/preset-mesh-root-certificate.yaml diff --git a/charts/osm/README.md b/charts/osm/README.md index 03d48524e5..69257ef22b 100644 --- a/charts/osm/README.md +++ b/charts/osm/README.md @@ -179,6 +179,7 @@ The following table lists the configurable parameters of the osm chart and their | osm.vault.protocol | string | `"http"` | protocol to use to connect to Vault | | osm.vault.role | string | `"openservicemesh"` | Vault role to be used by Open Service Mesh | | osm.vault.token | string | `""` | token that should be used to connect to Vault | +| osm.vault.tokenSecretName | string | `"osm-vault-token"` | The Kubernetes secret name to store the Vault token used in OSM | | osm.webhookConfigNamePrefix | string | `"osm-webhook"` | Prefix used in name of the webhook configuration resources | | smi.validateTrafficTarget | bool | `true` | Enables validation of SMI Traffic Target | diff --git a/charts/osm/templates/cleanup-hook.yaml b/charts/osm/templates/cleanup-hook.yaml index 0760004f20..ea491ed9ae 100644 --- a/charts/osm/templates/cleanup-hook.yaml +++ b/charts/osm/templates/cleanup-hook.yaml @@ -84,6 +84,7 @@ spec: kubectl replace -f /osm-crds; kubectl delete --ignore-not-found meshconfig -n '{{ include "osm.namespace" . }}' osm-mesh-config; kubectl delete --ignore-not-found secret -n '{{ include "osm.namespace" . }}' {{ .Values.osm.caBundleSecretName }}; + kubectl delete --ignore-not-found meshrootcertificate -n '{{ include "osm.namespace" . }}' osm-mesh-root-certificate; kubectl delete mutatingwebhookconfiguration -l app.kubernetes.io/name=openservicemesh.io,app.kubernetes.io/instance={{ .Values.osm.meshName }},app.kubernetes.io/version={{ .Chart.AppVersion }},app=osm-injector --ignore-not-found; kubectl delete validatingwebhookconfiguration -l app.kubernetes.io/name=openservicemesh.io,app.kubernetes.io/instance={{ .Values.osm.meshName }},app.kubernetes.io/version={{ .Chart.AppVersion }},app=osm-controller --ignore-not-found; nodeSelector: diff --git a/charts/osm/templates/preset-mesh-root-certificate.yaml b/charts/osm/templates/preset-mesh-root-certificate.yaml new file mode 100644 index 0000000000..16a87c0693 --- /dev/null +++ b/charts/osm/templates/preset-mesh-root-certificate.yaml @@ -0,0 +1,41 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: preset-mesh-root-certificate + namespace: {{ include "osm.namespace" . }} +data: + preset-mesh-root-certificate.json: | + { + "provider": { + {{- if eq (.Values.osm.certificateProvider.kind | lower) "tresor"}} + "tresor": { + "ca": { + "secretRef": { + "name": {{.Values.osm.caBundleSecretName | mustToJson}}, + "namespace": "{{include "osm.namespace" .}}" + } + } + } + {{- end}} + {{- if eq (.Values.osm.certificateProvider.kind | lower) "certmanager"}} + "certManager": { + "issuerName": {{.Values.osm.certManager.issuerName | mustToJson}}, + "issuerKind": {{.Values.osm.certManager.issuerKind | mustToJson}}, + "issuerGroup": {{.Values.osm.certManager.issuerGroup | mustToJson}} + } + {{- end}} + {{- if eq (.Values.osm.certificateProvider.kind | lower) "vault"}} + "vault": { + "token": { + "secretRef": { + "name": {{.Values.osm.vault.tokenSecretName | mustToJson}}, + "namespace": "{{include "osm.namespace" .}}" + } + }, + "host": {{.Values.osm.vault.host | mustToJson}}, + "role": {{.Values.osm.vault.role | mustToJson}}, + "protocol": {{.Values.osm.vault.protocol | mustToJson}} + } + {{- end}} + } + } diff --git a/charts/osm/values.schema.json b/charts/osm/values.schema.json index 336a5ac10d..e8c67a3a67 100644 --- a/charts/osm/values.schema.json +++ b/charts/osm/values.schema.json @@ -1292,6 +1292,12 @@ "title": "Hashicorp Vault's role schema", "description": "Role to use with Vault", "type": "string" + }, + "tokenSecretName": { + "$id": "#/properties/osm/properties/vault/properties/tokenSecretName", + "title": "Vault token secret name schema", + "description": "Name of the Kubernetes Secret to store the vault token", + "type": "string" } }, "examples": [ diff --git a/charts/osm/values.yaml b/charts/osm/values.yaml index bf6ce7d22d..f4b6fdf972 100644 --- a/charts/osm/values.yaml +++ b/charts/osm/values.yaml @@ -129,6 +129,8 @@ osm: token: "" # -- Vault role to be used by Open Service Mesh role: openservicemesh + # -- The Kubernetes secret name to store the Vault token used in OSM + tokenSecretName: osm-vault-token # # -- cert-manager.io configuration diff --git a/cmd/cli/uninstall_mesh.go b/cmd/cli/uninstall_mesh.go index ff4d358eb9..17b4506649 100644 --- a/cmd/cli/uninstall_mesh.go +++ b/cmd/cli/uninstall_mesh.go @@ -251,6 +251,7 @@ func (d *uninstallMeshCmd) uninstallCustomResourceDefinitions() error { "egresses.policy.openservicemesh.io", "ingressbackends.policy.openservicemesh.io", "meshconfigs.config.openservicemesh.io", + "meshRootCertificate.config.openservicemesh.io", "upstreamtrafficsettings.policy.openservicemesh.io", "retries.policy.openservicemesh.io", "multiclusterservices.config.openservicemesh.io", diff --git a/cmd/osm-bootstrap/osm-bootstrap.go b/cmd/osm-bootstrap/osm-bootstrap.go index 7eca301ebe..a3ff1d44e8 100644 --- a/cmd/osm-bootstrap/osm-bootstrap.go +++ b/cmd/osm-bootstrap/osm-bootstrap.go @@ -44,9 +44,12 @@ import ( ) const ( - meshConfigName = "osm-mesh-config" - presetMeshConfigName = "preset-mesh-config" - presetMeshConfigJSONKey = "preset-mesh-config.json" + meshConfigName = "osm-mesh-config" + presetMeshConfigName = "preset-mesh-config" + presetMeshConfigJSONKey = "preset-mesh-config.json" + meshRootCertificateName = "osm-mesh-root-certificate" + presetMeshRootCertificateName = "preset-mesh-root-certificate" + presetMeshRootCertificateJSONKey = "preset-mesh-root-certificate.json" ) var ( @@ -76,9 +79,9 @@ var ( ) type bootstrap struct { - kubeClient kubernetes.Interface - meshConfigClient configClientset.Interface - namespace string + kubeClient kubernetes.Interface + configClient configClientset.Interface + namespace string } func init() { @@ -156,9 +159,9 @@ func main() { } bootstrap := bootstrap{ - kubeClient: kubeClient, - meshConfigClient: configClient, - namespace: osmNamespace, + kubeClient: kubeClient, + configClient: configClient, + namespace: osmNamespace, } err = bootstrap.ensureMeshConfig() @@ -167,6 +170,12 @@ func main() { return } + err = bootstrap.ensureMeshRootCertificate() + if err != nil { + log.Fatal().Err(err).Msgf("Error setting up default MeshRootCertificate %s from Secret %s", meshRootCertificateName, presetMeshRootCertificateName) + return + } + err = bootstrap.initiatilizeKubernetesEventsRecorder() if err != nil { log.Fatal().Err(err).Msg("Error initializing Kubernetes events recorder") @@ -253,7 +262,7 @@ func (b *bootstrap) createDefaultMeshConfig() error { if err != nil { return err } - if _, err := b.meshConfigClient.ConfigV1alpha2().MeshConfigs(b.namespace).Create(context.TODO(), defaultMeshConfig, metav1.CreateOptions{}); err == nil { + if _, err := b.configClient.ConfigV1alpha2().MeshConfigs(b.namespace).Create(context.TODO(), defaultMeshConfig, metav1.CreateOptions{}); err == nil { log.Info().Msgf("MeshConfig (%s) created in namespace %s", meshConfigName, b.namespace) return nil } @@ -267,7 +276,7 @@ func (b *bootstrap) createDefaultMeshConfig() error { } func (b *bootstrap) ensureMeshConfig() error { - config, err := b.meshConfigClient.ConfigV1alpha2().MeshConfigs(b.namespace).Get(context.TODO(), meshConfigName, metav1.GetOptions{}) + config, err := b.configClient.ConfigV1alpha2().MeshConfigs(b.namespace).Get(context.TODO(), meshConfigName, metav1.GetOptions{}) if apierrors.IsNotFound(err) { // create a default mesh config since it was not found return b.createDefaultMeshConfig() @@ -281,7 +290,7 @@ func (b *bootstrap) ensureMeshConfig() error { if err := util.CreateApplyAnnotation(config, unstructured.UnstructuredJSONScheme); err != nil { return err } - if _, err := b.meshConfigClient.ConfigV1alpha2().MeshConfigs(b.namespace).Update(context.TODO(), config, metav1.UpdateOptions{}); err != nil { + if _, err := b.configClient.ConfigV1alpha2().MeshConfigs(b.namespace).Update(context.TODO(), config, metav1.UpdateOptions{}); err != nil { return err } } @@ -355,3 +364,82 @@ func buildDefaultMeshConfig(presetMeshConfigMap *corev1.ConfigMap) (*configv1alp return config, util.CreateApplyAnnotation(config, unstructured.UnstructuredJSONScheme) } + +func (b *bootstrap) ensureMeshRootCertificate() error { + listOptions := metav1.ListOptions{ + FieldSelector: "status.state=complete,status.rotationStage=issuing", + } + meshRootCertificateList, err := b.configClient.ConfigV1alpha2().MeshRootCertificates(b.namespace).List(context.TODO(), listOptions) + + if len(meshRootCertificateList.Items) == 0 { + // create a MeshRootCertificate since none were found in the complete state and issuing rotationState + return b.createMeshRootCertificate() + } + if err != nil { + return err + } + + meshRootCertificate := meshRootCertificateList.Items[0] + if _, exists := meshRootCertificate.Annotations[corev1.LastAppliedConfigAnnotation]; !exists { + // MeshRootCertificate was found, but may not have the last applied annotation. + if err := util.CreateApplyAnnotation(&meshRootCertificate, unstructured.UnstructuredJSONScheme); err != nil { + return err + } + if _, err := b.configClient.ConfigV1alpha2().MeshRootCertificates(b.namespace).Update(context.TODO(), &meshRootCertificate, metav1.UpdateOptions{}); err != nil { + return err + } + } + return nil +} + +func (b *bootstrap) createMeshRootCertificate() error { + // find preset config map to build the MeshRootCertificate from + presetMeshRootCertificate, err := b.kubeClient.CoreV1().ConfigMaps(b.namespace).Get(context.TODO(), presetMeshRootCertificateName, metav1.GetOptions{}) + // If the preset MeshRootCertificate could not be loaded return the error + if err != nil { + return err + } + + // Create a MeshRootCertificate + defaultMeshConfig, err := buildMeshRootCertificate(presetMeshRootCertificate) + if err != nil { + return err + } + if _, err := b.configClient.ConfigV1alpha2().MeshRootCertificates(b.namespace).Create(context.TODO(), defaultMeshConfig, metav1.CreateOptions{}); err == nil { + log.Info().Msgf("MeshRootCertificate (%s) created in namespace %s", meshConfigName, b.namespace) + return nil + } + + if apierrors.IsAlreadyExists(err) { + log.Info().Msgf("MeshRootCertificate already exists in %s. Skip creating.", b.namespace) + return nil + } + + return err +} + +func buildMeshRootCertificate(presetMeshRootCertificateConfigMap *corev1.ConfigMap) (*configv1alpha2.MeshRootCertificate, error) { + presetMeshRootCertificate := presetMeshRootCertificateConfigMap.Data[presetMeshRootCertificateJSONKey] + presetMeshRootCertificateSpec := configv1alpha2.MeshRootCertificateSpec{} + err := json.Unmarshal([]byte(presetMeshRootCertificate), &presetMeshRootCertificateSpec) + if err != nil { + log.Fatal().Err(err).Msgf("Error converting preset-mesh-root-certificate json string to MeshRootCertificate object") + } + + config := &configv1alpha2.MeshRootCertificate{ + TypeMeta: metav1.TypeMeta{ + Kind: "MeshRootCertificate", + APIVersion: "config.openservicemesh.io/configv1alpha2", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: meshRootCertificateName, + }, + Spec: presetMeshRootCertificateSpec, + Status: configv1alpha2.MeshRootCertificateStatus{ + State: "complete", + RotationStage: "issuing", + }, + } + + return config, util.CreateApplyAnnotation(config, unstructured.UnstructuredJSONScheme) +} diff --git a/cmd/osm-bootstrap/osm-bootstrap_test.go b/cmd/osm-bootstrap/osm-bootstrap_test.go index 6667fe02dd..1e75ac21d2 100644 --- a/cmd/osm-bootstrap/osm-bootstrap_test.go +++ b/cmd/osm-bootstrap/osm-bootstrap_test.go @@ -89,6 +89,56 @@ var testPresetMeshConfigMap *corev1.ConfigMap = &corev1.ConfigMap{ }, } +var testMeshRootCertificate *configv1alpha2.MeshRootCertificate = &configv1alpha2.MeshRootCertificate{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: testNamespace, + Name: meshRootCertificateName, + }, + Spec: configv1alpha2.MeshRootCertificateSpec{}, + Status: configv1alpha2.MeshRootCertificateStatus{ + State: "complete", + RotationStage: "issuing", + }, +} + +var testMeshRootCertificateWithLastAppliedAnnotation *configv1alpha2.MeshRootCertificate = &configv1alpha2.MeshRootCertificate{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: testNamespace, + Name: meshRootCertificateName, + Annotations: map[string]string{ + "kubectl.kubernetes.io/last-applied-configuration": `{"metadata":{"name":"osm-mesh-root-certificate","namespace":"test-namespace","creationTimestamp":null},"spec":{}}`, + }, + }, + Spec: configv1alpha2.MeshRootCertificateSpec{}, + Status: configv1alpha2.MeshRootCertificateStatus{ + State: "complete", + RotationStage: "issuing", + }, +} + +var testPresetMeshRootCertificate *corev1.ConfigMap = &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: presetMeshRootCertificateName, + Namespace: testNamespace, + }, + Data: map[string]string{ + presetMeshRootCertificateJSONKey: `{ +"provider": { + "tresor": { + "secretRef": { + "name": "osm-ca-bundle", + "namespace": "test-namespace" + } + } + } +}`, + }, +} + func TestBuildDefaultMeshConfig(t *testing.T) { assert := tassert.New(t) @@ -109,6 +159,19 @@ func TestBuildDefaultMeshConfig(t *testing.T) { assert.False(meshConfig.Spec.FeatureFlags.EnableRetryPolicy) } +func TestBuildMeshRootCertificate(t *testing.T) { + assert := tassert.New(t) + + meshRootCertificate, err := buildMeshRootCertificate(testPresetMeshRootCertificate) + assert.NoError(err) + assert.Contains(meshRootCertificate.Annotations, "kubectl.kubernetes.io/last-applied-configuration") + assert.Equal(meshRootCertificate.Name, meshRootCertificateName) + assert.Equal(meshRootCertificate.Spec.Provider.Tresor.SecretRef.Name, "osm-ca-bundle") + assert.Equal(meshRootCertificate.Spec.Provider.Tresor.SecretRef.Namespace, testNamespace) + assert.Nil(meshRootCertificate.Spec.Provider.Vault) + assert.Nil(meshRootCertificate.Spec.Provider.CertManager) +} + func TestValidateCLIParams(t *testing.T) { assert := tassert.New(t) @@ -156,7 +219,7 @@ func TestCreateDefaultMeshConfig(t *testing.T) { name string namespace string kubeClient kubernetes.Interface - meshConfigClient configClientset.Interface + configClient configClientset.Interface expectDefaultMeshConfig bool expectErr bool }{ @@ -164,7 +227,7 @@ func TestCreateDefaultMeshConfig(t *testing.T) { name: "successfully create default meshconfig from preset configmap", namespace: testNamespace, kubeClient: fakeKube.NewSimpleClientset([]runtime.Object{testPresetMeshConfigMap}...), - meshConfigClient: fakeConfig.NewSimpleClientset(), + configClient: fakeConfig.NewSimpleClientset(), expectDefaultMeshConfig: true, expectErr: false, }, @@ -172,7 +235,7 @@ func TestCreateDefaultMeshConfig(t *testing.T) { name: "preset configmap does not exist", namespace: testNamespace, kubeClient: fakeKube.NewSimpleClientset(), - meshConfigClient: fakeConfig.NewSimpleClientset(), + configClient: fakeConfig.NewSimpleClientset(), expectDefaultMeshConfig: false, expectErr: true, }, @@ -180,7 +243,7 @@ func TestCreateDefaultMeshConfig(t *testing.T) { name: "default MeshConfig already exists", namespace: testNamespace, kubeClient: fakeKube.NewSimpleClientset([]runtime.Object{testPresetMeshConfigMap}...), - meshConfigClient: fakeConfig.NewSimpleClientset([]runtime.Object{testMeshConfig}...), + configClient: fakeConfig.NewSimpleClientset([]runtime.Object{testMeshConfig}...), expectDefaultMeshConfig: true, expectErr: false, }, @@ -190,9 +253,9 @@ func TestCreateDefaultMeshConfig(t *testing.T) { t.Run(tc.name, func(t *testing.T) { assert := tassert.New(t) b := bootstrap{ - kubeClient: tc.kubeClient, - meshConfigClient: tc.meshConfigClient, - namespace: tc.namespace, + kubeClient: tc.kubeClient, + configClient: tc.configClient, + namespace: tc.namespace, } err := b.createDefaultMeshConfig() @@ -202,7 +265,7 @@ func TestCreateDefaultMeshConfig(t *testing.T) { assert.Nil(err) } - _, err = b.meshConfigClient.ConfigV1alpha2().MeshConfigs(b.namespace).Get(context.TODO(), meshConfigName, metav1.GetOptions{}) + _, err = b.configClient.ConfigV1alpha2().MeshConfigs(b.namespace).Get(context.TODO(), meshConfigName, metav1.GetOptions{}) if tc.expectDefaultMeshConfig { if err == nil { assert.Nil(err) @@ -218,39 +281,39 @@ func TestCreateDefaultMeshConfig(t *testing.T) { func TestEnsureMeshConfig(t *testing.T) { tests := []struct { - name string - namespace string - kubeClient kubernetes.Interface - meshConfigClient configClientset.Interface - expectErr bool + name string + namespace string + kubeClient kubernetes.Interface + configClient configClientset.Interface + expectErr bool }{ { - name: "MeshConfig found with no last-applied annotation", - namespace: testNamespace, - kubeClient: fakeKube.NewSimpleClientset(), - meshConfigClient: fakeConfig.NewSimpleClientset([]runtime.Object{testMeshConfig}...), - expectErr: false, + name: "MeshConfig found with no last-applied annotation", + namespace: testNamespace, + kubeClient: fakeKube.NewSimpleClientset(), + configClient: fakeConfig.NewSimpleClientset([]runtime.Object{testMeshConfig}...), + expectErr: false, }, { - name: "MeshConfig found with last-applied annotation", - namespace: testNamespace, - kubeClient: fakeKube.NewSimpleClientset(), - meshConfigClient: fakeConfig.NewSimpleClientset([]runtime.Object{testMeshConfigWithLastAppliedAnnotation}...), - expectErr: false, + name: "MeshConfig found with last-applied annotation", + namespace: testNamespace, + kubeClient: fakeKube.NewSimpleClientset(), + configClient: fakeConfig.NewSimpleClientset([]runtime.Object{testMeshConfigWithLastAppliedAnnotation}...), + expectErr: false, }, { - name: "MeshConfig not found but successfully created", - namespace: testNamespace, - kubeClient: fakeKube.NewSimpleClientset([]runtime.Object{testPresetMeshConfigMap}...), - meshConfigClient: fakeConfig.NewSimpleClientset(), - expectErr: false, + name: "MeshConfig not found but successfully created", + namespace: testNamespace, + kubeClient: fakeKube.NewSimpleClientset([]runtime.Object{testPresetMeshConfigMap}...), + configClient: fakeConfig.NewSimpleClientset(), + expectErr: false, }, { - name: "MeshConfig not found and error creating it", - namespace: testNamespace, - kubeClient: fakeKube.NewSimpleClientset(), - meshConfigClient: fakeConfig.NewSimpleClientset(), - expectErr: true, + name: "MeshConfig not found and error creating it", + namespace: testNamespace, + kubeClient: fakeKube.NewSimpleClientset(), + configClient: fakeConfig.NewSimpleClientset(), + expectErr: true, }, } @@ -258,9 +321,9 @@ func TestEnsureMeshConfig(t *testing.T) { t.Run(tc.name, func(t *testing.T) { assert := tassert.New(t) b := bootstrap{ - kubeClient: tc.kubeClient, - meshConfigClient: tc.meshConfigClient, - namespace: tc.namespace, + kubeClient: tc.kubeClient, + configClient: tc.configClient, + namespace: tc.namespace, } err := b.ensureMeshConfig() @@ -268,7 +331,132 @@ func TestEnsureMeshConfig(t *testing.T) { assert.NotNil(err) } else { assert.Nil(err) - config, err := b.meshConfigClient.ConfigV1alpha2().MeshConfigs(b.namespace).Get(context.TODO(), meshConfigName, metav1.GetOptions{}) + config, err := b.configClient.ConfigV1alpha2().MeshConfigs(b.namespace).Get(context.TODO(), meshConfigName, metav1.GetOptions{}) + assert.Nil(err) + assert.Contains(config.Annotations, "kubectl.kubernetes.io/last-applied-configuration") + } + }) + } +} + +func TestCreateMeshRootCertificateConfig(t *testing.T) { + tests := []struct { + name string + namespace string + kubeClient kubernetes.Interface + configClient configClientset.Interface + expectDefaultMeshRootCertificate bool + expectErr bool + }{ + { + name: "successfully create default MeshRootCertificate from preset configmap", + namespace: testNamespace, + kubeClient: fakeKube.NewSimpleClientset([]runtime.Object{testPresetMeshRootCertificate}...), + configClient: fakeConfig.NewSimpleClientset(), + expectDefaultMeshRootCertificate: true, + expectErr: false, + }, + { + name: "preset configmap does not exist", + namespace: testNamespace, + kubeClient: fakeKube.NewSimpleClientset(), + configClient: fakeConfig.NewSimpleClientset(), + expectDefaultMeshRootCertificate: false, + expectErr: true, + }, + { + name: "default MeshRootCertificate already exists", + namespace: testNamespace, + kubeClient: fakeKube.NewSimpleClientset([]runtime.Object{testPresetMeshRootCertificate}...), + configClient: fakeConfig.NewSimpleClientset([]runtime.Object{testMeshRootCertificate}...), + expectDefaultMeshRootCertificate: true, + expectErr: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + assert := tassert.New(t) + b := bootstrap{ + kubeClient: tc.kubeClient, + configClient: tc.configClient, + namespace: tc.namespace, + } + + err := b.createMeshRootCertificate() + if tc.expectErr { + assert.NotNil(err) + } else { + assert.Nil(err) + } + + _, err = b.configClient.ConfigV1alpha2().MeshRootCertificates(b.namespace).Get(context.TODO(), meshRootCertificateName, metav1.GetOptions{}) + if tc.expectDefaultMeshRootCertificate { + if err == nil { + assert.Nil(err) + } + } else { + if err == nil { + assert.NotNil(err) + } + } + }) + } +} + +func TestEnsureMeshRootCertificate(t *testing.T) { + tests := []struct { + name string + namespace string + kubeClient kubernetes.Interface + configClient configClientset.Interface + expectErr bool + }{ + { + name: "MeshRootCertificate found with no last-applied annotation", + namespace: testNamespace, + kubeClient: fakeKube.NewSimpleClientset(), + configClient: fakeConfig.NewSimpleClientset([]runtime.Object{testMeshRootCertificate}...), + expectErr: false, + }, + { + name: "MeshRootCertificate found with last-applied annotation", + namespace: testNamespace, + kubeClient: fakeKube.NewSimpleClientset(), + configClient: fakeConfig.NewSimpleClientset([]runtime.Object{testMeshRootCertificateWithLastAppliedAnnotation}...), + expectErr: false, + }, + { + name: "MeshRootCertificate not found but successfully created", + namespace: testNamespace, + kubeClient: fakeKube.NewSimpleClientset([]runtime.Object{testPresetMeshRootCertificate}...), + configClient: fakeConfig.NewSimpleClientset(), + expectErr: false, + }, + { + name: "MeshRootCertificate not found and error creating it", + namespace: testNamespace, + kubeClient: fakeKube.NewSimpleClientset(), + configClient: fakeConfig.NewSimpleClientset(), + expectErr: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + assert := tassert.New(t) + b := bootstrap{ + kubeClient: tc.kubeClient, + configClient: tc.configClient, + namespace: tc.namespace, + } + + err := b.ensureMeshRootCertificate() + if tc.expectErr { + assert.NotNil(err) + } else { + assert.Nil(err) + config, err := b.configClient.ConfigV1alpha2().MeshRootCertificates(b.namespace).Get(context.TODO(), meshRootCertificateName, metav1.GetOptions{}) assert.Nil(err) assert.Contains(config.Annotations, "kubectl.kubernetes.io/last-applied-configuration") } From 56e7537778df9fe0faed60c239c95fd5a16a8231 Mon Sep 17 00:00:00 2001 From: jaellio Date: Thu, 19 May 2022 10:07:43 -0700 Subject: [PATCH 2/6] Rebase Signed-off-by: jaellio --- charts/osm/README.md | 1 + .../templates/preset-mesh-root-certificate.yaml | 6 ++++-- charts/osm/values.schema.json | 6 ++++++ charts/osm/values.yaml | 2 ++ .../crds/config_mesh_root_certificate.yaml | 4 ---- cmd/osm-bootstrap/osm-bootstrap.go | 8 ++++---- cmd/osm-bootstrap/osm-bootstrap_test.go | 12 +++++++----- pkg/apis/config/v1alpha2/meshrootcertificate.go | 3 --- pkg/certificate/providers/config.go | 4 ++-- pkg/constants/constants.go | 10 ++++++++-- tests/framework/common.go | 15 ++++++++++----- tests/framework/types.go | 11 +++++++---- 12 files changed, 51 insertions(+), 31 deletions(-) diff --git a/charts/osm/README.md b/charts/osm/README.md index 69257ef22b..4138089b8d 100644 --- a/charts/osm/README.md +++ b/charts/osm/README.md @@ -179,6 +179,7 @@ The following table lists the configurable parameters of the osm chart and their | osm.vault.protocol | string | `"http"` | protocol to use to connect to Vault | | osm.vault.role | string | `"openservicemesh"` | Vault role to be used by Open Service Mesh | | osm.vault.token | string | `""` | token that should be used to connect to Vault | +| osm.vault.tokenSecretKey | string | `"token"` | The Kubernetes secret key with the value bring the Vault token | | osm.vault.tokenSecretName | string | `"osm-vault-token"` | The Kubernetes secret name to store the Vault token used in OSM | | osm.webhookConfigNamePrefix | string | `"osm-webhook"` | Prefix used in name of the webhook configuration resources | | smi.validateTrafficTarget | bool | `true` | Enables validation of SMI Traffic Target | diff --git a/charts/osm/templates/preset-mesh-root-certificate.yaml b/charts/osm/templates/preset-mesh-root-certificate.yaml index 16a87c0693..9cfbb62270 100644 --- a/charts/osm/templates/preset-mesh-root-certificate.yaml +++ b/charts/osm/templates/preset-mesh-root-certificate.yaml @@ -27,14 +27,16 @@ data: {{- if eq (.Values.osm.certificateProvider.kind | lower) "vault"}} "vault": { "token": { - "secretRef": { + "secretKeyRef": { "name": {{.Values.osm.vault.tokenSecretName | mustToJson}}, + "key": {{.Values.osm.vault.tokenSecretKey | mustToJson}}, "namespace": "{{include "osm.namespace" .}}" } }, "host": {{.Values.osm.vault.host | mustToJson}}, "role": {{.Values.osm.vault.role | mustToJson}}, - "protocol": {{.Values.osm.vault.protocol | mustToJson}} + "protocol": {{.Values.osm.vault.protocol | mustToJson}}, + "port": {{.Values.osm.vault.port | mustToJson}} } {{- end}} } diff --git a/charts/osm/values.schema.json b/charts/osm/values.schema.json index e8c67a3a67..2c5dca0482 100644 --- a/charts/osm/values.schema.json +++ b/charts/osm/values.schema.json @@ -1298,6 +1298,12 @@ "title": "Vault token secret name schema", "description": "Name of the Kubernetes Secret to store the vault token", "type": "string" + }, + "tokenSecretKey": { + "$id": "#/properties/osm/properties/vault/properties/tokenSecretKey", + "title": "Vault token secret key schema", + "description": "Name of the Kubernetes Secret key with the value of the vault token", + "type": "string" } }, "examples": [ diff --git a/charts/osm/values.yaml b/charts/osm/values.yaml index f4b6fdf972..043f03fd3f 100644 --- a/charts/osm/values.yaml +++ b/charts/osm/values.yaml @@ -131,6 +131,8 @@ osm: role: openservicemesh # -- The Kubernetes secret name to store the Vault token used in OSM tokenSecretName: osm-vault-token + # -- The Kubernetes secret key with the value bring the Vault token + tokenSecretKey: token # # -- cert-manager.io configuration diff --git a/cmd/osm-bootstrap/crds/config_mesh_root_certificate.yaml b/cmd/osm-bootstrap/crds/config_mesh_root_certificate.yaml index f94344e0ab..ba5366b6d2 100644 --- a/cmd/osm-bootstrap/crds/config_mesh_root_certificate.yaml +++ b/cmd/osm-bootstrap/crds/config_mesh_root_certificate.yaml @@ -60,14 +60,10 @@ spec: description: Cert-manager provider configuration type: object required: - - secretName - issuerName - issuerKind - issuerGroup properties: - secretName: - description: The name of the kubernetes secret containing the root certificate - type: string issuerName: description: The name of the Issuer or ClusterIssuer resource type: string diff --git a/cmd/osm-bootstrap/osm-bootstrap.go b/cmd/osm-bootstrap/osm-bootstrap.go index a3ff1d44e8..05f40a1727 100644 --- a/cmd/osm-bootstrap/osm-bootstrap.go +++ b/cmd/osm-bootstrap/osm-bootstrap.go @@ -172,7 +172,7 @@ func main() { err = bootstrap.ensureMeshRootCertificate() if err != nil { - log.Fatal().Err(err).Msgf("Error setting up default MeshRootCertificate %s from Secret %s", meshRootCertificateName, presetMeshRootCertificateName) + log.Fatal().Err(err).Msgf("Error setting up default MeshRootCertificate %s from ConfigMap %s", meshRootCertificateName, presetMeshRootCertificateName) return } @@ -372,7 +372,7 @@ func (b *bootstrap) ensureMeshRootCertificate() error { meshRootCertificateList, err := b.configClient.ConfigV1alpha2().MeshRootCertificates(b.namespace).List(context.TODO(), listOptions) if len(meshRootCertificateList.Items) == 0 { - // create a MeshRootCertificate since none were found in the complete state and issuing rotationState + // create a MeshRootCertificate since none were found in the complete state and issuing rotationStage return b.createMeshRootCertificate() } if err != nil { @@ -436,8 +436,8 @@ func buildMeshRootCertificate(presetMeshRootCertificateConfigMap *corev1.ConfigM }, Spec: presetMeshRootCertificateSpec, Status: configv1alpha2.MeshRootCertificateStatus{ - State: "complete", - RotationStage: "issuing", + State: constants.MRCStateComplete, + RotationStage: constants.MRCStageIssuing, }, } diff --git a/cmd/osm-bootstrap/osm-bootstrap_test.go b/cmd/osm-bootstrap/osm-bootstrap_test.go index 1e75ac21d2..ad48821f48 100644 --- a/cmd/osm-bootstrap/osm-bootstrap_test.go +++ b/cmd/osm-bootstrap/osm-bootstrap_test.go @@ -129,9 +129,11 @@ var testPresetMeshRootCertificate *corev1.ConfigMap = &corev1.ConfigMap{ presetMeshRootCertificateJSONKey: `{ "provider": { "tresor": { - "secretRef": { - "name": "osm-ca-bundle", - "namespace": "test-namespace" + "ca": { + "secretRef": { + "name": "osm-ca-bundle", + "namespace": "test-namespace" + } } } } @@ -166,8 +168,8 @@ func TestBuildMeshRootCertificate(t *testing.T) { assert.NoError(err) assert.Contains(meshRootCertificate.Annotations, "kubectl.kubernetes.io/last-applied-configuration") assert.Equal(meshRootCertificate.Name, meshRootCertificateName) - assert.Equal(meshRootCertificate.Spec.Provider.Tresor.SecretRef.Name, "osm-ca-bundle") - assert.Equal(meshRootCertificate.Spec.Provider.Tresor.SecretRef.Namespace, testNamespace) + assert.Equal(meshRootCertificate.Spec.Provider.Tresor.CA.SecretRef.Name, "osm-ca-bundle") + assert.Equal(meshRootCertificate.Spec.Provider.Tresor.CA.SecretRef.Namespace, testNamespace) assert.Nil(meshRootCertificate.Spec.Provider.Vault) assert.Nil(meshRootCertificate.Spec.Provider.CertManager) } diff --git a/pkg/apis/config/v1alpha2/meshrootcertificate.go b/pkg/apis/config/v1alpha2/meshrootcertificate.go index 93a4be49a6..a0f06ae7cb 100644 --- a/pkg/apis/config/v1alpha2/meshrootcertificate.go +++ b/pkg/apis/config/v1alpha2/meshrootcertificate.go @@ -49,9 +49,6 @@ type ProviderSpec struct { // CertManagerProviderSpec defines the configuration of the cert-manager provider type CertManagerProviderSpec struct { - // SecretName specifies the name of the k8s secret containing the root certificate - SecretName string `json:"secretName"` - // IssuerName specifies the name of the Issuer resource IssuerName string `json:"issuerName"` diff --git a/pkg/certificate/providers/config.go b/pkg/certificate/providers/config.go index 4be4f2dfd2..a4e3eb8417 100644 --- a/pkg/certificate/providers/config.go +++ b/pkg/certificate/providers/config.go @@ -57,8 +57,8 @@ func NewCertificateManager(kubeClient kubernetes.Interface, kubeConfig *rest.Con }, // TODO(#4502): Detect if an actual MRC exists, and set the status accordingly. Status: v1alpha2.MeshRootCertificateStatus{ - State: constants.MRCStateValidating, - RotationStage: constants.MRCStageComplete, + State: constants.MRCStateComplete, + RotationStage: constants.MRCStageIssuing, }, }, } diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index df9a9728a3..961d9c20b0 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -166,8 +166,14 @@ const ( // MRCVersionAnnotation is the annotation used for the version of the MeshRootCertificate MRCVersionAnnotation = "openservicemesh.io/mrc-version" - MRCStateValidating = "validating" - MRCStageComplete = "complete" + // MRCStageValidating is the validating status option for the rotation stage of the MeshRootCertificate + MRCStageValidating = "validating" + + // MRCStageIssuing is the issuing status option for the rotation stage of the MeshRootCertificate + MRCStageIssuing = "issuing" + + // MRCStateComplete is the complete status option for the state of the MeshRootCertificate + MRCStateComplete = "complete" ) // Labels used by the control plane diff --git a/tests/framework/common.go b/tests/framework/common.go index 390a8049bb..3b767390fe 100644 --- a/tests/framework/common.go +++ b/tests/framework/common.go @@ -354,10 +354,13 @@ func (td *OsmTestData) GetOSMInstallOpts(options ...InstallOsmOpt) InstallOSMOpt DeployFluentbit: false, EnableReconciler: false, - VaultHost: "vault." + td.OsmNamespace + ".svc.cluster.local", - VaultProtocol: "http", - VaultRole: "openservicemesh", - VaultToken: "token", + VaultHost: "vault." + td.OsmNamespace + ".svc.cluster.local", + VaultProtocol: "http", + VaultPort: 8200, + VaultRole: "openservicemesh", + VaultToken: "token", + VaultTokenSecretName: "osm-vault-token", + VaultTokenSecretKey: "token-key", CertmanagerIssuerGroup: "cert-manager.io", CertmanagerIssuerKind: "Issuer", @@ -519,7 +522,9 @@ func (td *OsmTestData) InstallOSM(instOpts InstallOSMOpts) error { fmt.Sprintf("osm.vault.host=%s", instOpts.VaultHost), fmt.Sprintf("osm.vault.role=%s", instOpts.VaultRole), fmt.Sprintf("osm.vault.protocol=%s", instOpts.VaultProtocol), - fmt.Sprintf("osm.vault.token=%s", instOpts.VaultToken)) + fmt.Sprintf("osm.vault.token=%s", instOpts.VaultToken), + fmt.Sprintf("osm.vault.port=%d", instOpts.VaultPort), + ) // Wait for the vault pod if err := td.WaitForPodsRunningReady(instOpts.ControlPlaneNS, 60*time.Second, 1, nil); err != nil { return errors.Wrap(err, "failed waiting for vault pod to become ready") diff --git a/tests/framework/types.go b/tests/framework/types.go index a4ae502513..146b23b872 100644 --- a/tests/framework/types.go +++ b/tests/framework/types.go @@ -106,10 +106,13 @@ type InstallOSMOpts struct { DeployFluentbit bool EnableReconciler bool - VaultHost string - VaultProtocol string - VaultToken string - VaultRole string + VaultHost string + VaultProtocol string + VaultPort int + VaultToken string + VaultRole string + VaultTokenSecretName string + VaultTokenSecretKey string CertmanagerIssuerGroup string CertmanagerIssuerKind string From 3260809409f1b385ea16e1db3012b6d0b73ec6d8 Mon Sep 17 00:00:00 2001 From: jaellio Date: Fri, 20 May 2022 15:30:44 -0700 Subject: [PATCH 3/6] Updated tests and removed last applied annotation Signed-off-by: jaellio --- cmd/osm-bootstrap/osm-bootstrap.go | 17 +---- cmd/osm-bootstrap/osm-bootstrap_test.go | 86 +++++-------------------- 2 files changed, 18 insertions(+), 85 deletions(-) diff --git a/cmd/osm-bootstrap/osm-bootstrap.go b/cmd/osm-bootstrap/osm-bootstrap.go index 05f40a1727..2b486cdda5 100644 --- a/cmd/osm-bootstrap/osm-bootstrap.go +++ b/cmd/osm-bootstrap/osm-bootstrap.go @@ -370,32 +370,21 @@ func (b *bootstrap) ensureMeshRootCertificate() error { FieldSelector: "status.state=complete,status.rotationStage=issuing", } meshRootCertificateList, err := b.configClient.ConfigV1alpha2().MeshRootCertificates(b.namespace).List(context.TODO(), listOptions) + if err != nil { + return err + } if len(meshRootCertificateList.Items) == 0 { // create a MeshRootCertificate since none were found in the complete state and issuing rotationStage return b.createMeshRootCertificate() } - if err != nil { - return err - } - meshRootCertificate := meshRootCertificateList.Items[0] - if _, exists := meshRootCertificate.Annotations[corev1.LastAppliedConfigAnnotation]; !exists { - // MeshRootCertificate was found, but may not have the last applied annotation. - if err := util.CreateApplyAnnotation(&meshRootCertificate, unstructured.UnstructuredJSONScheme); err != nil { - return err - } - if _, err := b.configClient.ConfigV1alpha2().MeshRootCertificates(b.namespace).Update(context.TODO(), &meshRootCertificate, metav1.UpdateOptions{}); err != nil { - return err - } - } return nil } func (b *bootstrap) createMeshRootCertificate() error { // find preset config map to build the MeshRootCertificate from presetMeshRootCertificate, err := b.kubeClient.CoreV1().ConfigMaps(b.namespace).Get(context.TODO(), presetMeshRootCertificateName, metav1.GetOptions{}) - // If the preset MeshRootCertificate could not be loaded return the error if err != nil { return err } diff --git a/cmd/osm-bootstrap/osm-bootstrap_test.go b/cmd/osm-bootstrap/osm-bootstrap_test.go index ad48821f48..e2d01dc93e 100644 --- a/cmd/osm-bootstrap/osm-bootstrap_test.go +++ b/cmd/osm-bootstrap/osm-bootstrap_test.go @@ -13,6 +13,7 @@ import ( fakeKube "k8s.io/client-go/kubernetes/fake" configv1alpha2 "github.com/openservicemesh/osm/pkg/apis/config/v1alpha2" + "github.com/openservicemesh/osm/pkg/constants" configClientset "github.com/openservicemesh/osm/pkg/gen/client/config/clientset/versioned" fakeConfig "github.com/openservicemesh/osm/pkg/gen/client/config/clientset/versioned/fake" ) @@ -96,23 +97,8 @@ var testMeshRootCertificate *configv1alpha2.MeshRootCertificate = &configv1alpha }, Spec: configv1alpha2.MeshRootCertificateSpec{}, Status: configv1alpha2.MeshRootCertificateStatus{ - State: "complete", - RotationStage: "issuing", - }, -} - -var testMeshRootCertificateWithLastAppliedAnnotation *configv1alpha2.MeshRootCertificate = &configv1alpha2.MeshRootCertificate{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: testNamespace, - Name: meshRootCertificateName, - Annotations: map[string]string{ - "kubectl.kubernetes.io/last-applied-configuration": `{"metadata":{"name":"osm-mesh-root-certificate","namespace":"test-namespace","creationTimestamp":null},"spec":{}}`, - }, - }, - Spec: configv1alpha2.MeshRootCertificateSpec{}, - Status: configv1alpha2.MeshRootCertificateStatus{ - State: "complete", - RotationStage: "issuing", + State: constants.MRCStateComplete, + RotationStage: constants.MRCStageIssuing, }, } @@ -166,7 +152,6 @@ func TestBuildMeshRootCertificate(t *testing.T) { meshRootCertificate, err := buildMeshRootCertificate(testPresetMeshRootCertificate) assert.NoError(err) - assert.Contains(meshRootCertificate.Annotations, "kubectl.kubernetes.io/last-applied-configuration") assert.Equal(meshRootCertificate.Name, meshRootCertificateName) assert.Equal(meshRootCertificate.Spec.Provider.Tresor.CA.SecretRef.Name, "osm-ca-bundle") assert.Equal(meshRootCertificate.Spec.Provider.Tresor.CA.SecretRef.Namespace, testNamespace) @@ -261,22 +246,10 @@ func TestCreateDefaultMeshConfig(t *testing.T) { } err := b.createDefaultMeshConfig() - if tc.expectErr { - assert.NotNil(err) - } else { - assert.Nil(err) - } + assert.Equal(tc.expectErr, err != nil) _, err = b.configClient.ConfigV1alpha2().MeshConfigs(b.namespace).Get(context.TODO(), meshConfigName, metav1.GetOptions{}) - if tc.expectDefaultMeshConfig { - if err == nil { - assert.Nil(err) - } - } else { - if err == nil { - assert.NotNil(err) - } - } + assert.Equal(tc.expectDefaultMeshConfig, err == nil) }) } } @@ -329,10 +302,8 @@ func TestEnsureMeshConfig(t *testing.T) { } err := b.ensureMeshConfig() - if tc.expectErr { - assert.NotNil(err) - } else { - assert.Nil(err) + assert.Equal(tc.expectErr, err != nil) + if !tc.expectErr { config, err := b.configClient.ConfigV1alpha2().MeshConfigs(b.namespace).Get(context.TODO(), meshConfigName, metav1.GetOptions{}) assert.Nil(err) assert.Contains(config.Annotations, "kubectl.kubernetes.io/last-applied-configuration") @@ -386,22 +357,10 @@ func TestCreateMeshRootCertificateConfig(t *testing.T) { } err := b.createMeshRootCertificate() - if tc.expectErr { - assert.NotNil(err) - } else { - assert.Nil(err) - } + assert.Equal(tc.expectErr, err != nil) _, err = b.configClient.ConfigV1alpha2().MeshRootCertificates(b.namespace).Get(context.TODO(), meshRootCertificateName, metav1.GetOptions{}) - if tc.expectDefaultMeshRootCertificate { - if err == nil { - assert.Nil(err) - } - } else { - if err == nil { - assert.NotNil(err) - } - } + assert.Equal(tc.expectDefaultMeshRootCertificate, err == nil) }) } } @@ -415,19 +374,12 @@ func TestEnsureMeshRootCertificate(t *testing.T) { expectErr bool }{ { - name: "MeshRootCertificate found with no last-applied annotation", + name: "MeshRootCertificate found", namespace: testNamespace, kubeClient: fakeKube.NewSimpleClientset(), configClient: fakeConfig.NewSimpleClientset([]runtime.Object{testMeshRootCertificate}...), expectErr: false, }, - { - name: "MeshRootCertificate found with last-applied annotation", - namespace: testNamespace, - kubeClient: fakeKube.NewSimpleClientset(), - configClient: fakeConfig.NewSimpleClientset([]runtime.Object{testMeshRootCertificateWithLastAppliedAnnotation}...), - expectErr: false, - }, { name: "MeshRootCertificate not found but successfully created", namespace: testNamespace, @@ -454,14 +406,10 @@ func TestEnsureMeshRootCertificate(t *testing.T) { } err := b.ensureMeshRootCertificate() - if tc.expectErr { - assert.NotNil(err) - } else { - assert.Nil(err) - config, err := b.configClient.ConfigV1alpha2().MeshRootCertificates(b.namespace).Get(context.TODO(), meshRootCertificateName, metav1.GetOptions{}) - assert.Nil(err) - assert.Contains(config.Annotations, "kubectl.kubernetes.io/last-applied-configuration") - } + assert.Equal(tc.expectErr, err != nil) + + _, err = b.configClient.ConfigV1alpha2().MeshRootCertificates(b.namespace).Get(context.TODO(), meshRootCertificateName, metav1.GetOptions{}) + assert.Equal(tc.expectErr, err != nil) }) } } @@ -522,11 +470,7 @@ func TestGetBootstrapPod(t *testing.T) { assert.Nil(err) _, err = b.getBootstrapPod() - if tc.expectErr { - assert.NotNil(err) - } else { - assert.Nil(err) - } + assert.Equal(tc.expectErr, err != nil) }) } } From ee27ae81f86d39659567849b3a425cb50baa1b7b Mon Sep 17 00:00:00 2001 From: jaellio Date: Mon, 23 May 2022 10:47:52 -0700 Subject: [PATCH 4/6] Update MRC listing Signed-off-by: jaellio --- cmd/osm-bootstrap/osm-bootstrap.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/cmd/osm-bootstrap/osm-bootstrap.go b/cmd/osm-bootstrap/osm-bootstrap.go index 2b486cdda5..7e6f9d6133 100644 --- a/cmd/osm-bootstrap/osm-bootstrap.go +++ b/cmd/osm-bootstrap/osm-bootstrap.go @@ -366,15 +366,19 @@ func buildDefaultMeshConfig(presetMeshConfigMap *corev1.ConfigMap) (*configv1alp } func (b *bootstrap) ensureMeshRootCertificate() error { - listOptions := metav1.ListOptions{ - FieldSelector: "status.state=complete,status.rotationStage=issuing", - } - meshRootCertificateList, err := b.configClient.ConfigV1alpha2().MeshRootCertificates(b.namespace).List(context.TODO(), listOptions) + meshRootCertificateList, err := b.configClient.ConfigV1alpha2().MeshRootCertificates(b.namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { return err } + var foundIssuingAndCompleteMRC bool + for _, mrc := range meshRootCertificateList.Items { + if mrc.Status.RotationStage == constants.MRCStageIssuing && mrc.Status.State == constants.MRCStateComplete { + foundIssuingAndCompleteMRC = true + break + } + } - if len(meshRootCertificateList.Items) == 0 { + if !foundIssuingAndCompleteMRC { // create a MeshRootCertificate since none were found in the complete state and issuing rotationStage return b.createMeshRootCertificate() } @@ -390,11 +394,11 @@ func (b *bootstrap) createMeshRootCertificate() error { } // Create a MeshRootCertificate - defaultMeshConfig, err := buildMeshRootCertificate(presetMeshRootCertificate) + defaultMeshRootCertificate, err := buildMeshRootCertificate(presetMeshRootCertificate) if err != nil { return err } - if _, err := b.configClient.ConfigV1alpha2().MeshRootCertificates(b.namespace).Create(context.TODO(), defaultMeshConfig, metav1.CreateOptions{}); err == nil { + if _, err := b.configClient.ConfigV1alpha2().MeshRootCertificates(b.namespace).Create(context.TODO(), defaultMeshRootCertificate, metav1.CreateOptions{}); err == nil { log.Info().Msgf("MeshRootCertificate (%s) created in namespace %s", meshConfigName, b.namespace) return nil } @@ -430,5 +434,5 @@ func buildMeshRootCertificate(presetMeshRootCertificateConfigMap *corev1.ConfigM }, } - return config, util.CreateApplyAnnotation(config, unstructured.UnstructuredJSONScheme) + return config, nil } From 9dd1784757e47da3a5e21c89b10a84a25c44a911 Mon Sep 17 00:00:00 2001 From: jaellio Date: Mon, 23 May 2022 14:04:09 -0700 Subject: [PATCH 5/6] Add annotation on create Signed-off-by: jaellio --- cmd/osm-bootstrap/osm-bootstrap.go | 15 +++++---------- cmd/osm-bootstrap/osm-bootstrap_test.go | 5 +++-- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/cmd/osm-bootstrap/osm-bootstrap.go b/cmd/osm-bootstrap/osm-bootstrap.go index 7e6f9d6133..969c6d5330 100644 --- a/cmd/osm-bootstrap/osm-bootstrap.go +++ b/cmd/osm-bootstrap/osm-bootstrap.go @@ -370,20 +370,15 @@ func (b *bootstrap) ensureMeshRootCertificate() error { if err != nil { return err } - var foundIssuingAndCompleteMRC bool + for _, mrc := range meshRootCertificateList.Items { if mrc.Status.RotationStage == constants.MRCStageIssuing && mrc.Status.State == constants.MRCStateComplete { - foundIssuingAndCompleteMRC = true - break + return nil } } - if !foundIssuingAndCompleteMRC { - // create a MeshRootCertificate since none were found in the complete state and issuing rotationStage - return b.createMeshRootCertificate() - } - - return nil + // create a MeshRootCertificate since none were found in the complete state and issuing rotationStage + return b.createMeshRootCertificate() } func (b *bootstrap) createMeshRootCertificate() error { @@ -434,5 +429,5 @@ func buildMeshRootCertificate(presetMeshRootCertificateConfigMap *corev1.ConfigM }, } - return config, nil + return config, util.CreateApplyAnnotation(config, unstructured.UnstructuredJSONScheme) } diff --git a/cmd/osm-bootstrap/osm-bootstrap_test.go b/cmd/osm-bootstrap/osm-bootstrap_test.go index e2d01dc93e..32e3eec19e 100644 --- a/cmd/osm-bootstrap/osm-bootstrap_test.go +++ b/cmd/osm-bootstrap/osm-bootstrap_test.go @@ -151,6 +151,7 @@ func TestBuildMeshRootCertificate(t *testing.T) { assert := tassert.New(t) meshRootCertificate, err := buildMeshRootCertificate(testPresetMeshRootCertificate) + assert.Contains(meshRootCertificate.Annotations, "kubectl.kubernetes.io/last-applied-configuration") assert.NoError(err) assert.Equal(meshRootCertificate.Name, meshRootCertificateName) assert.Equal(meshRootCertificate.Spec.Provider.Tresor.CA.SecretRef.Name, "osm-ca-bundle") @@ -312,7 +313,7 @@ func TestEnsureMeshConfig(t *testing.T) { } } -func TestCreateMeshRootCertificateConfig(t *testing.T) { +func TestCreateMeshRootCertificate(t *testing.T) { tests := []struct { name string namespace string @@ -338,7 +339,7 @@ func TestCreateMeshRootCertificateConfig(t *testing.T) { expectErr: true, }, { - name: "default MeshRootCertificate already exists", + name: "MeshRootCertificate already exists", namespace: testNamespace, kubeClient: fakeKube.NewSimpleClientset([]runtime.Object{testPresetMeshRootCertificate}...), configClient: fakeConfig.NewSimpleClientset([]runtime.Object{testMeshRootCertificate}...), From 5d30cc59aa2f61c7ea389627faa4b47c88e83490 Mon Sep 17 00:00:00 2001 From: jaellio Date: Tue, 24 May 2022 12:43:24 -0700 Subject: [PATCH 6/6] PR feedback Signed-off-by: jaellio --- charts/osm/README.md | 5 ++-- .../preset-mesh-root-certificate.yaml | 12 ++++---- charts/osm/values.schema.json | 30 ++++++++++++------- charts/osm/values.yaml | 10 ++++--- cmd/osm-bootstrap/osm-bootstrap.go | 26 ++++++++-------- 5 files changed, 46 insertions(+), 37 deletions(-) diff --git a/charts/osm/README.md b/charts/osm/README.md index 4138089b8d..058120bb75 100644 --- a/charts/osm/README.md +++ b/charts/osm/README.md @@ -178,9 +178,10 @@ The following table lists the configurable parameters of the osm chart and their | osm.vault.port | int | `8200` | port to use to connect to Vault | | osm.vault.protocol | string | `"http"` | protocol to use to connect to Vault | | osm.vault.role | string | `"openservicemesh"` | Vault role to be used by Open Service Mesh | +| osm.vault.secret | object | `{"key":"token","name":"osm-vault-token"}` | The Kubernetes secret storing the Vault token used in OSM | +| osm.vault.secret.key | string | `"token"` | The Kubernetes secret key with the value bring the Vault token | +| osm.vault.secret.name | string | `"osm-vault-token"` | The Kubernetes secret name storing the Vault token used in OSM | | osm.vault.token | string | `""` | token that should be used to connect to Vault | -| osm.vault.tokenSecretKey | string | `"token"` | The Kubernetes secret key with the value bring the Vault token | -| osm.vault.tokenSecretName | string | `"osm-vault-token"` | The Kubernetes secret name to store the Vault token used in OSM | | osm.webhookConfigNamePrefix | string | `"osm-webhook"` | Prefix used in name of the webhook configuration resources | | smi.validateTrafficTarget | bool | `true` | Enables validation of SMI Traffic Target | diff --git a/charts/osm/templates/preset-mesh-root-certificate.yaml b/charts/osm/templates/preset-mesh-root-certificate.yaml index 9cfbb62270..00a1e412d9 100644 --- a/charts/osm/templates/preset-mesh-root-certificate.yaml +++ b/charts/osm/templates/preset-mesh-root-certificate.yaml @@ -17,19 +17,19 @@ data: } } {{- end}} - {{- if eq (.Values.osm.certificateProvider.kind | lower) "certmanager"}} + {{- if eq (.Values.osm.certificateProvider.kind | lower) "cert-manager"}} "certManager": { - "issuerName": {{.Values.osm.certManager.issuerName | mustToJson}}, - "issuerKind": {{.Values.osm.certManager.issuerKind | mustToJson}}, - "issuerGroup": {{.Values.osm.certManager.issuerGroup | mustToJson}} + "issuerName": {{.Values.osm.certmanager.issuerName | mustToJson}}, + "issuerKind": {{.Values.osm.certmanager.issuerKind | mustToJson}}, + "issuerGroup": {{.Values.osm.certmanager.issuerGroup | mustToJson}} } {{- end}} {{- if eq (.Values.osm.certificateProvider.kind | lower) "vault"}} "vault": { "token": { "secretKeyRef": { - "name": {{.Values.osm.vault.tokenSecretName | mustToJson}}, - "key": {{.Values.osm.vault.tokenSecretKey | mustToJson}}, + "name": {{.Values.osm.vault.secret.name | mustToJson}}, + "key": {{.Values.osm.vault.secret.key | mustToJson}}, "namespace": "{{include "osm.namespace" .}}" } }, diff --git a/charts/osm/values.schema.json b/charts/osm/values.schema.json index 2c5dca0482..f8604a170b 100644 --- a/charts/osm/values.schema.json +++ b/charts/osm/values.schema.json @@ -1293,17 +1293,25 @@ "description": "Role to use with Vault", "type": "string" }, - "tokenSecretName": { - "$id": "#/properties/osm/properties/vault/properties/tokenSecretName", - "title": "Vault token secret name schema", - "description": "Name of the Kubernetes Secret to store the vault token", - "type": "string" - }, - "tokenSecretKey": { - "$id": "#/properties/osm/properties/vault/properties/tokenSecretKey", - "title": "Vault token secret key schema", - "description": "Name of the Kubernetes Secret key with the value of the vault token", - "type": "string" + "secret": { + "$id": "#/properties/osm/properties/vault/properties/secret", + "type": "object", + "title": "Vault token secret schema", + "description": "Vault token secret reference parameters", + "properties": { + "name": { + "$id": "#/properties/osm/properties/vault/properties/secret/properties/name", + "title": "Vault token secret name schema", + "description": "Name of the Kubernetes Secret to store the vault token", + "type": "string" + }, + "key": { + "$id": "#/properties/osm/properties/vault/properties/secret/properties/key", + "title": "Vault token secret key schema", + "description": "Name of the Kubernetes Secret key with the value of the vault token", + "type": "string" + } + } } }, "examples": [ diff --git a/charts/osm/values.yaml b/charts/osm/values.yaml index 043f03fd3f..61057a5405 100644 --- a/charts/osm/values.yaml +++ b/charts/osm/values.yaml @@ -129,10 +129,12 @@ osm: token: "" # -- Vault role to be used by Open Service Mesh role: openservicemesh - # -- The Kubernetes secret name to store the Vault token used in OSM - tokenSecretName: osm-vault-token - # -- The Kubernetes secret key with the value bring the Vault token - tokenSecretKey: token + # -- The Kubernetes secret storing the Vault token used in OSM + secret: + # -- The Kubernetes secret name storing the Vault token used in OSM + name: osm-vault-token + # -- The Kubernetes secret key with the value bring the Vault token + key: token # # -- cert-manager.io configuration diff --git a/cmd/osm-bootstrap/osm-bootstrap.go b/cmd/osm-bootstrap/osm-bootstrap.go index 969c6d5330..0649c21eb1 100644 --- a/cmd/osm-bootstrap/osm-bootstrap.go +++ b/cmd/osm-bootstrap/osm-bootstrap.go @@ -371,13 +371,11 @@ func (b *bootstrap) ensureMeshRootCertificate() error { return err } - for _, mrc := range meshRootCertificateList.Items { - if mrc.Status.RotationStage == constants.MRCStageIssuing && mrc.Status.State == constants.MRCStateComplete { - return nil - } + if len(meshRootCertificateList.Items) != 0 { + return nil } - // create a MeshRootCertificate since none were found in the complete state and issuing rotationStage + // create a MeshRootCertificate since none were found return b.createMeshRootCertificate() } @@ -393,17 +391,17 @@ func (b *bootstrap) createMeshRootCertificate() error { if err != nil { return err } - if _, err := b.configClient.ConfigV1alpha2().MeshRootCertificates(b.namespace).Create(context.TODO(), defaultMeshRootCertificate, metav1.CreateOptions{}); err == nil { - log.Info().Msgf("MeshRootCertificate (%s) created in namespace %s", meshConfigName, b.namespace) - return nil - } - + _, err = b.configClient.ConfigV1alpha2().MeshRootCertificates(b.namespace).Create(context.TODO(), defaultMeshRootCertificate, metav1.CreateOptions{}) if apierrors.IsAlreadyExists(err) { log.Info().Msgf("MeshRootCertificate already exists in %s. Skip creating.", b.namespace) return nil } + if err != nil { + return err + } - return err + log.Info().Msgf("Successfully created MeshRootCertificate %s in %s.", meshRootCertificateName, b.namespace) + return nil } func buildMeshRootCertificate(presetMeshRootCertificateConfigMap *corev1.ConfigMap) (*configv1alpha2.MeshRootCertificate, error) { @@ -411,10 +409,10 @@ func buildMeshRootCertificate(presetMeshRootCertificateConfigMap *corev1.ConfigM presetMeshRootCertificateSpec := configv1alpha2.MeshRootCertificateSpec{} err := json.Unmarshal([]byte(presetMeshRootCertificate), &presetMeshRootCertificateSpec) if err != nil { - log.Fatal().Err(err).Msgf("Error converting preset-mesh-root-certificate json string to MeshRootCertificate object") + return nil, fmt.Errorf("error converting preset-mesh-root-certificate json string to MeshRootCertificate object: %w", err) } - config := &configv1alpha2.MeshRootCertificate{ + mrc := &configv1alpha2.MeshRootCertificate{ TypeMeta: metav1.TypeMeta{ Kind: "MeshRootCertificate", APIVersion: "config.openservicemesh.io/configv1alpha2", @@ -429,5 +427,5 @@ func buildMeshRootCertificate(presetMeshRootCertificateConfigMap *corev1.ConfigM }, } - return config, util.CreateApplyAnnotation(config, unstructured.UnstructuredJSONScheme) + return mrc, util.CreateApplyAnnotation(mrc, unstructured.UnstructuredJSONScheme) }