Skip to content

Commit cc4930e

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 74a356d commit cc4930e

File tree

4 files changed

+80
-21
lines changed

4 files changed

+80
-21
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: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"sync"
1212
"time"
1313

14+
apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
15+
1416
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/labeller"
1517
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/validatingroundtripper"
1618
errorwrap "github.com/pkg/errors"
@@ -149,7 +151,9 @@ func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clo
149151
}
150152

151153
// create a config that validates we're creating objects with labels
152-
validatingConfig := validatingroundtripper.Wrap(config)
154+
_ = apiextensionsv1.AddToScheme(scheme) // required by opClient
155+
_ = apiregistrationv1.AddToScheme(scheme) // required by opClient
156+
validatingConfig := validatingroundtripper.Wrap(config, scheme)
153157

154158
// Create a new client for dynamic types (CRs)
155159
dynamicClient, err := dynamic.NewForConfig(validatingConfig)

pkg/controller/operators/validatingroundtripper/validating_round_tripper.go

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ package validatingroundtripper
22

33
import (
44
"fmt"
5+
"io"
56
"net/http"
67
"os"
78

9+
"k8s.io/apimachinery/pkg/runtime"
10+
"k8s.io/apimachinery/pkg/runtime/serializer"
11+
812
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
913
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1014
"k8s.io/apimachinery/pkg/util/yaml"
@@ -13,23 +17,71 @@ import (
1317

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

1874
func (rt *validatingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
1975
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-
}
76+
unstructuredObject := rt.decodeRequestBody(req)
2977
gvk := unstructuredObject.GroupVersionKind()
3078
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))
79+
labels := unstructuredObject.GetLabels()
80+
if labels[install.OLMManagedLabelKey] != install.OLMManagedLabelValue {
81+
panic(fmt.Errorf("%s.%s/%v %s/%s does not have labels[%s]=%s",
82+
gvk.Kind, gvk.Group, gvk.Version,
83+
unstructuredObject.GetNamespace(), unstructuredObject.GetName(),
84+
install.OLMManagedLabelKey, install.OLMManagedLabelValue))
3385
}
3486
}
3587
}
@@ -40,14 +92,17 @@ var _ http.RoundTripper = (*validatingRoundTripper)(nil)
4092

4193
// Wrap is meant to be used in developer environments and CI to make it easy to find places
4294
// where we accidentally create Kubernetes objects without our management label.
43-
func Wrap(cfg *rest.Config) *rest.Config {
95+
func Wrap(cfg *rest.Config, scheme *runtime.Scheme) *rest.Config {
4496
if _, set := os.LookupEnv("CI"); !set {
4597
return cfg
4698
}
4799

48100
cfgCopy := *cfg
49101
cfgCopy.Wrap(func(rt http.RoundTripper) http.RoundTripper {
50-
return &validatingRoundTripper{delegate: rt}
102+
return &validatingRoundTripper{
103+
delegate: rt,
104+
codecs: serializer.NewCodecFactory(scheme),
105+
}
51106
})
52107
return &cfgCopy
53108
}

pkg/lib/operatorclient/client.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,25 +172,25 @@ func NewClientFromConfig(kubeconfig string, logger *logrus.Logger) ClientInterfa
172172
}
173173

174174
func NewClientFromRestConfig(config *rest.Config) (client ClientInterface, err error) {
175-
kubernetes, err := kubernetes.NewForConfig(config)
175+
k8s, err := kubernetes.NewForConfig(config)
176176
if err != nil {
177177
return
178178
}
179179

180-
apiextensions, err := apiextensions.NewForConfig(config)
180+
apiext, err := apiextensions.NewForConfig(config)
181181
if err != nil {
182182
return
183183
}
184184

185-
apiregistration, err := apiregistration.NewForConfig(config)
185+
apireg, err := apiregistration.NewForConfig(config)
186186
if err != nil {
187187
return
188188
}
189189

190190
client = &Client{
191-
kubernetes,
192-
apiextensions,
193-
apiregistration,
191+
k8s,
192+
apiext,
193+
apireg,
194194
}
195195

196196
return

0 commit comments

Comments
 (0)