Skip to content

Commit 72ab237

Browse files
authored
Merge pull request #167 from kbst/allow-disabling-compression
Add provider flag to disable lastAppliedConfig compression feature
2 parents 694e018 + 403a331 commit 72ab237

File tree

5 files changed

+110
-63
lines changed

5 files changed

+110
-63
lines changed

docs/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ provider "kustomization" {
4747
- `kubeconfig_incluster` - Set to `true` when running inside a kubernetes cluster.
4848
- `context` - (Optional) Context to use in kubeconfig with multiple contexts, if not specified the default context is used.
4949
- `legacy_id_format` - (Optional) Defaults to `false`. Provided for backward compability, set to `true` to use the legacy ID format.
50+
- `gzip_last_applied_config` - (Optional) Defaults to `true`. Use a gzip compressed and base64 encoded value for the lastAppliedConfig annotation if a resource would otherwise exceed the Kubernetes max annotation size. All other resources use the regular uncompressed annotation. Set to `false` to disable compressed annotation.
5051

5152
## Migrating resource IDs from legacy format to format enabling API version upgrades
5253

kustomize/provider.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type Config struct {
2525
Mutex *sync.Mutex
2626
// whether legacy IDs are produced or not
2727
LegacyIDs bool
28+
GzipLastAppliedConfig bool
2829
}
2930

3031
// Provider ...
@@ -77,6 +78,12 @@ func Provider() *schema.Provider {
7778
Deprecated: "legacy_id_format will be removed in a future version",
7879
Description: "If legacy_id_format is true, then resource IDs will look like group_version_kind|namespace|name. If legacy_id_format is false, then resource IDs will look like group/kind/namespace/name",
7980
},
81+
"gzip_last_applied_config": {
82+
Type: schema.TypeBool,
83+
Optional: true,
84+
Default: true,
85+
Description: "When 'true' compress the lastAppliedConfig annotation for resources that otherwise would exceed K8s' max annotation size. All other resources use the regular uncompressed annotation. Set to 'false' to disable compression entirely.",
86+
},
8087
},
8188
}
8289

@@ -144,8 +151,9 @@ func Provider() *schema.Provider {
144151
mu := &sync.Mutex{}
145152

146153
legacyIDs := d.Get("legacy_id_format").(bool)
154+
gzipLastAppliedConfig := d.Get("gzip_last_applied_config").(bool)
147155

148-
return &Config{client, mapper, mu, legacyIDs}, nil
156+
return &Config{client, mapper, mu, legacyIDs, gzipLastAppliedConfig}, nil
149157
}
150158

151159
return p

kustomize/resource_kustomization.go

+10-6
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ func kustomizationResource() *schema.Resource {
4242
func kustomizationResourceCreate(d *schema.ResourceData, m interface{}) error {
4343
client := m.(*Config).Client
4444
mapper := m.(*Config).Mapper
45+
gzipLastAppliedConfig := m.(*Config).GzipLastAppliedConfig
4546

4647
srcJSON := d.Get("manifest").(string)
4748
u, err := parseJSON(srcJSON)
@@ -79,7 +80,7 @@ func kustomizationResourceCreate(d *schema.ResourceData, m interface{}) error {
7980

8081
namespace := u.GetNamespace()
8182

82-
setLastAppliedConfig(u, srcJSON)
83+
setLastAppliedConfig(u, srcJSON, gzipLastAppliedConfig)
8384

8485
if namespace != "" {
8586
// wait for the namespace to exist
@@ -136,14 +137,15 @@ func kustomizationResourceCreate(d *schema.ResourceData, m interface{}) error {
136137
id := string(resp.GetUID())
137138
d.SetId(id)
138139

139-
d.Set("manifest", getLastAppliedConfig(resp))
140+
d.Set("manifest", getLastAppliedConfig(resp, gzipLastAppliedConfig))
140141

141142
return kustomizationResourceRead(d, m)
142143
}
143144

144145
func kustomizationResourceRead(d *schema.ResourceData, m interface{}) error {
145146
client := m.(*Config).Client
146147
mapper := m.(*Config).Mapper
148+
gzipLastAppliedConfig := m.(*Config).GzipLastAppliedConfig
147149

148150
srcJSON := d.Get("manifest").(string)
149151
u, err := parseJSON(srcJSON)
@@ -173,7 +175,7 @@ func kustomizationResourceRead(d *schema.ResourceData, m interface{}) error {
173175
id := string(resp.GetUID())
174176
d.SetId(id)
175177

176-
d.Set("manifest", getLastAppliedConfig(resp))
178+
d.Set("manifest", getLastAppliedConfig(resp, gzipLastAppliedConfig))
177179

178180
return nil
179181
}
@@ -317,6 +319,7 @@ func kustomizationResourceExists(d *schema.ResourceData, m interface{}) (bool, e
317319
func kustomizationResourceUpdate(d *schema.ResourceData, m interface{}) error {
318320
client := m.(*Config).Client
319321
mapper := m.(*Config).Mapper
322+
gzipLastAppliedConfig := m.(*Config).GzipLastAppliedConfig
320323

321324
originalJSON, modifiedJSON := d.GetChange("manifest")
322325

@@ -382,7 +385,7 @@ func kustomizationResourceUpdate(d *schema.ResourceData, m interface{}) error {
382385
id := string(patchResp.GetUID())
383386
d.SetId(id)
384387

385-
d.Set("manifest", getLastAppliedConfig(patchResp))
388+
d.Set("manifest", getLastAppliedConfig(patchResp, gzipLastAppliedConfig))
386389

387390
return kustomizationResourceRead(d, m)
388391
}
@@ -464,6 +467,7 @@ func kustomizationResourceDelete(d *schema.ResourceData, m interface{}) error {
464467
func kustomizationResourceImport(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) {
465468
client := m.(*Config).Client
466469
mapper := m.(*Config).Mapper
470+
gzipLastAppliedConfig := m.(*Config).GzipLastAppliedConfig
467471

468472
k, err := parseEitherIdFormat(d.Id())
469473
if err != nil {
@@ -494,10 +498,10 @@ func kustomizationResourceImport(d *schema.ResourceData, m interface{}) ([]*sche
494498
id := string(resp.GetUID())
495499
d.SetId(id)
496500

497-
lac := getLastAppliedConfig(resp)
501+
lac := getLastAppliedConfig(resp, gzipLastAppliedConfig)
498502
if lac == "" {
499503
return nil, logError(
500-
fmt.Errorf("group: %q, kind: %q, namespace: %q, name: %q: can not import resources without %q annotation", gk.Group, gk.Kind, k.Namespace, k.Name, lastAppliedConfig),
504+
fmt.Errorf("group: %q, kind: %q, namespace: %q, name: %q: can not import resources without %q or %q annotation", gk.Group, gk.Kind, k.Namespace, k.Name, lastAppliedConfigAnnotation, gzipLastAppliedConfigAnnotation),
501505
)
502506
}
503507

kustomize/util.go

+47-42
Original file line numberDiff line numberDiff line change
@@ -26,65 +26,69 @@ import (
2626
"k8s.io/kubectl/pkg/scheme"
2727
)
2828

29-
const lastAppliedConfig = k8scorev1.LastAppliedConfigAnnotation
30-
const gzipLastAppliedConfig = "kustomization.kubestack.com/last-applied-config-gzip"
29+
const lastAppliedConfigAnnotation = k8scorev1.LastAppliedConfigAnnotation
30+
const gzipLastAppliedConfigAnnotation = "kustomization.kubestack.com/last-applied-config-gzip"
3131

32-
func setLastAppliedConfig(u *k8sunstructured.Unstructured, srcJSON string) {
32+
func setLastAppliedConfig(u *k8sunstructured.Unstructured, srcJSON string, gzipLastAppliedConfig bool) {
3333
annotations := u.GetAnnotations()
3434
if len(annotations) == 0 {
3535
annotations = make(map[string]string)
3636
}
3737

38-
annotations[lastAppliedConfig] = srcJSON
39-
40-
needsGzip := false
41-
sErr := k8svalidation.ValidateAnnotationsSize(annotations)
42-
if sErr != nil {
43-
needsGzip = true
44-
}
38+
annotations[lastAppliedConfigAnnotation] = srcJSON
4539

46-
if needsGzip {
47-
var buf bytes.Buffer
48-
zw := gzip.NewWriter(&buf)
40+
if gzipLastAppliedConfig {
41+
needsGzip := false
42+
sErr := k8svalidation.ValidateAnnotationsSize(annotations)
43+
if sErr != nil {
44+
needsGzip = true
45+
}
46+
47+
if needsGzip {
48+
var buf bytes.Buffer
49+
zw := gzip.NewWriter(&buf)
4950

50-
_, err1 := zw.Write([]byte(srcJSON))
51+
_, err1 := zw.Write([]byte(srcJSON))
5152

52-
err2 := zw.Close()
53+
err2 := zw.Close()
5354

54-
if err1 == nil && err2 == nil {
55-
annotations[gzipLastAppliedConfig] = base64.StdEncoding.EncodeToString(buf.Bytes())
56-
delete(annotations, lastAppliedConfig)
55+
if err1 == nil && err2 == nil {
56+
annotations[gzipLastAppliedConfigAnnotation] = base64.StdEncoding.EncodeToString(buf.Bytes())
57+
delete(annotations, lastAppliedConfigAnnotation)
58+
}
5759
}
5860
}
5961

6062
u.SetAnnotations(annotations)
6163
}
6264

63-
func getLastAppliedConfig(u *k8sunstructured.Unstructured) (lac string) {
65+
func getLastAppliedConfig(u *k8sunstructured.Unstructured, gzipLastAppliedConfig bool) (lac string) {
6466
annotations := u.GetAnnotations()
6567

66-
lac = u.GetAnnotations()[lastAppliedConfig]
67-
68-
// read the compressed lac if available
69-
if gzEnc, ok := annotations[gzipLastAppliedConfig]; ok {
70-
gzDec, err := base64.StdEncoding.DecodeString(gzEnc)
71-
if err != nil {
72-
log.Fatal(err)
73-
}
74-
75-
var buf bytes.Buffer
76-
buf.Write(gzDec)
68+
lac = u.GetAnnotations()[lastAppliedConfigAnnotation]
7769

78-
zr, err1 := gzip.NewReader(&buf)
79-
80-
lacBuf := new(strings.Builder)
81-
_, err2 := io.Copy(lacBuf, zr)
82-
83-
err3 := zr.Close()
84-
85-
// in case of any error, fall back to the uncompressed lac
86-
if err1 == nil && err2 == nil && err3 == nil {
87-
lac = lacBuf.String()
70+
if gzipLastAppliedConfig {
71+
// read the compressed lac if available
72+
if gzEnc, ok := annotations[gzipLastAppliedConfigAnnotation]; ok {
73+
gzDec, err := base64.StdEncoding.DecodeString(gzEnc)
74+
if err != nil {
75+
log.Fatal(err)
76+
}
77+
78+
var buf bytes.Buffer
79+
buf.Write(gzDec)
80+
81+
zr, err1 := gzip.NewReader(&buf)
82+
83+
lacBuf := new(strings.Builder)
84+
_, err2 := io.Copy(lacBuf, zr)
85+
86+
err3 := zr.Close()
87+
88+
// in case of any error, fall back to the uncompressed lac
89+
if err1 == nil && err2 == nil && err3 == nil {
90+
lac = lacBuf.String()
91+
}
8892
}
8993
}
9094

@@ -94,6 +98,7 @@ func getLastAppliedConfig(u *k8sunstructured.Unstructured) (lac string) {
9498
func getOriginalModifiedCurrent(originalJSON string, modifiedJSON string, currentAllowNotFound bool, m interface{}) (original []byte, modified []byte, current []byte, err error) {
9599
client := m.(*Config).Client
96100
mapper := m.(*Config).Mapper
101+
gzipLastAppliedConfig := m.(*Config).GzipLastAppliedConfig
97102

98103
n, err := parseJSON(modifiedJSON)
99104
if err != nil {
@@ -104,8 +109,8 @@ func getOriginalModifiedCurrent(originalJSON string, modifiedJSON string, curren
104109
return nil, nil, nil, err
105110
}
106111

107-
setLastAppliedConfig(o, originalJSON)
108-
setLastAppliedConfig(n, modifiedJSON)
112+
setLastAppliedConfig(o, originalJSON, gzipLastAppliedConfig)
113+
setLastAppliedConfig(n, modifiedJSON, gzipLastAppliedConfig)
109114

110115
mapping, err := mapper.RESTMapping(n.GroupVersionKind().GroupKind(), n.GroupVersionKind().Version)
111116
if err != nil {

kustomize/util_test.go

+43-14
Original file line numberDiff line numberDiff line change
@@ -15,53 +15,82 @@ func TestLastAppliedConfig(t *testing.T) {
1515
if err != nil {
1616
t.Errorf("Error: %s", err)
1717
}
18-
setLastAppliedConfig(u, srcJSON)
18+
setLastAppliedConfig(u, srcJSON, true)
1919

2020
annotations := u.GetAnnotations()
2121
count := len(annotations)
2222
if count != 1 {
2323
t.Errorf("TestLastAppliedConfig: incorrect number of annotations, got: %d, want: %d.", count, 1)
2424
}
2525

26-
lac := getLastAppliedConfig(u)
26+
lac := getLastAppliedConfig(u, true)
2727
if lac != srcJSON {
2828
t.Errorf("TestLastAppliedConfig: incorrect annotation value, got: %s, want: %s.", srcJSON, lac)
2929
}
3030
}
3131

32-
func TestLastAppliedConfigCompressed(t *testing.T) {
33-
filler := func(n int) string {
34-
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
35-
b := make([]byte, n)
36-
for i := range b {
37-
b[i] = letterBytes[rand.Intn(len(letterBytes))]
38-
}
39-
return string(b)
40-
}(256 * (1 << 10))
32+
func randomDataHelper(n int) string {
33+
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
34+
b := make([]byte, n)
35+
for i := range b {
36+
b[i] = letterBytes[rand.Intn(len(letterBytes))]
37+
}
38+
return string(b)
39+
}
4140

41+
func TestLastAppliedConfigCompressed(t *testing.T) {
42+
filler := randomDataHelper(256 * (1 << 10))
4243
srcJSON := fmt.Sprintf("{\"apiVersion\": \"v1\", \"kind\": \"ConfigMap\", \"metadata\": {\"name\": \"test-unit\", \"namespace\": \"test-unit\"}, \"data\": {\"payload\": %q}}", filler)
4344
u, err := parseJSON(srcJSON)
4445
if err != nil {
4546
t.Errorf("Error: %s", err)
4647
}
47-
setLastAppliedConfig(u, srcJSON)
48+
setLastAppliedConfig(u, srcJSON, true)
4849

4950
annotations := u.GetAnnotations()
5051

51-
_, ok := annotations[gzipLastAppliedConfig]
52+
_, ok := annotations[gzipLastAppliedConfigAnnotation]
5253
assert.Equal(t, true, ok, "TestLastAppliedConfigCompressed: did not have gzipLastAppliedConfig annotation")
5354

5455
count := len(annotations)
5556
if count != 1 {
5657
t.Errorf("TestLastAppliedConfigCompressed: incorrect number of annotations, got: %d, want: %d.", count, 1)
5758
}
5859

59-
lac := getLastAppliedConfig(u)
60+
lac := getLastAppliedConfig(u, true)
6061
if lac != srcJSON {
6162
t.Errorf("TestLastAppliedConfigCompressed: incorrect annotation value, got: %s, want: %s.", srcJSON, lac)
6263
}
6364
}
6465

66+
func TestLastAppliedConfigCompressionDisabled(t *testing.T) {
67+
filler := randomDataHelper(256 * (1 << 10))
68+
srcJSON := fmt.Sprintf("{\"apiVersion\": \"v1\", \"kind\": \"ConfigMap\", \"metadata\": {\"name\": \"test-unit\", \"namespace\": \"test-unit\"}, \"data\": {\"payload\": %q}}", filler)
69+
u, err := parseJSON(srcJSON)
70+
if err != nil {
71+
t.Errorf("Error: %s", err)
72+
}
73+
setLastAppliedConfig(u, srcJSON, false)
74+
75+
annotations := u.GetAnnotations()
76+
77+
_, ok := annotations[lastAppliedConfigAnnotation]
78+
assert.Equal(t, true, ok, "TestLastAppliedConfigCompressionDisabled: did not have lastAppliedConfig annotation")
79+
80+
_, ok = annotations[gzipLastAppliedConfigAnnotation]
81+
assert.Equal(t, false, ok, "TestLastAppliedConfigCompressionDisabled: found gzipLastAppliedConfig annotation unexpectedly")
82+
83+
count := len(annotations)
84+
if count != 1 {
85+
t.Errorf("TestLastAppliedConfigCompressionDisabled: incorrect number of annotations, got: %d, want: %d.", count, 1)
86+
}
87+
88+
lac := getLastAppliedConfig(u, false)
89+
if lac != srcJSON {
90+
t.Errorf("TestLastAppliedConfigCompressionDisabled: incorrect annotation value, got: %s, want: %s.", srcJSON, lac)
91+
}
92+
}
93+
6594
func TestGetPatchStrategicMergePatch1(t *testing.T) {
6695
o, _ := parseJSON(testGetPatchStrategicMergePatch1OriginalJSON)
6796
m, _ := parseJSON(testGetPatchStrategicMergePatch1ModifiedJSON)

0 commit comments

Comments
 (0)