Skip to content

Commit

Permalink
refactor ToUnstructured helper to a new package
Browse files Browse the repository at this point in the history
  • Loading branch information
dprotaso committed Jun 9, 2021
1 parent 1582291 commit fd279b9
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 77 deletions.
46 changes: 6 additions & 40 deletions injection/clients/dynamicclient/fake/fake.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ package fake

import (
"context"
"encoding/json"
"strings"

"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/dynamic/fake"
Expand All @@ -31,6 +29,7 @@ import (
"knative.dev/pkg/injection"
"knative.dev/pkg/injection/clients/dynamicclient"
"knative.dev/pkg/logging"
pkgunstructured "knative.dev/pkg/unstructured"
)

func init() {
Expand Down Expand Up @@ -61,7 +60,11 @@ func With(ctx context.Context, scheme *runtime.Scheme, objects ...runtime.Object
unstructuredScheme.AddKnownTypeWithName(gvk, &unstructured.Unstructured{})
}

objects = ToUnstructured(scheme, objects)
objects, err := pkgunstructured.ConvertManyToObjects(scheme, objects)
if err != nil {
panic(err)
}

for _, obj := range objects {
gvk := obj.GetObjectKind().GroupVersionKind()
if !unstructuredScheme.Recognizes(gvk) {
Expand All @@ -86,40 +89,3 @@ func Get(ctx context.Context) *fake.FakeDynamicClient {
}
return untyped.(*fake.FakeDynamicClient)
}

// ToUnstructured takes a list of k8s resources and converts them to
// Unstructured objects.
// We must pass objects as Unstructured to the dynamic client fake, or it
// won't handle them properly.
func ToUnstructured(sch *runtime.Scheme, objs []runtime.Object) (us []runtime.Object) {
for _, obj := range objs {
// Don't mess with the primary copy
obj = obj.DeepCopyObject()

ta, err := meta.TypeAccessor(obj)
if err != nil {
panic("Unable to create type accessor: " + err.Error())
}
if ta.GetAPIVersion() == "" || ta.GetKind() == "" {
// Determine and set the TypeMeta for this object based on our test scheme.
gvks, _, err := sch.ObjectKinds(obj)
if err != nil {
panic("Unable to determine kind for type: " + err.Error())
}
apiv, k := gvks[0].ToAPIVersionAndKind()
ta.SetAPIVersion(apiv)
ta.SetKind(k)
}

b, err := json.Marshal(obj)
if err != nil {
panic("Unable to marshal: " + err.Error())
}
u := &unstructured.Unstructured{}
if err := json.Unmarshal(b, u); err != nil {
panic("Unable to unmarshal: " + err.Error())
}
us = append(us, u)
}
return
}
80 changes: 80 additions & 0 deletions unstructured/unstructured.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
Copyright 2021 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
http://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 unstructured

import (
"fmt"

"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
)

// ConvertTo converts a runtime.Object to an unstructured.Unstructured type
func ConvertTo(s *runtime.Scheme, obj runtime.Object) (*unstructured.Unstructured, error) {
var (
err error
u unstructured.Unstructured
)

u.Object, err = runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
if err != nil {
return nil, fmt.Errorf("failed to convert to unstructured: %w", err)
}

gvk := u.GroupVersionKind()
if gvk.Group == "" || gvk.Kind == "" {
gvks, _, err := s.ObjectKinds(obj)
if err != nil {
return nil, fmt.Errorf("failed to convert to unstructured: %w", err)
}
apiv, k := gvks[0].ToAPIVersionAndKind()
u.SetAPIVersion(apiv)
u.SetKind(k)
}
return &u, nil
}

// ConvertManyTo converts a slice of runtime.Object to a slice of *unstructured.Unstructured
func ConvertManyTo(s *runtime.Scheme, objs []runtime.Object) ([]*unstructured.Unstructured, error) {
ul := make([]*unstructured.Unstructured, 0, len(objs))

for _, obj := range objs {
u, err := ConvertTo(s, obj)
if err != nil {
return nil, err
}

ul = append(ul, u)
}
return ul, nil
}

// ConvertManyToObjects converts a slice of runtime.Object to a slice of runtime.Objects
// where each element is of the type *unstructured.Unstructured
func ConvertManyToObjects(s *runtime.Scheme, objs []runtime.Object) ([]runtime.Object, error) {
ul := make([]runtime.Object, 0, len(objs))

for _, obj := range objs {
u, err := ConvertTo(s, obj)
if err != nil {
return nil, err
}

ul = append(ul, u)
}
return ul, nil
}
46 changes: 9 additions & 37 deletions webhook/testing/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package testing

import (
"context"
"encoding/json"
"testing"

fakeapixclient "knative.dev/pkg/client/injection/apiextensions/client/fake"
Expand All @@ -30,9 +29,8 @@ import (
"knative.dev/pkg/controller"
"knative.dev/pkg/logging"
logtesting "knative.dev/pkg/logging/testing"
"knative.dev/pkg/unstructured"

"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ktesting "k8s.io/client-go/testing"
Expand Down Expand Up @@ -63,9 +61,16 @@ func MakeFactory(ctor Ctor) rtesting.Factory {
logger := logtesting.TestLogger(t)
ctx = logging.WithLogger(ctx, logger)

scheme := ls.NewScheme()

objs, err := unstructured.ConvertManyToObjects(scheme, r.Objects)
if err != nil {
t.Fatal("failed to setup fake dynamic client", err)
}

ctx, kubeClient := fakekubeclient.With(ctx, ls.GetKubeObjects()...)
ctx, apixClient := fakeapixclient.With(ctx, ls.GetAPIExtensionsObjects()...)
ctx, dynamicClient := fakedynamicclient.With(ctx, ls.NewScheme(), ToUnstructured(t, ls.NewScheme(), r.Objects)...)
ctx, dynamicClient := fakedynamicclient.With(ctx, scheme, objs...)

// The dynamic client's support for patching is BS. Implement it
// here via PrependReactor (this can be overridden below by the
Expand Down Expand Up @@ -117,36 +122,3 @@ func MakeFactory(ctor Ctor) rtesting.Factory {
return c, actionRecorderList, eventList
}
}

// ToUnstructured takes a list of k8s resources and converts them to
// Unstructured objects.
// We must pass objects as Unstructured to the dynamic client fake, or it
// won't handle them properly.
func ToUnstructured(t *testing.T, sch *runtime.Scheme, objs []runtime.Object) (us []runtime.Object) {
for _, obj := range objs {
obj = obj.DeepCopyObject() // Don't mess with the primary copy
// Determine and set the TypeMeta for this object based on our test scheme.
gvks, _, err := sch.ObjectKinds(obj)
if err != nil {
t.Fatal("Unable to determine kind for type:", err)
}
apiv, k := gvks[0].ToAPIVersionAndKind()
ta, err := meta.TypeAccessor(obj)
if err != nil {
t.Fatal("Unable to create type accessor:", err)
}
ta.SetAPIVersion(apiv)
ta.SetKind(k)

b, err := json.Marshal(obj)
if err != nil {
t.Fatal("Unable to marshal:", err)
}
u := &unstructured.Unstructured{}
if err := json.Unmarshal(b, u); err != nil {
t.Fatal("Unable to unmarshal:", err)
}
us = append(us, u)
}
return
}

0 comments on commit fd279b9

Please sign in to comment.