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

Commit 868c132

Browse files
steelingkeithmattix
authored andcommitted
add the last applied annotation to allow using kubectl apply on the mesh config (#4673)
Signed-off-by: Sean Teeling <[email protected]>
1 parent 79eef29 commit 868c132

File tree

4 files changed

+113
-70
lines changed

4 files changed

+113
-70
lines changed

cmd/osm-bootstrap/osm-bootstrap.go

+24-11
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@ import (
1818
apiclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1"
1919
apierrors "k8s.io/apimachinery/pkg/api/errors"
2020
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2122
"k8s.io/apimachinery/pkg/runtime"
2223
"k8s.io/client-go/kubernetes"
2324
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
2425
"k8s.io/client-go/tools/clientcmd"
26+
"k8s.io/kubectl/pkg/util"
2527

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

@@ -225,7 +227,10 @@ func (b *bootstrap) createDefaultMeshConfig() error {
225227
}
226228

227229
// Create a default meshConfig
228-
defaultMeshConfig := buildDefaultMeshConfig(presetsConfigMap)
230+
defaultMeshConfig, err := buildDefaultMeshConfig(presetsConfigMap)
231+
if err != nil {
232+
return err
233+
}
229234
if _, err := b.meshConfigClient.ConfigV1alpha2().MeshConfigs(b.namespace).Create(context.TODO(), defaultMeshConfig, metav1.CreateOptions{}); err == nil {
230235
log.Info().Msgf("MeshConfig (%s) created in namespace %s", meshConfigName, b.namespace)
231236
return nil
@@ -240,19 +245,25 @@ func (b *bootstrap) createDefaultMeshConfig() error {
240245
}
241246

242247
func (b *bootstrap) ensureMeshConfig() error {
243-
_, err := b.meshConfigClient.ConfigV1alpha2().MeshConfigs(b.namespace).Get(context.TODO(), meshConfigName, metav1.GetOptions{})
244-
if err == nil {
245-
return nil // default meshConfig was found
246-
}
247-
248+
config, err := b.meshConfigClient.ConfigV1alpha2().MeshConfigs(b.namespace).Get(context.TODO(), meshConfigName, metav1.GetOptions{})
248249
if apierrors.IsNotFound(err) {
249250
// create a default mesh config since it was not found
250-
if err = b.createDefaultMeshConfig(); err != nil {
251+
return b.createDefaultMeshConfig()
252+
}
253+
if err != nil {
254+
return err
255+
}
256+
257+
if _, exists := config.Annotations[corev1.LastAppliedConfigAnnotation]; !exists {
258+
// Mesh was found, but may not have the last applied annotation.
259+
if err := util.CreateApplyAnnotation(config, unstructured.UnstructuredJSONScheme); err != nil {
260+
return err
261+
}
262+
if _, err := b.meshConfigClient.ConfigV1alpha2().MeshConfigs(b.namespace).Update(context.TODO(), config, metav1.UpdateOptions{}); err != nil {
251263
return err
252264
}
253265
}
254-
255-
return err
266+
return nil
256267
}
257268

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

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

316-
return &configv1alpha2.MeshConfig{
327+
config := &configv1alpha2.MeshConfig{
317328
TypeMeta: metav1.TypeMeta{
318329
Kind: "MeshConfig",
319330
APIVersion: "config.openservicemesh.io/configv1alpha2",
@@ -323,4 +334,6 @@ func buildDefaultMeshConfig(presetMeshConfigMap *corev1.ConfigMap) *configv1alph
323334
},
324335
Spec: presetMeshConfigSpec,
325336
}
337+
338+
return config, util.CreateApplyAnnotation(config, unstructured.UnstructuredJSONScheme)
326339
}

cmd/osm-bootstrap/osm-bootstrap_test.go

+85-58
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ var testMeshConfig *configv1alpha2.MeshConfig = &configv1alpha2.MeshConfig{
2727
Spec: configv1alpha2.MeshConfigSpec{},
2828
}
2929

30+
var testMeshConfigWithLastAppliedAnnotation *configv1alpha2.MeshConfig = &configv1alpha2.MeshConfig{
31+
ObjectMeta: metav1.ObjectMeta{
32+
Namespace: testNamespace,
33+
Name: meshConfigName,
34+
Annotations: map[string]string{
35+
"kubectl.kubernetes.io/last-applied-configuration": `{"metadata":{"name":"osm-mesh-config","namespace":"test-namespace","creationTimestamp":null},"spec":{}}`,
36+
},
37+
},
38+
Spec: configv1alpha2.MeshConfigSpec{},
39+
}
40+
3041
var testPresetMeshConfigMap *corev1.ConfigMap = &corev1.ConfigMap{
3142
TypeMeta: metav1.TypeMeta{
3243
Kind: "ConfigMap",
@@ -81,7 +92,9 @@ var testPresetMeshConfigMap *corev1.ConfigMap = &corev1.ConfigMap{
8192
func TestBuildDefaultMeshConfig(t *testing.T) {
8293
assert := tassert.New(t)
8394

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

105118
tests := []struct {
106-
caseName string
107-
setup func()
108-
verify func(error)
119+
name string
120+
setup func()
121+
verify func(error)
109122
}{
110123
{
111-
caseName: "osm-namespace is empty",
124+
name: "osm-namespace is empty",
112125
setup: func() {
113126
osmNamespace = ""
114127
},
@@ -118,7 +131,7 @@ func TestValidateCLIParams(t *testing.T) {
118131
},
119132
},
120133
{
121-
caseName: "osm-namespace is valid",
134+
name: "osm-namespace is valid",
122135
setup: func() {
123136
osmNamespace = "valid-ns"
124137
},
@@ -128,7 +141,7 @@ func TestValidateCLIParams(t *testing.T) {
128141
},
129142
},
130143
{
131-
caseName: "osm-namespace and ca-bundle-secret-name is valid",
144+
name: "osm-namespace and ca-bundle-secret-name is valid",
132145
setup: func() {
133146
osmNamespace = "valid-ns"
134147
caBundleSecretName = "valid-ca-bundle"
@@ -150,8 +163,6 @@ func TestValidateCLIParams(t *testing.T) {
150163
}
151164

152165
func TestCreateDefaultMeshConfig(t *testing.T) {
153-
assert := tassert.New(t)
154-
155166
tests := []struct {
156167
name string
157168
namespace string
@@ -187,35 +198,36 @@ func TestCreateDefaultMeshConfig(t *testing.T) {
187198
}
188199

189200
for _, tc := range tests {
190-
b := bootstrap{
191-
kubeClient: tc.kubeClient,
192-
meshConfigClient: tc.meshConfigClient,
193-
namespace: tc.namespace,
194-
}
195-
196-
err := b.createDefaultMeshConfig()
197-
if tc.expectErr {
198-
assert.NotNil(err)
199-
} else {
200-
assert.Nil(err)
201-
}
201+
t.Run(tc.name, func(t *testing.T) {
202+
assert := tassert.New(t)
203+
b := bootstrap{
204+
kubeClient: tc.kubeClient,
205+
meshConfigClient: tc.meshConfigClient,
206+
namespace: tc.namespace,
207+
}
202208

203-
_, err = b.meshConfigClient.ConfigV1alpha2().MeshConfigs(b.namespace).Get(context.TODO(), meshConfigName, metav1.GetOptions{})
204-
if tc.expectDefaultMeshConfig {
205-
if err == nil {
209+
err := b.createDefaultMeshConfig()
210+
if tc.expectErr {
211+
assert.NotNil(err)
212+
} else {
206213
assert.Nil(err)
207214
}
208-
} else {
209-
if err == nil {
210-
assert.NotNil(err)
215+
216+
_, err = b.meshConfigClient.ConfigV1alpha2().MeshConfigs(b.namespace).Get(context.TODO(), meshConfigName, metav1.GetOptions{})
217+
if tc.expectDefaultMeshConfig {
218+
if err == nil {
219+
assert.Nil(err)
220+
}
221+
} else {
222+
if err == nil {
223+
assert.NotNil(err)
224+
}
211225
}
212-
}
226+
})
213227
}
214228
}
215229

216230
func TestEnsureMeshConfig(t *testing.T) {
217-
assert := tassert.New(t)
218-
219231
tests := []struct {
220232
name string
221233
namespace string
@@ -224,12 +236,19 @@ func TestEnsureMeshConfig(t *testing.T) {
224236
expectErr bool
225237
}{
226238
{
227-
name: "MeshConfig found",
239+
name: "MeshConfig found with no last-applied annotation",
228240
namespace: testNamespace,
229241
kubeClient: fakeKube.NewSimpleClientset(),
230242
meshConfigClient: fakeConfig.NewSimpleClientset([]runtime.Object{testMeshConfig}...),
231243
expectErr: false,
232244
},
245+
{
246+
name: "MeshConfig found with last-applied annotation",
247+
namespace: testNamespace,
248+
kubeClient: fakeKube.NewSimpleClientset(),
249+
meshConfigClient: fakeConfig.NewSimpleClientset([]runtime.Object{testMeshConfigWithLastAppliedAnnotation}...),
250+
expectErr: false,
251+
},
233252
{
234253
name: "MeshConfig not found but successfully created",
235254
namespace: testNamespace,
@@ -247,18 +266,24 @@ func TestEnsureMeshConfig(t *testing.T) {
247266
}
248267

249268
for _, tc := range tests {
250-
b := bootstrap{
251-
kubeClient: tc.kubeClient,
252-
meshConfigClient: tc.meshConfigClient,
253-
namespace: tc.namespace,
254-
}
269+
t.Run(tc.name, func(t *testing.T) {
270+
assert := tassert.New(t)
271+
b := bootstrap{
272+
kubeClient: tc.kubeClient,
273+
meshConfigClient: tc.meshConfigClient,
274+
namespace: tc.namespace,
275+
}
255276

256-
err := b.ensureMeshConfig()
257-
if tc.expectErr {
258-
assert.NotNil(err)
259-
} else {
260-
assert.Nil(err)
261-
}
277+
err := b.ensureMeshConfig()
278+
if tc.expectErr {
279+
assert.NotNil(err)
280+
} else {
281+
assert.Nil(err)
282+
config, err := b.meshConfigClient.ConfigV1alpha2().MeshConfigs(b.namespace).Get(context.TODO(), meshConfigName, metav1.GetOptions{})
283+
assert.Nil(err)
284+
assert.Contains(config.Annotations, "kubectl.kubernetes.io/last-applied-configuration")
285+
}
286+
})
262287
}
263288
}
264289

@@ -304,24 +329,26 @@ func TestGetBootstrapPod(t *testing.T) {
304329
}
305330

306331
for _, tc := range tests {
307-
b := bootstrap{
308-
namespace: tc.namespace,
309-
kubeClient: tc.kubeClient,
310-
}
311-
defer func() {
312-
err := resetEnv("BOOTSTRAP_POD_NAME", os.Getenv("BOOTSTRAP_POD_NAME"))
313-
assert.Nil(err)
314-
}()
315-
316-
err := os.Setenv("BOOTSTRAP_POD_NAME", tc.bootstrapPodNameEnv)
317-
assert.Nil(err)
332+
t.Run(tc.name, func(t *testing.T) {
333+
b := bootstrap{
334+
namespace: tc.namespace,
335+
kubeClient: tc.kubeClient,
336+
}
337+
defer func() {
338+
err := resetEnv("BOOTSTRAP_POD_NAME", os.Getenv("BOOTSTRAP_POD_NAME"))
339+
assert.Nil(err)
340+
}()
318341

319-
_, err = b.getBootstrapPod()
320-
if tc.expectErr {
321-
assert.NotNil(err)
322-
} else {
342+
err := os.Setenv("BOOTSTRAP_POD_NAME", tc.bootstrapPodNameEnv)
323343
assert.Nil(err)
324-
}
344+
345+
_, err = b.getBootstrapPod()
346+
if tc.expectErr {
347+
assert.NotNil(err)
348+
} else {
349+
assert.Nil(err)
350+
}
351+
})
325352
}
326353
}
327354

go.mod

+3-1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ require (
8080
honnef.co/go/tools v0.1.1 // indirect
8181
)
8282

83+
require k8s.io/kubectl v0.23.5
84+
8385
require (
8486
4d63.com/gochecknoglobals v0.0.0-20201008074935-acfc0b28355a // indirect
8587
cloud.google.com/go v0.81.0 // indirect
@@ -148,6 +150,7 @@ require (
148150
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
149151
github.com/fatih/structs v1.1.0 // indirect
150152
github.com/fsnotify/fsnotify v1.5.1 // indirect
153+
github.com/fvbommel/sortorder v1.0.1 // indirect
151154
github.com/go-critic/go-critic v0.5.2 // indirect
152155
github.com/go-errors/errors v1.4.1 // indirect
153156
github.com/go-logr/logr v1.2.0 // indirect
@@ -374,7 +377,6 @@ require (
374377
k8s.io/helm v2.14.3+incompatible // indirect
375378
k8s.io/klog/v2 v2.30.0 // indirect
376379
k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect
377-
k8s.io/kubectl v0.23.5 // indirect
378380
mvdan.cc/gofumpt v0.1.1 // indirect
379381
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed // indirect
380382
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect

go.sum

+1
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,7 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
662662
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
663663
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
664664
github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA=
665+
github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE=
665666
github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
666667
github.com/gabriel-vasile/mimetype v1.3.1 h1:qevA6c2MtE1RorlScnixeG0VA1H4xrXyhyX3oWBynNQ=
667668
github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8=

0 commit comments

Comments
 (0)