Skip to content

Commit 6a6edfd

Browse files
authored
Fix 6323: compress the bundle configMap (#6408)
When creating the bundle ConfigMap(s), compress the content using gzip. Signed-off-by: Nahshon Unna-Tsameret <[email protected]>
1 parent 5a1acbf commit 6a6edfd

File tree

4 files changed

+503
-205
lines changed

4 files changed

+503
-205
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# entries is a list of entries to include in
2+
# release notes and/or the migration guide
3+
entries:
4+
- description: >
5+
Compress the bundle content, to avoid the configMap exceed max length error.
6+
The error will look like this:
7+
8+
`... ConfigMap ... is invalid: []: Too long: must have at most 1048576 bytes`.
9+
10+
Fixes issue [#6323](https://github.com/operator-framework/operator-sdk/issues/6323)
11+
12+
# kind is one of:
13+
# - addition
14+
# - change
15+
# - deprecation
16+
# - removal
17+
# - bugfix
18+
kind: "bugfix"
19+
20+
# Is this a breaking change?
21+
breaking: false
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// Copyright 2023 The Operator-SDK Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package fbcindex
16+
17+
import (
18+
"bytes"
19+
"compress/gzip"
20+
"fmt"
21+
22+
corev1 "k8s.io/api/core/v1"
23+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+
)
25+
26+
const (
27+
yamlSeparator = "\n---\n"
28+
gzipSuffixLength = 13
29+
maxGZIPLength = maxConfigMapSize - gzipSuffixLength
30+
31+
ConfigMapEncodingAnnotationKey = "olm.contentEncoding"
32+
ConfigMapEncodingAnnotationGzip = "gzip+base64"
33+
)
34+
35+
/*
36+
This file implements the actual building of the CM list. It uses the template method design pattern to implement both
37+
regular string VM, and compressed binary CM.
38+
39+
The method itself is FBCRegistryPod.getConfigMaps. This file contains the actual implementation of the writing actions,
40+
used by the method.
41+
*/
42+
43+
type configMapWriter interface {
44+
reset()
45+
newConfigMap(string) *corev1.ConfigMap
46+
getFilePath() string
47+
isEmpty() bool
48+
exceedMaxLength(cmSize int, data string) (bool, error)
49+
closeCM(cm *corev1.ConfigMap) error
50+
addData(data string) error
51+
continueAddData(data string) error
52+
writeLastFragment(cm *corev1.ConfigMap) error
53+
}
54+
55+
type gzipCMWriter struct {
56+
actualBuff *bytes.Buffer
57+
helperBuff *bytes.Buffer
58+
actualWriter *gzip.Writer
59+
helperWriter *gzip.Writer
60+
cmName string
61+
namespace string
62+
}
63+
64+
func newGZIPWriter(name, namespace string) *gzipCMWriter {
65+
actualBuff := &bytes.Buffer{}
66+
helperBuff := &bytes.Buffer{}
67+
68+
return &gzipCMWriter{
69+
actualBuff: actualBuff,
70+
helperBuff: helperBuff,
71+
actualWriter: gzip.NewWriter(actualBuff),
72+
helperWriter: gzip.NewWriter(helperBuff),
73+
cmName: name,
74+
namespace: namespace,
75+
}
76+
}
77+
78+
func (cmw *gzipCMWriter) reset() {
79+
cmw.actualBuff.Reset()
80+
cmw.actualWriter.Reset(cmw.actualBuff)
81+
cmw.helperBuff.Reset()
82+
cmw.helperWriter.Reset(cmw.helperBuff)
83+
}
84+
85+
func (cmw *gzipCMWriter) newConfigMap(name string) *corev1.ConfigMap {
86+
return &corev1.ConfigMap{
87+
TypeMeta: metav1.TypeMeta{
88+
APIVersion: corev1.SchemeGroupVersion.String(),
89+
Kind: "ConfigMap",
90+
},
91+
ObjectMeta: metav1.ObjectMeta{
92+
Namespace: cmw.namespace,
93+
Name: name,
94+
Annotations: map[string]string{
95+
ConfigMapEncodingAnnotationKey: ConfigMapEncodingAnnotationGzip,
96+
},
97+
},
98+
BinaryData: map[string][]byte{},
99+
}
100+
}
101+
102+
func (cmw *gzipCMWriter) getFilePath() string {
103+
return fmt.Sprintf("%s.yaml.gz", defaultConfigMapKey)
104+
}
105+
106+
func (cmw *gzipCMWriter) isEmpty() bool {
107+
return cmw.actualBuff.Len() > 0
108+
}
109+
110+
func (cmw *gzipCMWriter) exceedMaxLength(cmSize int, data string) (bool, error) {
111+
_, err := cmw.helperWriter.Write([]byte(data))
112+
if err != nil {
113+
return false, err
114+
}
115+
116+
err = cmw.helperWriter.Flush()
117+
if err != nil {
118+
return false, err
119+
}
120+
121+
return cmSize+cmw.helperBuff.Len() > maxGZIPLength, nil
122+
}
123+
124+
func (cmw *gzipCMWriter) closeCM(cm *corev1.ConfigMap) error {
125+
err := cmw.actualWriter.Close()
126+
if err != nil {
127+
return err
128+
}
129+
130+
err = cmw.actualWriter.Flush()
131+
if err != nil {
132+
return err
133+
}
134+
135+
cm.BinaryData[defaultConfigMapKey] = make([]byte, cmw.actualBuff.Len())
136+
copy(cm.BinaryData[defaultConfigMapKey], cmw.actualBuff.Bytes())
137+
138+
cmw.reset()
139+
140+
return nil
141+
}
142+
143+
func (cmw *gzipCMWriter) addData(data string) error {
144+
dataBytes := []byte(data)
145+
_, err := cmw.helperWriter.Write(dataBytes)
146+
if err != nil {
147+
return err
148+
}
149+
_, err = cmw.actualWriter.Write(dataBytes)
150+
if err != nil {
151+
return err
152+
}
153+
return nil
154+
}
155+
156+
// continueAddData completes adding the data after starting adding it in exceedMaxLength
157+
func (cmw *gzipCMWriter) continueAddData(data string) error {
158+
_, err := cmw.actualWriter.Write([]byte(data))
159+
if err != nil {
160+
return err
161+
}
162+
return nil
163+
}
164+
165+
func (cmw *gzipCMWriter) writeLastFragment(cm *corev1.ConfigMap) error {
166+
err := cmw.actualWriter.Close()
167+
if err != nil {
168+
return err
169+
}
170+
171+
cm.BinaryData[defaultConfigMapKey] = cmw.actualBuff.Bytes()
172+
return nil
173+
}

0 commit comments

Comments
 (0)