Merge 341f7a2a8b into ca27889cd1
This commit is contained in:
commit
4748927d10
|
|
@ -45,6 +45,30 @@ func New() *Unstructured {
|
|||
return &Unstructured{unstructured.Unstructured{Object: make(map[string]any)}}
|
||||
}
|
||||
|
||||
// To converts a unstructured composed resource to the provided object.
|
||||
func To[T runtime.Object](un *Unstructured, obj T) error {
|
||||
|
||||
// Get known GVKs for the runtime object type
|
||||
knownGVKs, _, err := Scheme.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return errors.Errorf("could not retrieve GVKs for the provided object: %v", err)
|
||||
}
|
||||
|
||||
// Check if GVK is known as we should not try to convert it if it doesn't match
|
||||
gvkMatches := false
|
||||
for _, knownGVK := range knownGVKs {
|
||||
if knownGVK == un.GetObjectKind().GroupVersionKind() {
|
||||
gvkMatches = true
|
||||
}
|
||||
}
|
||||
|
||||
if !gvkMatches {
|
||||
return errors.Errorf("GVK %v is not known by the scheme for the provided object type", un.GetObjectKind().GroupVersionKind())
|
||||
}
|
||||
|
||||
return runtime.DefaultUnstructuredConverter.FromUnstructured(un.Object, obj)
|
||||
}
|
||||
|
||||
// From creates a new unstructured composed resource from the supplied object.
|
||||
func From(o runtime.Object) (*Unstructured, error) {
|
||||
// If the supplied object is already unstructured content, avoid a JSON
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package composed
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
|
|
@ -203,3 +204,150 @@ func TestFrom(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleTo() {
|
||||
// Add all v1beta2 types to the scheme so that From can automatically
|
||||
// determine their apiVersion and kind.
|
||||
v1beta2.AddToScheme(Scheme)
|
||||
|
||||
// Create a unstructured object as we would receive by the function (observed/desired).
|
||||
ub := &Unstructured{Unstructured: unstructured.Unstructured{Object: map[string]any{
|
||||
"apiVersion": v1beta2.CRDGroupVersion.String(),
|
||||
"kind": v1beta2.Bucket_Kind,
|
||||
"metadata": map[string]any{
|
||||
"name": "cool-bucket",
|
||||
},
|
||||
"spec": map[string]any{
|
||||
"forProvider": map[string]any{
|
||||
"region": "us-east-2",
|
||||
},
|
||||
},
|
||||
"status": map[string]any{
|
||||
"observedGeneration": float64(0),
|
||||
},
|
||||
}}}
|
||||
|
||||
// Create a strongly typed object from the unstructured object.
|
||||
sb := &v1beta2.Bucket{}
|
||||
err := To(ub, sb)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Now you have a strongly typed Bucket object.
|
||||
objectLock := true
|
||||
sb.Spec.ForProvider.ObjectLockEnabled = &objectLock
|
||||
}
|
||||
|
||||
// Test the To function
|
||||
func TestTo(t *testing.T) {
|
||||
v1beta2.AddToScheme(Scheme)
|
||||
type args struct {
|
||||
un *Unstructured
|
||||
obj runtime.Object
|
||||
}
|
||||
type want struct {
|
||||
obj interface{}
|
||||
err error
|
||||
}
|
||||
|
||||
cases := map[string]struct {
|
||||
reason string
|
||||
args args
|
||||
want want
|
||||
}{
|
||||
"SuccessfulConversion": {
|
||||
reason: "A valid unstructured object should convert to a structured object without errors",
|
||||
args: args{
|
||||
un: &Unstructured{Unstructured: unstructured.Unstructured{Object: map[string]any{
|
||||
"apiVersion": v1beta2.CRDGroupVersion.String(),
|
||||
"kind": v1beta2.Bucket_Kind,
|
||||
"metadata": map[string]any{
|
||||
"name": "cool-bucket",
|
||||
},
|
||||
"spec": map[string]any{
|
||||
"forProvider": map[string]any{
|
||||
"region": "us-east-2",
|
||||
},
|
||||
},
|
||||
"status": map[string]any{
|
||||
"observedGeneration": float64(0),
|
||||
},
|
||||
}}},
|
||||
obj: &v1beta2.Bucket{},
|
||||
},
|
||||
want: want{
|
||||
obj: &v1beta2.Bucket{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: v1beta2.Bucket_Kind,
|
||||
APIVersion: v1beta2.CRDGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "cool-bucket",
|
||||
},
|
||||
Spec: v1beta2.BucketSpec{
|
||||
ForProvider: v1beta2.BucketParameters{
|
||||
Region: ptr.To[string]("us-east-2"),
|
||||
},
|
||||
},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
"InvalidGVK": {
|
||||
reason: "An unstructured object with mismatched GVK should result in an error",
|
||||
args: args{
|
||||
un: &Unstructured{Unstructured: unstructured.Unstructured{Object: map[string]any{
|
||||
"apiVersion": "test.example.io",
|
||||
"kind": "Unknown",
|
||||
"metadata": map[string]any{
|
||||
"name": "cool-bucket",
|
||||
},
|
||||
"spec": map[string]any{
|
||||
"forProvider": map[string]any{
|
||||
"region": "us-east-2",
|
||||
},
|
||||
},
|
||||
"status": map[string]any{
|
||||
"observedGeneration": float64(0),
|
||||
},
|
||||
}}},
|
||||
obj: &v1beta2.Bucket{},
|
||||
},
|
||||
want: want{
|
||||
obj: &v1beta2.Bucket{},
|
||||
err: errors.New("GVK /test.example.io, Kind=Unknown is not known by the scheme for the provided object type"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
err := To(tc.args.un, tc.args.obj)
|
||||
|
||||
// Compare the resulting object with the expected one
|
||||
if diff := cmp.Diff(tc.want.obj, tc.args.obj); diff != "" {
|
||||
t.Errorf("\n%s\nTo(...): -want, +got:\n%s", tc.reason, diff)
|
||||
}
|
||||
// Compare the error with the expected error
|
||||
if diff := cmp.Diff(tc.want.err, err, EquateErrors()); diff != "" {
|
||||
t.Errorf("\n%s\nTo(...): -want error, +got error:\n%s", tc.reason, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// EquateErrors returns true if the supplied errors are of the same type and
|
||||
// produce identical strings. This mirrors the error comparison behaviour of
|
||||
// https://github.com/go-test/deep,
|
||||
//
|
||||
// This differs from cmpopts.EquateErrors, which does not test for error strings
|
||||
// and instead returns whether one error 'is' (in the errors.Is sense) the
|
||||
// other.
|
||||
func EquateErrors() cmp.Option {
|
||||
return cmp.Comparer(func(a, b error) bool {
|
||||
if a == nil || b == nil {
|
||||
return a == nil && b == nil
|
||||
}
|
||||
return a.Error() == b.Error()
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue