@@ -34,6 +34,8 @@ import (
34
34
"k8s.io/apimachinery/pkg/runtime"
35
35
"k8s.io/apimachinery/pkg/runtime/schema"
36
36
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
37
+ "sigs.k8s.io/controller-runtime/pkg/client"
38
+ "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
37
39
"sigs.k8s.io/controller-runtime/pkg/conversion"
38
40
logf "sigs.k8s.io/controller-runtime/pkg/log"
39
41
conversionmetrics "sigs.k8s.io/controller-runtime/pkg/webhook/conversion/metrics"
@@ -43,14 +45,110 @@ var (
43
45
log = logf .Log .WithName ("conversion-webhook" )
44
46
)
45
47
46
- func NewWebhookHandler (scheme * runtime.Scheme ) http.Handler {
47
- return & webhook {scheme : scheme , decoder : NewDecoder (scheme )}
48
+ type Registry struct {
49
+ scheme * runtime.Scheme
50
+ convertersByHubGK map [schema.GroupKind ]convertersForHub
51
+ }
52
+
53
+ func NewRegistry (scheme * runtime.Scheme ) Registry {
54
+ return Registry {
55
+ scheme : scheme ,
56
+ convertersByHubGK : map [schema.GroupKind ]convertersForHub {},
57
+ }
58
+ }
59
+
60
+ type convertersForHub struct {
61
+ hubGVK schema.GroupVersionKind
62
+ convertersBySpokeGVK map [schema.GroupVersionKind ]Converter
63
+ }
64
+
65
+ func (r Registry ) Register (hubGVK schema.GroupVersionKind , converters ... Converter ) error {
66
+ if _ , ok := r .convertersByHubGK [hubGVK .GroupKind ()]; ok {
67
+ return fmt .Errorf ("converter already registered for %s" , hubGVK .GroupKind ())
68
+ }
69
+
70
+ // TODO: validate against schema that all converters have been registred for a type (similar to previous validation)
71
+
72
+ r .convertersByHubGK [hubGVK .GroupKind ()] = convertersForHub {
73
+ hubGVK : hubGVK ,
74
+ convertersBySpokeGVK : map [schema.GroupVersionKind ]Converter {},
75
+ }
76
+ for _ , converter := range converters {
77
+ converterHubGVK , err := apiutil .GVKForObject (converter .GetHub (), r .scheme )
78
+ if err != nil {
79
+ return err
80
+ }
81
+ if hubGVK != converterHubGVK {
82
+ return fmt .Errorf ("converter GVK does not match builder gvk: FIXME" )
83
+ }
84
+ converterSpokeGVK , err := apiutil .GVKForObject (converter .GetSpoke (), r .scheme )
85
+ if err != nil {
86
+ return err
87
+ }
88
+ if hubGVK .GroupKind () != converterSpokeGVK .GroupKind () {
89
+ return fmt .Errorf ("converter GVK does not match builder gvk: FIXME" )
90
+ }
91
+ r .convertersByHubGK [hubGVK .GroupKind ()].convertersBySpokeGVK [converterSpokeGVK ] = converter
92
+ }
93
+
94
+ return nil
95
+ }
96
+
97
+ type Converter interface {
98
+ GetHub () client.Object
99
+ GetSpoke () client.Object
100
+ ConvertHubToSpoke (hub , spoke runtime.Object ) error
101
+ ConvertSpokeToHub (hub , spoke runtime.Object ) error
102
+ }
103
+
104
+ func NewConverter [hubObject , spokeObject client.Object ](
105
+ hub hubObject ,
106
+ spoke spokeObject ,
107
+ convertHubToSpokeFunc func (src hubObject , dst spokeObject ) error ,
108
+ convertSpokeToHubFunc func (src spokeObject , dst hubObject ) error ,
109
+ ) Converter {
110
+ return & converter [hubObject , spokeObject ]{
111
+ hub : hub ,
112
+ spoke : spoke ,
113
+ convertSpokeToHubFunc : convertSpokeToHubFunc ,
114
+ convertHubToSpokeFunc : convertHubToSpokeFunc ,
115
+ }
116
+ }
117
+
118
+ var _ Converter = converter [client.Object , client.Object ]{}
119
+
120
+ type converter [hubObject , spokeObject client.Object ] struct {
121
+ hub hubObject
122
+ spoke spokeObject
123
+ convertHubToSpokeFunc func (src hubObject , dst spokeObject ) error
124
+ convertSpokeToHubFunc func (src spokeObject , dst hubObject ) error
125
+ }
126
+
127
+ func (c converter [hubObject , spokeObject ]) GetHub () client.Object {
128
+ return c .hub
129
+ }
130
+
131
+ func (c converter [hubObject , spokeObject ]) GetSpoke () client.Object {
132
+ return c .spoke
133
+ }
134
+
135
+ func (c converter [hubObject , spokeObject ]) ConvertHubToSpoke (hub , spoke runtime.Object ) error {
136
+ return c .convertHubToSpokeFunc (hub .(hubObject ), spoke .(spokeObject ))
137
+ }
138
+
139
+ func (c converter [hubObject , spokeObject ]) ConvertSpokeToHub (hub , spoke runtime.Object ) error {
140
+ return c .convertSpokeToHubFunc (spoke .(spokeObject ), hub .(hubObject ))
141
+ }
142
+
143
+ func NewWebhookHandler (scheme * runtime.Scheme , registry Registry ) http.Handler {
144
+ return & webhook {scheme : scheme , decoder : NewDecoder (scheme ), registry : registry }
48
145
}
49
146
50
147
// webhook implements a CRD conversion webhook HTTP handler.
51
148
type webhook struct {
52
- scheme * runtime.Scheme
53
- decoder * Decoder
149
+ scheme * runtime.Scheme
150
+ decoder * Decoder
151
+ registry Registry
54
152
}
55
153
56
154
// ensure Webhook implements http.Handler
@@ -149,6 +247,34 @@ func (wh *webhook) convertObject(src, dst runtime.Object) error {
149
247
return fmt .Errorf ("conversion is not allowed between same type %T" , src )
150
248
}
151
249
250
+ if converters , ok := wh .registry .convertersByHubGK [srcGVK .GroupKind ()]; ok {
251
+ srcIsHub := converters .hubGVK == srcGVK
252
+ dstIsHub := converters .hubGVK == dstGVK
253
+ _ , srcIsConvertible := converters .convertersBySpokeGVK [srcGVK ]
254
+ _ , dstIsConvertible := converters .convertersBySpokeGVK [dstGVK ]
255
+
256
+ switch {
257
+ case srcIsHub && dstIsConvertible :
258
+ return converters .convertersBySpokeGVK [dstGVK ].ConvertHubToSpoke (src , dst )
259
+ case dstIsHub && srcIsConvertible :
260
+ return converters .convertersBySpokeGVK [srcGVK ].ConvertSpokeToHub (src , dst )
261
+ case srcIsConvertible && dstIsConvertible :
262
+ hubGVK := converters .hubGVK
263
+ hub , err := wh .scheme .New (hubGVK )
264
+ if err != nil {
265
+ return fmt .Errorf ("failed to allocate an instance for gvk %v: %w" , hubGVK , err )
266
+ }
267
+ if err := converters .convertersBySpokeGVK [srcGVK ].ConvertSpokeToHub (src , hub ); err != nil {
268
+ return fmt .Errorf ("%T failed to convert to hub version %T : %w" , src , hub , err )
269
+ }
270
+ if err := converters .convertersBySpokeGVK [dstGVK ].ConvertHubToSpoke (hub , dst ); err != nil {
271
+ return fmt .Errorf ("%T failed to convert from hub version %T : %w" , dst , hub , err )
272
+ }
273
+ default :
274
+ return fmt .Errorf ("%T is not convertible to %T" , src , dst )
275
+ }
276
+ }
277
+
152
278
srcIsHub , dstIsHub := isHub (src ), isHub (dst )
153
279
srcIsConvertible , dstIsConvertible := isConvertible (src ), isConvertible (dst )
154
280
0 commit comments