diff --git a/controller/helper.go b/controller/helper.go new file mode 100644 index 000000000..8c7c90136 --- /dev/null +++ b/controller/helper.go @@ -0,0 +1,51 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +type Callback func(interface{}) + +func EnsureTypeMeta(f Callback, gvk schema.GroupVersionKind) Callback { + apiVersion, kind := gvk.ToAPIVersionAndKind() + + return func(untyped interface{}) { + // TODO(mattmoor): Handle deletion. + typed, ok := untyped.(runtime.Object) + if !ok { + return + } + // We need to populated TypeMeta, but cannot trample the + // informer's copy. + // TODO(mattmoor): Avoid the copy if TypeMeta is set. + copy := typed.DeepCopyObject() + + accessor, err := meta.TypeAccessor(copy) + if err != nil { + return + } + accessor.SetAPIVersion(apiVersion) + accessor.SetKind(kind) + + // Pass in the mutated copy (accessor is not just a type cast) + f(copy) + } +} diff --git a/controller/helper_test.go b/controller/helper_test.go new file mode 100644 index 000000000..04553a506 --- /dev/null +++ b/controller/helper_test.go @@ -0,0 +1,87 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + + . "github.com/knative/pkg/testing" +) + +func TestEnsureTypeMeta(t *testing.T) { + gvk := schema.GroupVersionKind{ + Group: "foo.bar.com", + Version: "v23", + Kind: "Magic", + } + apiVersion, kind := gvk.ToAPIVersionAndKind() + + tests := []struct { + name string + obj interface{} + want runtime.Object + }{{ + name: "not a runtime.Object", + obj: struct{}{}, + }, { + name: "called with type meta", + obj: &Resource{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "thing", + }, + }, + want: &Resource{ + TypeMeta: metav1.TypeMeta{ + APIVersion: apiVersion, + Kind: kind, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "thing", + }, + }, + }} + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + var cb Callback + if test.want != nil { + cb = func(got interface{}) { + if diff := cmp.Diff(got, test.want); diff != "" { + t.Errorf("EnsureTypeMeta = %s", diff) + } + } + } else { + cb = func(got interface{}) { + t.Errorf("Wanted no call, got %#v", got) + } + } + + ncb := EnsureTypeMeta(cb, gvk) + + // This should either invoke the callback or not, the + // rest of the test is in the configured callback. + ncb(test.obj) + }) + } +}