Skip to content

Commit 1ed6f77

Browse files
committed
Fix 6323: allow compression of the configMap
Add the `--gzip-configmap=true` CLI parameter in order to compress the configMap, if it exeeds the max length. Signed-off-by: Nahshon Unna-Tsameret <[email protected]>
1 parent af14062 commit 1ed6f77

File tree

8 files changed

+670
-179
lines changed

8 files changed

+670
-179
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# entries is a list of entries to include in
2+
# release notes and/or the migration guide
3+
entries:
4+
- description: >
5+
Allow compress the bundle content. Added a new cli flag `--gzip-configmap=true` to the `operator-sdk run bundle`.
6+
This will create compressed configmaps. Use it when getting this error:
7+
`... ConfigMap ... is invalid: []: Too long: must have at most 1048576 bytes`.
8+
Fixes issue [#6323](https://github.com/operator-framework/operator-sdk/issues/6323)
9+
10+
# kind is one of:
11+
# - addition
12+
# - change
13+
# - deprecation
14+
# - removal
15+
# - bugfix
16+
kind: "bugfix"
17+
18+
# Is this a breaking change?
19+
breaking: false

internal/olm/operator/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type Configuration struct {
3737
Client client.Client
3838
Scheme *runtime.Scheme
3939
Timeout time.Duration
40+
GzipCM bool
4041

4142
overrides *clientcmd.ConfigOverrides
4243
}
@@ -62,6 +63,8 @@ func (c *Configuration) BindFlags(fs *pflag.FlagSet) {
6263
"This value does not override the operator's service account")
6364
fs.DurationVar(&c.Timeout, "timeout", 2*time.Minute,
6465
"Duration to wait for the command to complete before failing")
66+
fs.BoolVar(&c.GzipCM, "gzip-configmap", false, `If 'true', the configmap data will be compressed. `+
67+
`Use if in case of getting the like "... ConfigMap ... is invalid: []: Too long: must have at most 1048576 bytes"`)
6568
}
6669

6770
func (c *Configuration) Load() error {
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
package fbcindex
2+
3+
import (
4+
"bytes"
5+
"compress/gzip"
6+
"fmt"
7+
corev1 "k8s.io/api/core/v1"
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
"strings"
10+
)
11+
12+
const (
13+
yamlSeparator = "\n---\n"
14+
gzipSuffixLength = 13
15+
maxGZIPLength = maxConfigMapSize - gzipSuffixLength
16+
17+
ConfigMapEncodingAnnotationKey = "olm.contentEncoding"
18+
ConfigMapEncodingAnnotationGzip = "gzip+base64"
19+
)
20+
21+
/*
22+
This file implements the actual building of the CM list. It uses the template method design pattern to implement both
23+
regular string VM, and compressed binary CM.
24+
25+
The method itself is FBCRegistryPod.getConfigMaps. This file contains the actual implementation of the writing actions,
26+
used by the method.
27+
*/
28+
29+
type configMapWriter interface {
30+
reset()
31+
newConfigMap(string) *corev1.ConfigMap
32+
getFilePath() string
33+
isEmpty() bool
34+
exceedMaxLength(cmSize int, data string) (bool, error)
35+
closeCM(cm *corev1.ConfigMap) error
36+
addData(data string) error
37+
continueAddData(data string) error
38+
writeLastFragment(cm *corev1.ConfigMap) error
39+
}
40+
41+
type defaultCMWriter struct {
42+
cmName string
43+
namespace string
44+
dataBuilder strings.Builder
45+
}
46+
47+
func newDefaultWriter(name, namespace string) *defaultCMWriter {
48+
return &defaultCMWriter{
49+
cmName: name,
50+
namespace: namespace,
51+
dataBuilder: strings.Builder{},
52+
}
53+
}
54+
55+
func (cmw *defaultCMWriter) reset() {
56+
cmw.dataBuilder = strings.Builder{}
57+
}
58+
59+
func (cmw *defaultCMWriter) newConfigMap(name string) *corev1.ConfigMap {
60+
return &corev1.ConfigMap{
61+
TypeMeta: metav1.TypeMeta{
62+
APIVersion: corev1.SchemeGroupVersion.String(),
63+
Kind: "ConfigMap",
64+
},
65+
ObjectMeta: metav1.ObjectMeta{
66+
Name: name,
67+
Namespace: cmw.namespace,
68+
},
69+
Data: map[string]string{},
70+
}
71+
}
72+
73+
func (cmw *defaultCMWriter) isEmpty() bool {
74+
return cmw.dataBuilder.Len() > 0
75+
}
76+
77+
func (cmw *defaultCMWriter) exceedMaxLength(cmSize int, data string) (bool, error) {
78+
return cmSize+cmw.dataBuilder.Len()+len(data) > maxConfigMapSize, nil
79+
}
80+
81+
func (cmw *defaultCMWriter) closeCM(cm *corev1.ConfigMap) error {
82+
cm.Data[defaultConfigMapKey] = cmw.dataBuilder.String()
83+
cmw.dataBuilder = strings.Builder{}
84+
return nil
85+
}
86+
87+
func (cmw *defaultCMWriter) getFilePath() string {
88+
return fmt.Sprintf("%s.yaml", defaultConfigMapKey)
89+
}
90+
91+
func (cmw *defaultCMWriter) addData(data string) error {
92+
cmw.dataBuilder.WriteString(data)
93+
return nil
94+
}
95+
96+
// continueAddData completes adding the data after starting adding it in exceedMaxLength
97+
func (cmw *defaultCMWriter) continueAddData(data string) error {
98+
return cmw.addData(data)
99+
}
100+
101+
func (cmw *defaultCMWriter) writeLastFragment(cm *corev1.ConfigMap) error {
102+
cm.Data[defaultConfigMapKey] = cmw.dataBuilder.String()
103+
104+
return nil
105+
}
106+
107+
type gzipCMWriter struct {
108+
actualBuff *bytes.Buffer
109+
helperBuff *bytes.Buffer
110+
actualWriter *gzip.Writer
111+
helperWriter *gzip.Writer
112+
cmName string
113+
namespace string
114+
}
115+
116+
func newGZIPWriter(name, namespace string) *gzipCMWriter {
117+
actualBuff := &bytes.Buffer{}
118+
helperBuff := &bytes.Buffer{}
119+
120+
return &gzipCMWriter{
121+
actualBuff: actualBuff,
122+
helperBuff: helperBuff,
123+
actualWriter: gzip.NewWriter(actualBuff),
124+
helperWriter: gzip.NewWriter(helperBuff),
125+
cmName: name,
126+
namespace: namespace,
127+
}
128+
}
129+
130+
func (cmw *gzipCMWriter) reset() {
131+
cmw.actualBuff.Reset()
132+
cmw.actualWriter.Reset(cmw.actualBuff)
133+
cmw.helperBuff.Reset()
134+
cmw.helperWriter.Reset(cmw.helperBuff)
135+
}
136+
137+
func (cmw *gzipCMWriter) newConfigMap(name string) *corev1.ConfigMap {
138+
return &corev1.ConfigMap{
139+
TypeMeta: metav1.TypeMeta{
140+
APIVersion: corev1.SchemeGroupVersion.String(),
141+
Kind: "ConfigMap",
142+
},
143+
ObjectMeta: metav1.ObjectMeta{
144+
Namespace: cmw.namespace,
145+
Name: name,
146+
Annotations: map[string]string{
147+
ConfigMapEncodingAnnotationKey: ConfigMapEncodingAnnotationGzip,
148+
},
149+
},
150+
BinaryData: map[string][]byte{},
151+
}
152+
}
153+
154+
func (cmw *gzipCMWriter) getFilePath() string {
155+
return fmt.Sprintf("%s.yaml.gz", defaultConfigMapKey)
156+
}
157+
158+
func (cmw *gzipCMWriter) isEmpty() bool {
159+
return cmw.actualBuff.Len() > 0
160+
}
161+
162+
func (cmw *gzipCMWriter) exceedMaxLength(cmSize int, data string) (bool, error) {
163+
_, err := cmw.helperWriter.Write([]byte(data))
164+
if err != nil {
165+
return false, err
166+
}
167+
168+
err = cmw.helperWriter.Flush()
169+
if err != nil {
170+
return false, err
171+
}
172+
173+
return cmSize+cmw.helperBuff.Len() > maxGZIPLength, nil
174+
}
175+
176+
func (cmw *gzipCMWriter) closeCM(cm *corev1.ConfigMap) error {
177+
err := cmw.actualWriter.Close()
178+
if err != nil {
179+
return err
180+
}
181+
182+
err = cmw.actualWriter.Flush()
183+
if err != nil {
184+
return err
185+
}
186+
187+
cm.BinaryData[defaultConfigMapKey] = make([]byte, cmw.actualBuff.Len())
188+
copy(cm.BinaryData[defaultConfigMapKey], cmw.actualBuff.Bytes())
189+
190+
cmw.reset()
191+
192+
return nil
193+
}
194+
195+
func (cmw *gzipCMWriter) addData(data string) error {
196+
dataBytes := []byte(data)
197+
_, err := cmw.helperWriter.Write(dataBytes)
198+
if err != nil {
199+
return err
200+
}
201+
_, err = cmw.actualWriter.Write(dataBytes)
202+
if err != nil {
203+
return err
204+
}
205+
return nil
206+
}
207+
208+
// continueAddData completes adding the data after starting adding it in exceedMaxLength
209+
func (cmw *gzipCMWriter) continueAddData(data string) error {
210+
_, err := cmw.actualWriter.Write([]byte(data))
211+
if err != nil {
212+
return err
213+
}
214+
return nil
215+
}
216+
217+
func (cmw *gzipCMWriter) writeLastFragment(cm *corev1.ConfigMap) error {
218+
err := cmw.actualWriter.Close()
219+
if err != nil {
220+
return err
221+
}
222+
223+
cm.BinaryData[defaultConfigMapKey] = cmw.actualBuff.Bytes()
224+
return nil
225+
}

0 commit comments

Comments
 (0)