Skip to content
This repository has been archived by the owner on Jul 11, 2023. It is now read-only.

Commit

Permalink
add the last applied annotation to allow using kubectl apply on the m…
Browse files Browse the repository at this point in the history
…esh config (#4673)

Signed-off-by: Sean Teeling <seanteeling@microsoft.com>
  • Loading branch information
steeling authored and keithmattix committed May 3, 2022
1 parent 79eef29 commit 868c132
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 70 deletions.
35 changes: 24 additions & 11 deletions cmd/osm-bootstrap/osm-bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ import (
apiclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/kubectl/pkg/util"

configv1alpha2 "github.com/openservicemesh/osm/pkg/apis/config/v1alpha2"

Expand Down Expand Up @@ -225,7 +227,10 @@ func (b *bootstrap) createDefaultMeshConfig() error {
}

// Create a default meshConfig
defaultMeshConfig := buildDefaultMeshConfig(presetsConfigMap)
defaultMeshConfig, err := buildDefaultMeshConfig(presetsConfigMap)
if err != nil {
return err
}
if _, err := b.meshConfigClient.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
Expand All @@ -240,19 +245,25 @@ func (b *bootstrap) createDefaultMeshConfig() error {
}

func (b *bootstrap) ensureMeshConfig() error {
_, err := b.meshConfigClient.ConfigV1alpha2().MeshConfigs(b.namespace).Get(context.TODO(), meshConfigName, metav1.GetOptions{})
if err == nil {
return nil // default meshConfig was found
}

config, err := b.meshConfigClient.ConfigV1alpha2().MeshConfigs(b.namespace).Get(context.TODO(), meshConfigName, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
// create a default mesh config since it was not found
if err = b.createDefaultMeshConfig(); err != nil {
return b.createDefaultMeshConfig()
}
if err != nil {
return err
}

if _, exists := config.Annotations[corev1.LastAppliedConfigAnnotation]; !exists {
// Mesh was found, but may not have the last applied annotation.
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 {
return err
}
}

return err
return nil
}

// initiatilizeKubernetesEventsRecorder initializes the generic Kubernetes event recorder and associates it with
Expand Down Expand Up @@ -305,15 +316,15 @@ func validateCLIParams() error {
return nil
}

func buildDefaultMeshConfig(presetMeshConfigMap *corev1.ConfigMap) *configv1alpha2.MeshConfig {
func buildDefaultMeshConfig(presetMeshConfigMap *corev1.ConfigMap) (*configv1alpha2.MeshConfig, error) {
presetMeshConfig := presetMeshConfigMap.Data[presetMeshConfigJSONKey]
presetMeshConfigSpec := configv1alpha2.MeshConfigSpec{}
err := json.Unmarshal([]byte(presetMeshConfig), &presetMeshConfigSpec)
if err != nil {
log.Fatal().Err(err).Msgf("Error converting preset-mesh-config json string to meshConfig object")
}

return &configv1alpha2.MeshConfig{
config := &configv1alpha2.MeshConfig{
TypeMeta: metav1.TypeMeta{
Kind: "MeshConfig",
APIVersion: "config.openservicemesh.io/configv1alpha2",
Expand All @@ -323,4 +334,6 @@ func buildDefaultMeshConfig(presetMeshConfigMap *corev1.ConfigMap) *configv1alph
},
Spec: presetMeshConfigSpec,
}

return config, util.CreateApplyAnnotation(config, unstructured.UnstructuredJSONScheme)
}
143 changes: 85 additions & 58 deletions cmd/osm-bootstrap/osm-bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ var testMeshConfig *configv1alpha2.MeshConfig = &configv1alpha2.MeshConfig{
Spec: configv1alpha2.MeshConfigSpec{},
}

var testMeshConfigWithLastAppliedAnnotation *configv1alpha2.MeshConfig = &configv1alpha2.MeshConfig{
ObjectMeta: metav1.ObjectMeta{
Namespace: testNamespace,
Name: meshConfigName,
Annotations: map[string]string{
"kubectl.kubernetes.io/last-applied-configuration": `{"metadata":{"name":"osm-mesh-config","namespace":"test-namespace","creationTimestamp":null},"spec":{}}`,
},
},
Spec: configv1alpha2.MeshConfigSpec{},
}

var testPresetMeshConfigMap *corev1.ConfigMap = &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
Kind: "ConfigMap",
Expand Down Expand Up @@ -81,7 +92,9 @@ var testPresetMeshConfigMap *corev1.ConfigMap = &corev1.ConfigMap{
func TestBuildDefaultMeshConfig(t *testing.T) {
assert := tassert.New(t)

meshConfig := buildDefaultMeshConfig(testPresetMeshConfigMap)
meshConfig, err := buildDefaultMeshConfig(testPresetMeshConfigMap)
assert.NoError(err)
assert.Contains(meshConfig.Annotations, "kubectl.kubernetes.io/last-applied-configuration")
assert.Equal(meshConfig.Name, meshConfigName)
assert.Equal(meshConfig.Spec.Sidecar.LogLevel, "error")
assert.Equal(meshConfig.Spec.Sidecar.ConfigResyncInterval, "2s")
Expand All @@ -103,12 +116,12 @@ func TestValidateCLIParams(t *testing.T) {
prevOsmNamespace := osmNamespace

tests := []struct {
caseName string
setup func()
verify func(error)
name string
setup func()
verify func(error)
}{
{
caseName: "osm-namespace is empty",
name: "osm-namespace is empty",
setup: func() {
osmNamespace = ""
},
Expand All @@ -118,7 +131,7 @@ func TestValidateCLIParams(t *testing.T) {
},
},
{
caseName: "osm-namespace is valid",
name: "osm-namespace is valid",
setup: func() {
osmNamespace = "valid-ns"
},
Expand All @@ -128,7 +141,7 @@ func TestValidateCLIParams(t *testing.T) {
},
},
{
caseName: "osm-namespace and ca-bundle-secret-name is valid",
name: "osm-namespace and ca-bundle-secret-name is valid",
setup: func() {
osmNamespace = "valid-ns"
caBundleSecretName = "valid-ca-bundle"
Expand All @@ -150,8 +163,6 @@ func TestValidateCLIParams(t *testing.T) {
}

func TestCreateDefaultMeshConfig(t *testing.T) {
assert := tassert.New(t)

tests := []struct {
name string
namespace string
Expand Down Expand Up @@ -187,35 +198,36 @@ func TestCreateDefaultMeshConfig(t *testing.T) {
}

for _, tc := range tests {
b := bootstrap{
kubeClient: tc.kubeClient,
meshConfigClient: tc.meshConfigClient,
namespace: tc.namespace,
}

err := b.createDefaultMeshConfig()
if tc.expectErr {
assert.NotNil(err)
} else {
assert.Nil(err)
}
t.Run(tc.name, func(t *testing.T) {
assert := tassert.New(t)
b := bootstrap{
kubeClient: tc.kubeClient,
meshConfigClient: tc.meshConfigClient,
namespace: tc.namespace,
}

_, err = b.meshConfigClient.ConfigV1alpha2().MeshConfigs(b.namespace).Get(context.TODO(), meshConfigName, metav1.GetOptions{})
if tc.expectDefaultMeshConfig {
if err == nil {
err := b.createDefaultMeshConfig()
if tc.expectErr {
assert.NotNil(err)
} else {
assert.Nil(err)
}
} else {
if err == nil {
assert.NotNil(err)

_, err = b.meshConfigClient.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)
}
}
}
})
}
}

func TestEnsureMeshConfig(t *testing.T) {
assert := tassert.New(t)

tests := []struct {
name string
namespace string
Expand All @@ -224,12 +236,19 @@ func TestEnsureMeshConfig(t *testing.T) {
expectErr bool
}{
{
name: "MeshConfig found",
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 last-applied annotation",
namespace: testNamespace,
kubeClient: fakeKube.NewSimpleClientset(),
meshConfigClient: fakeConfig.NewSimpleClientset([]runtime.Object{testMeshConfigWithLastAppliedAnnotation}...),
expectErr: false,
},
{
name: "MeshConfig not found but successfully created",
namespace: testNamespace,
Expand All @@ -247,18 +266,24 @@ func TestEnsureMeshConfig(t *testing.T) {
}

for _, tc := range tests {
b := bootstrap{
kubeClient: tc.kubeClient,
meshConfigClient: tc.meshConfigClient,
namespace: tc.namespace,
}
t.Run(tc.name, func(t *testing.T) {
assert := tassert.New(t)
b := bootstrap{
kubeClient: tc.kubeClient,
meshConfigClient: tc.meshConfigClient,
namespace: tc.namespace,
}

err := b.ensureMeshConfig()
if tc.expectErr {
assert.NotNil(err)
} else {
assert.Nil(err)
}
err := b.ensureMeshConfig()
if tc.expectErr {
assert.NotNil(err)
} else {
assert.Nil(err)
config, err := b.meshConfigClient.ConfigV1alpha2().MeshConfigs(b.namespace).Get(context.TODO(), meshConfigName, metav1.GetOptions{})
assert.Nil(err)
assert.Contains(config.Annotations, "kubectl.kubernetes.io/last-applied-configuration")
}
})
}
}

Expand Down Expand Up @@ -304,24 +329,26 @@ func TestGetBootstrapPod(t *testing.T) {
}

for _, tc := range tests {
b := bootstrap{
namespace: tc.namespace,
kubeClient: tc.kubeClient,
}
defer func() {
err := resetEnv("BOOTSTRAP_POD_NAME", os.Getenv("BOOTSTRAP_POD_NAME"))
assert.Nil(err)
}()

err := os.Setenv("BOOTSTRAP_POD_NAME", tc.bootstrapPodNameEnv)
assert.Nil(err)
t.Run(tc.name, func(t *testing.T) {
b := bootstrap{
namespace: tc.namespace,
kubeClient: tc.kubeClient,
}
defer func() {
err := resetEnv("BOOTSTRAP_POD_NAME", os.Getenv("BOOTSTRAP_POD_NAME"))
assert.Nil(err)
}()

_, err = b.getBootstrapPod()
if tc.expectErr {
assert.NotNil(err)
} else {
err := os.Setenv("BOOTSTRAP_POD_NAME", tc.bootstrapPodNameEnv)
assert.Nil(err)
}

_, err = b.getBootstrapPod()
if tc.expectErr {
assert.NotNil(err)
} else {
assert.Nil(err)
}
})
}
}

Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ require (
honnef.co/go/tools v0.1.1 // indirect
)

require k8s.io/kubectl v0.23.5

require (
4d63.com/gochecknoglobals v0.0.0-20201008074935-acfc0b28355a // indirect
cloud.google.com/go v0.81.0 // indirect
Expand Down Expand Up @@ -148,6 +150,7 @@ require (
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/fvbommel/sortorder v1.0.1 // indirect
github.com/go-critic/go-critic v0.5.2 // indirect
github.com/go-errors/errors v1.4.1 // indirect
github.com/go-logr/logr v1.2.0 // indirect
Expand Down Expand Up @@ -374,7 +377,6 @@ require (
k8s.io/helm v2.14.3+incompatible // indirect
k8s.io/klog/v2 v2.30.0 // indirect
k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect
k8s.io/kubectl v0.23.5 // indirect
mvdan.cc/gofumpt v0.1.1 // indirect
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed // indirect
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA=
github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE=
github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
github.com/gabriel-vasile/mimetype v1.3.1 h1:qevA6c2MtE1RorlScnixeG0VA1H4xrXyhyX3oWBynNQ=
github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8=
Expand Down

0 comments on commit 868c132

Please sign in to comment.