Skip to content

Commit 8735be1

Browse files
author
Per Goncalves da Silva
committed
add protobuf object handling to validating round tripper
Signed-off-by: Per Goncalves da Silva <[email protected]>
1 parent f5058f7 commit 8735be1

File tree

3 files changed

+74
-15
lines changed

3 files changed

+74
-15
lines changed

cmd/olm/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ func main() {
141141
config := mgr.GetConfig()
142142

143143
// create a config that validates we're creating objects with labels
144-
validatingConfig := validatingroundtripper.Wrap(config)
144+
validatingConfig := validatingroundtripper.Wrap(config, mgr.GetScheme())
145145

146146
versionedConfigClient, err := configclientset.NewForConfig(config)
147147
if err != nil {

pkg/controller/operators/catalog/operator.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clo
149149
}
150150

151151
// create a config that validates we're creating objects with labels
152-
validatingConfig := validatingroundtripper.Wrap(config)
152+
validatingConfig := validatingroundtripper.Wrap(config, scheme)
153153

154154
// Create a new client for dynamic types (CRs)
155155
dynamicClient, err := dynamic.NewForConfig(validatingConfig)
Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
package validatingroundtripper
22

33
import (
4+
"bytes"
45
"fmt"
6+
"io"
57
"net/http"
68
"os"
79

10+
"k8s.io/apimachinery/pkg/runtime"
11+
"k8s.io/apimachinery/pkg/runtime/serializer"
12+
813
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
914
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1015
"k8s.io/apimachinery/pkg/util/yaml"
@@ -13,23 +18,74 @@ import (
1318

1419
type validatingRoundTripper struct {
1520
delegate http.RoundTripper
21+
codecs serializer.CodecFactory
22+
}
23+
24+
func (rt *validatingRoundTripper) decodeYAMLOrJSON(data []byte) (*unstructured.Unstructured, error) {
25+
bodyBuffer := bytes.NewBuffer(data)
26+
dec := yaml.NewYAMLOrJSONDecoder(bodyBuffer, 10)
27+
unstructuredObject := &unstructured.Unstructured{}
28+
if err := dec.Decode(unstructuredObject); err != nil {
29+
return nil, fmt.Errorf("error decoding yaml/json object to an unstructured object: %w: %+v", err, string(data))
30+
}
31+
return unstructuredObject, nil
32+
}
33+
34+
func (rt *validatingRoundTripper) decodeProtobuf(data []byte) (*unstructured.Unstructured, error) {
35+
// Decode Protobuf
36+
decoder := rt.codecs.UniversalDeserializer()
37+
obj, _, err := decoder.Decode(data, nil, nil)
38+
if err != nil {
39+
return nil, fmt.Errorf("failed to decode protobuf data: %w", err)
40+
}
41+
42+
// Convert to Unstructured for further processing
43+
unstructuredObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
44+
if err != nil {
45+
return nil, fmt.Errorf("failed to convert object to unstructured: %w", err)
46+
}
47+
48+
return &unstructured.Unstructured{Object: unstructuredObj}, nil
49+
}
50+
51+
func (rt *validatingRoundTripper) decodeRequestBody(req *http.Request) *unstructured.Unstructured {
52+
b, err := req.GetBody()
53+
if err != nil {
54+
panic(fmt.Errorf("failed to get request body: %w", err))
55+
}
56+
defer b.Close()
57+
58+
data, err := io.ReadAll(b)
59+
if err != nil {
60+
panic(fmt.Errorf("failed to read request body: %w", err))
61+
}
62+
63+
var unstructuredObject *unstructured.Unstructured
64+
switch req.Header.Get("Content-Type") {
65+
case "application/vnd.kubernetes.protobuf":
66+
unstructuredObject, err = rt.decodeProtobuf(data)
67+
default:
68+
unstructuredObject, err = rt.decodeYAMLOrJSON(data)
69+
}
70+
71+
if err != nil {
72+
panic(fmt.Errorf("failed to decode request body: %w", err))
73+
}
74+
75+
return unstructuredObject
1676
}
1777

1878
func (rt *validatingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
1979
if req.Method == "POST" {
20-
b, err := req.GetBody()
21-
if err != nil {
22-
panic(err)
23-
}
24-
dec := yaml.NewYAMLOrJSONDecoder(b, 10)
25-
unstructuredObject := &unstructured.Unstructured{}
26-
if err := dec.Decode(unstructuredObject); err != nil {
27-
panic(fmt.Errorf("error decoding object to an unstructured object: %w", err))
28-
}
80+
unstructuredObject := rt.decodeRequestBody(req)
2981
gvk := unstructuredObject.GroupVersionKind()
3082
if gvk.Kind != "Event" {
31-
if labels := unstructuredObject.GetLabels(); labels[install.OLMManagedLabelKey] != install.OLMManagedLabelValue {
32-
panic(fmt.Errorf("%s.%s/%v %s/%s does not have labels[%s]=%s", gvk.Kind, gvk.Group, gvk.Version, unstructuredObject.GetNamespace(), unstructuredObject.GetName(), install.OLMManagedLabelKey, install.OLMManagedLabelValue))
83+
labels := unstructuredObject.GetLabels()
84+
if labels[install.OLMManagedLabelKey] != install.OLMManagedLabelValue {
85+
panic(fmt.Errorf("%s.%s/%v %s/%s does not have labels[%s]=%s",
86+
gvk.Kind, gvk.Group, gvk.Version,
87+
unstructuredObject.GetNamespace(), unstructuredObject.GetName(),
88+
install.OLMManagedLabelKey, install.OLMManagedLabelValue))
3389
}
3490
}
3591
}
@@ -40,14 +96,17 @@ var _ http.RoundTripper = (*validatingRoundTripper)(nil)
4096

4197
// Wrap is meant to be used in developer environments and CI to make it easy to find places
4298
// where we accidentally create Kubernetes objects without our management label.
43-
func Wrap(cfg *rest.Config) *rest.Config {
99+
func Wrap(cfg *rest.Config, scheme *runtime.Scheme) *rest.Config {
44100
if _, set := os.LookupEnv("CI"); !set {
45101
return cfg
46102
}
47103

48104
cfgCopy := *cfg
49105
cfgCopy.Wrap(func(rt http.RoundTripper) http.RoundTripper {
50-
return &validatingRoundTripper{delegate: rt}
106+
return &validatingRoundTripper{
107+
delegate: rt,
108+
codecs: serializer.NewCodecFactory(scheme),
109+
}
51110
})
52111
return &cfgCopy
53112
}

0 commit comments

Comments
 (0)