1
2
3
4
19
20 package internal
21
22 import (
23 "fmt"
24
25 "k8s.io/apimachinery/pkg/runtime"
26 "k8s.io/apimachinery/pkg/runtime/schema"
27 "sigs.k8s.io/controller-runtime/pkg/conversion"
28 )
29
30 type Webhook struct {
31 scheme *runtime.Scheme
32 }
33
34
35
36
37 func (wh *Webhook) convertObject(src, dst runtime.Object) error {
38 srcGVK := src.GetObjectKind().GroupVersionKind()
39 dstGVK := dst.GetObjectKind().GroupVersionKind()
40
41 if srcGVK.GroupKind() != dstGVK.GroupKind() {
42 return fmt.Errorf("src %T and dst %T does not belong to same API Group", src, dst)
43 }
44
45 if srcGVK == dstGVK {
46 return fmt.Errorf("conversion is not allowed between same type %T", src)
47 }
48
49 srcIsHub, dstIsHub := isHub(src), isHub(dst)
50 srcIsConvertible, dstIsConvertible := isConvertible(src), isConvertible(dst)
51
52 switch {
53 case srcIsHub && dstIsConvertible:
54 return dst.(conversion.Convertible).ConvertFrom(src.(conversion.Hub))
55 case dstIsHub && srcIsConvertible:
56 return src.(conversion.Convertible).ConvertTo(dst.(conversion.Hub))
57 case srcIsConvertible && dstIsConvertible:
58 return wh.convertViaHub(src.(conversion.Convertible), dst.(conversion.Convertible))
59 default:
60 return fmt.Errorf("%T is not convertible to %T", src, dst)
61 }
62 }
63
64 func (wh *Webhook) convertViaHub(src, dst conversion.Convertible) error {
65 hub, err := wh.getHub(src)
66 if err != nil {
67 return err
68 }
69
70 if hub == nil {
71 return fmt.Errorf("%s does not have any Hub defined", src)
72 }
73
74 err = src.ConvertTo(hub)
75 if err != nil {
76 return fmt.Errorf("%T failed to convert to hub version %T : %w", src, hub, err)
77 }
78
79 err = dst.ConvertFrom(hub)
80 if err != nil {
81 return fmt.Errorf("%T failed to convert from hub version %T : %w", dst, hub, err)
82 }
83
84 return nil
85 }
86
87
88 func (wh *Webhook) getHub(obj runtime.Object) (conversion.Hub, error) {
89 gvks, err := objectGVKs(wh.scheme, obj)
90 if err != nil {
91 return nil, err
92 }
93 if len(gvks) == 0 {
94 return nil, fmt.Errorf("error retrieving gvks for object : %v", obj)
95 }
96
97 var hub conversion.Hub
98 var hubFoundAlready bool
99 for _, gvk := range gvks {
100 instance, err := wh.scheme.New(gvk)
101 if err != nil {
102 return nil, fmt.Errorf("failed to allocate an instance for gvk %v: %w", gvk, err)
103 }
104 if val, isHub := instance.(conversion.Hub); isHub {
105 if hubFoundAlready {
106 return nil, fmt.Errorf("multiple hub version defined for %T", obj)
107 }
108 hubFoundAlready = true
109 hub = val
110 }
111 }
112 return hub, nil
113 }
114
115
116 func objectGVKs(scheme *runtime.Scheme, obj runtime.Object) ([]schema.GroupVersionKind, error) {
117
118
119
120 objGVKs, _, err := scheme.ObjectKinds(obj)
121 if err != nil {
122 return nil, err
123 }
124 if len(objGVKs) != 1 {
125 return nil, fmt.Errorf("expect to get only one GVK for %v", obj)
126 }
127 objGVK := objGVKs[0]
128 knownTypes := scheme.AllKnownTypes()
129
130 var gvks []schema.GroupVersionKind
131 for gvk := range knownTypes {
132 if objGVK.GroupKind() == gvk.GroupKind() {
133 gvks = append(gvks, gvk)
134 }
135 }
136 return gvks, nil
137 }
138
139
140 func isHub(obj runtime.Object) bool {
141 _, yes := obj.(conversion.Hub)
142 return yes
143 }
144
145
146 func isConvertible(obj runtime.Object) bool {
147 _, yes := obj.(conversion.Convertible)
148 return yes
149 }
150
View as plain text