Skip to content

Commit a3e3dfa

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 616897a commit a3e3dfa

File tree

5 files changed

+84
-20
lines changed

5 files changed

+84
-20
lines changed

cmd/catalog/main.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import (
77
"os"
88
"time"
99

10+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
11+
apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
12+
1013
configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
1114
"github.com/sirupsen/logrus"
1215
k8sscheme "k8s.io/client-go/kubernetes/scheme"
@@ -88,6 +91,14 @@ func (o *options) run(ctx context.Context, logger *logrus.Logger) error {
8891
if o.setWorkloadUserID {
8992
workloadUserID = defaultWorkLoadUserID
9093
}
94+
95+
// the scheme is used by the catalog operator to create
96+
// a validatingroundtripper that ensures that all created
97+
// resources are appropriately labeled
98+
scheme := k8sscheme.Scheme
99+
_ = apiextensionsv1.AddToScheme(scheme) // required by opClient
100+
_ = apiregistrationv1.AddToScheme(scheme) // required by opClient
101+
91102
// TODO(tflannag): Use options pattern for catalog operator
92103
// Create a new instance of the operator.
93104
op, err := catalog.NewOperator(
@@ -100,7 +111,7 @@ func (o *options) run(ctx context.Context, logger *logrus.Logger) error {
100111
o.opmImage,
101112
o.utilImage,
102113
o.catalogNamespace,
103-
k8sscheme.Scheme,
114+
scheme,
104115
o.installPlanTimeout,
105116
o.bundleUnpackTimeout,
106117
workloadUserID,

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)

pkg/controller/operators/validatingroundtripper/validating_round_tripper.go

Lines changed: 64 additions & 11 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,69 @@ 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+
return nil, 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, error) {
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+
switch req.Header.Get("Content-Type") {
60+
case "application/vnd.kubernetes.protobuf":
61+
return rt.decodeProtobuf(b)
62+
default:
63+
return rt.decodeYAMLOrJSON(b)
64+
}
1665
}
1766

1867
func (rt *validatingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
1968
if req.Method == "POST" {
20-
b, err := req.GetBody()
69+
unstructuredObject, err := rt.decodeRequestBody(req)
70+
2171
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))
72+
return nil, err
2873
}
74+
2975
gvk := unstructuredObject.GroupVersionKind()
3076
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))
77+
labels := unstructuredObject.GetLabels()
78+
if labels[install.OLMManagedLabelKey] != install.OLMManagedLabelValue {
79+
panic(fmt.Errorf("%s.%s/%v %s/%s does not have labels[%s]=%s",
80+
gvk.Kind, gvk.Group, gvk.Version,
81+
unstructuredObject.GetNamespace(), unstructuredObject.GetName(),
82+
install.OLMManagedLabelKey, install.OLMManagedLabelValue))
3383
}
3484
}
3585
}
@@ -40,14 +90,17 @@ var _ http.RoundTripper = (*validatingRoundTripper)(nil)
4090

4191
// Wrap is meant to be used in developer environments and CI to make it easy to find places
4292
// where we accidentally create Kubernetes objects without our management label.
43-
func Wrap(cfg *rest.Config) *rest.Config {
93+
func Wrap(cfg *rest.Config, scheme *runtime.Scheme) *rest.Config {
4494
if _, set := os.LookupEnv("CI"); !set {
4595
return cfg
4696
}
4797

4898
cfgCopy := *cfg
4999
cfgCopy.Wrap(func(rt http.RoundTripper) http.RoundTripper {
50-
return &validatingRoundTripper{delegate: rt}
100+
return &validatingRoundTripper{
101+
delegate: rt,
102+
codecs: serializer.NewCodecFactory(scheme),
103+
}
51104
})
52105
return &cfgCopy
53106
}

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)