Skip to content

Commit cf37108

Browse files
committed
✨ address review comments
1 parent 16c1485 commit cf37108

17 files changed

+62
-129
lines changed

pkg/webhook/conversion/testData/pkg/apis/addtoscheme_jobs_v1.go examples/conversion/pkg/apis/addtoscheme_jobs_v1.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ limitations under the License.
1616
package apis
1717

1818
import (
19-
"sigs.k8s.io/controller-runtime/pkg/webhook/conversion/testData/pkg/apis/jobs/v1"
19+
"sigs.k8s.io/controller-runtime/examples/conversion/pkg/apis/jobs/v1"
2020
)
2121

2222
func init() {

pkg/webhook/conversion/testData/pkg/apis/addtoscheme_jobs_v2.go examples/conversion/pkg/apis/addtoscheme_jobs_v2.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ limitations under the License.
1616
package apis
1717

1818
import (
19-
"sigs.k8s.io/controller-runtime/pkg/webhook/conversion/testData/pkg/apis/jobs/v2"
19+
"sigs.k8s.io/controller-runtime/examples/conversion/pkg/apis/jobs/v2"
2020
)
2121

2222
func init() {

pkg/webhook/conversion/testData/pkg/apis/jobs/v1/externaljob_types.go examples/conversion/pkg/apis/jobs/v1/externaljob_types.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ import (
1919
"fmt"
2020

2121
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22+
"sigs.k8s.io/controller-runtime/examples/conversion/pkg/apis/jobs/v2"
2223
"sigs.k8s.io/controller-runtime/pkg/conversion"
23-
"sigs.k8s.io/controller-runtime/pkg/webhook/conversion/testData/pkg/apis/jobs/v2"
2424
)
2525

2626
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!

pkg/conversion/conversion.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17+
/*
18+
Package conversion provides interface definitions that an API Type needs to
19+
implement in order to be supported by the generic conversion webhook handler
20+
defined under pkg/webhook/conversion.
21+
*/
1722
package conversion
1823

1924
import "k8s.io/apimachinery/pkg/runtime"
@@ -25,9 +30,10 @@ type Convertible interface {
2530
ConvertFrom(src Hub) error
2631
}
2732

28-
// Hub defines capability to indicate whether a versioned type is a Hub or not.
29-
// Default conversion handler will use this interface to implement spoke to
30-
// spoke conversion.
33+
// Hub marks that a given type is the hub type for conversion. This means that
34+
// all conversions will first convert to the hub type, then convert from the hub
35+
// type to the destination type. All types besides the hub type should implement
36+
// Convertible.
3137
type Hub interface {
3238
runtime.Object
3339
Hub()

pkg/webhook/conversion/conversion.go

+34-57
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,29 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17+
/*
18+
Package conversion provides implementation for CRD conversion webhook that implements handler for version conversion requests for types that are convertible.
19+
20+
See pkg/conversion for interface definitions required to ensure a Type is convertible.
21+
*/
1722
package conversion
1823

1924
import (
2025
"encoding/json"
2126
"fmt"
22-
"io/ioutil"
2327
"net/http"
2428

2529
apix "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
2630
"k8s.io/apimachinery/pkg/api/meta"
31+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2732
"k8s.io/apimachinery/pkg/runtime"
2833
"k8s.io/apimachinery/pkg/runtime/schema"
2934
"sigs.k8s.io/controller-runtime/pkg/conversion"
3035
logf "sigs.k8s.io/controller-runtime/pkg/log"
3136
)
3237

3338
var (
34-
log = logf.Log.WithName("conversion_webhook")
39+
log = logf.Log.WithName("conversion-webhook")
3540
)
3641

3742
// Webhook implements a CRD conversion webhook HTTP handler.
@@ -49,22 +54,15 @@ func (wh *Webhook) InjectScheme(s *runtime.Scheme) error {
4954
return err
5055
}
5156

52-
// inject the decoder here too, just in case the order of calling this is not
53-
// scheme first, then inject func
54-
// if w.Handler != nil {
55-
// if _, err := InjectDecoderInto(w.GetDecoder(), w.Handler); err != nil {
56-
// return err
57-
// }
58-
// }
59-
6057
return nil
6158
}
6259

6360
// ensure Webhook implements http.Handler
6461
var _ http.Handler = &Webhook{}
6562

6663
func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
67-
convertReview, err := wh.readRequest(r)
64+
convertReview := &apix.ConversionReview{}
65+
err := json.NewDecoder(r.Body).Decode(convertReview)
6866
if err != nil {
6967
log.Error(err, "failed to read conversion request")
7068
w.WriteHeader(http.StatusBadRequest)
@@ -89,28 +87,6 @@ func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
8987
}
9088
}
9189

92-
func (wh *Webhook) readRequest(r *http.Request) (*apix.ConversionReview, error) {
93-
94-
var body []byte
95-
if r.Body == nil {
96-
return nil, fmt.Errorf("nil request body")
97-
}
98-
data, err := ioutil.ReadAll(r.Body)
99-
if err != nil {
100-
return nil, err
101-
}
102-
body = data
103-
104-
convertReview := &apix.ConversionReview{}
105-
// TODO(droot): figure out if we want to split decoder for conversion
106-
// request from the objects contained in the request
107-
err = wh.decoder.DecodeInto(body, convertReview)
108-
if err != nil {
109-
return nil, err
110-
}
111-
return convertReview, nil
112-
}
113-
11490
// handles a version conversion request.
11591
func (wh *Webhook) handleConvertRequest(req *apix.ConversionRequest) (*apix.ConversionResponse, error) {
11692
if req == nil {
@@ -142,58 +118,49 @@ func (wh *Webhook) handleConvertRequest(req *apix.ConversionRequest) (*apix.Conv
142118
// convertObject will convert given a src object to dst object.
143119
// Note(droot): couldn't find a way to reduce the cyclomatic complexity under 10
144120
// without compromising readability, so disabling gocyclo linter
145-
// nolint: gocyclo
146121
func (wh *Webhook) convertObject(src, dst runtime.Object) error {
147122
srcGVK := src.GetObjectKind().GroupVersionKind()
148123
dstGVK := dst.GetObjectKind().GroupVersionKind()
149124

150-
if srcGVK.GroupKind().String() != dstGVK.GroupKind().String() {
125+
if srcGVK.GroupKind() != dstGVK.GroupKind() {
151126
return fmt.Errorf("src %T and dst %T does not belong to same API Group", src, dst)
152127
}
153128

154-
if srcGVK.String() == dstGVK.String() {
129+
if srcGVK == dstGVK {
155130
return fmt.Errorf("conversion is not allowed between same type %T", src)
156131
}
157132

158133
srcIsHub, dstIsHub := isHub(src), isHub(dst)
159134
srcIsConvertible, dstIsConvertible := isConvertible(src), isConvertible(dst)
160135

161-
if srcIsHub {
162-
if dstIsConvertible {
163-
return dst.(conversion.Convertible).ConvertFrom(src.(conversion.Hub))
164-
}
136+
switch {
137+
case srcIsHub && dstIsConvertible:
138+
return dst.(conversion.Convertible).ConvertFrom(src.(conversion.Hub))
139+
case dstIsHub && srcIsConvertible:
140+
return src.(conversion.Convertible).ConvertTo(dst.(conversion.Hub))
141+
case srcIsConvertible && dstIsConvertible:
142+
return wh.convertViaHub(src.(conversion.Convertible), dst.(conversion.Convertible))
143+
default:
165144
return fmt.Errorf("%T is not convertible to %T", src, dst)
166145
}
146+
}
167147

168-
if dstIsHub {
169-
if srcIsConvertible {
170-
return src.(conversion.Convertible).ConvertTo(dst.(conversion.Hub))
171-
}
172-
return fmt.Errorf("%T is not convertible %T", dst, src)
173-
}
174-
175-
// neither src nor dst are Hub, means both of them are spoke, so lets get the hub
176-
// version type.
148+
func (wh *Webhook) convertViaHub(src, dst conversion.Convertible) error {
177149
hub, err := wh.getHub(src)
178150
if err != nil {
179151
return err
180152
}
181153

182154
if hub == nil {
183-
return fmt.Errorf("API Group %s does not have any Hub defined", srcGVK)
155+
return fmt.Errorf("%s does not have any Hub defined", src)
184156
}
185157

186-
// src and dst needs to be convertable for it to work
187-
if !srcIsConvertible || !dstIsConvertible {
188-
return fmt.Errorf("%T and %T needs to be convertable", src, dst)
189-
}
190-
191-
err = src.(conversion.Convertible).ConvertTo(hub)
158+
err = src.ConvertTo(hub)
192159
if err != nil {
193160
return fmt.Errorf("%T failed to convert to hub version %T : %v", src, hub, err)
194161
}
195162

196-
err = dst.(conversion.Convertible).ConvertFrom(hub)
163+
err = dst.ConvertFrom(hub)
197164
if err != nil {
198165
return fmt.Errorf("%T failed to convert from hub version %T : %v", dst, hub, err)
199166
}
@@ -256,3 +223,13 @@ func isConvertible(obj runtime.Object) bool {
256223
_, yes := obj.(conversion.Convertible)
257224
return yes
258225
}
226+
227+
// helper to construct error response.
228+
func errored(err error) *apix.ConversionResponse {
229+
return &apix.ConversionResponse{
230+
Result: metav1.Status{
231+
Status: metav1.StatusFailure,
232+
Message: err.Error(),
233+
},
234+
}
235+
}

pkg/webhook/conversion/conversion_suite_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,4 @@ var _ = BeforeSuite(func(done Done) {
3535
logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))
3636

3737
close(done)
38-
}, 60)
38+
})

pkg/webhook/conversion/conversion_test.go

+15-34
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ import (
3232
"k8s.io/apimachinery/pkg/runtime"
3333
kscheme "k8s.io/client-go/kubernetes/scheme"
3434

35-
jobsapis "sigs.k8s.io/controller-runtime/pkg/webhook/conversion/testData/pkg/apis"
36-
jobsv1 "sigs.k8s.io/controller-runtime/pkg/webhook/conversion/testData/pkg/apis/jobs/v1"
37-
jobsv2 "sigs.k8s.io/controller-runtime/pkg/webhook/conversion/testData/pkg/apis/jobs/v2"
35+
jobsapis "sigs.k8s.io/controller-runtime/examples/conversion/pkg/apis"
36+
jobsv1 "sigs.k8s.io/controller-runtime/examples/conversion/pkg/apis/jobs/v1"
37+
jobsv2 "sigs.k8s.io/controller-runtime/examples/conversion/pkg/apis/jobs/v2"
3838
)
3939

4040
var _ = Describe("Conversion Webhook", func() {
@@ -74,9 +74,8 @@ var _ = Describe("Conversion Webhook", func() {
7474
return convReview
7575
}
7676

77-
It("should convert objects successfully", func() {
78-
79-
v1Obj := &jobsv1.ExternalJob{
77+
makeV1Obj := func() *jobsv1.ExternalJob {
78+
return &jobsv1.ExternalJob{
8079
TypeMeta: metav1.TypeMeta{
8180
Kind: "ExternalJob",
8281
APIVersion: "jobs.example.org/v1",
@@ -89,6 +88,11 @@ var _ = Describe("Conversion Webhook", func() {
8988
RunAt: "every 2 seconds",
9089
},
9190
}
91+
}
92+
93+
It("should convert objects successfully", func() {
94+
95+
v1Obj := makeV1Obj()
9296

9397
expected := &jobsv2.ExternalJob{
9498
TypeMeta: metav1.TypeMeta{
@@ -118,26 +122,14 @@ var _ = Describe("Conversion Webhook", func() {
118122

119123
convReview := doRequest(convReq)
120124

121-
Expect(convReview.Response.ConvertedObjects).Should(HaveLen(1))
125+
Expect(convReview.Response.ConvertedObjects).To(HaveLen(1))
122126
got, _, err := decoder.Decode(convReview.Response.ConvertedObjects[0].Raw)
123127
Expect(err).NotTo(HaveOccurred())
124128
Expect(got).To(Equal(expected))
125129
})
126130

127131
It("should return error when dest/src objects belong to different API groups", func() {
128-
v1Obj := &jobsv1.ExternalJob{
129-
TypeMeta: metav1.TypeMeta{
130-
Kind: "ExternalJob",
131-
APIVersion: "jobs.example.org/v1",
132-
},
133-
ObjectMeta: metav1.ObjectMeta{
134-
Namespace: "default",
135-
Name: "obj-1",
136-
},
137-
Spec: jobsv1.ExternalJobSpec{
138-
RunAt: "every 2 seconds",
139-
},
140-
}
132+
v1Obj := makeV1Obj()
141133

142134
convReq := &apix.ConversionReview{
143135
TypeMeta: metav1.TypeMeta{},
@@ -159,19 +151,8 @@ var _ = Describe("Conversion Webhook", func() {
159151
})
160152

161153
It("should return error when dest/src objects are of same type", func() {
162-
v1Obj := &jobsv1.ExternalJob{
163-
TypeMeta: metav1.TypeMeta{
164-
Kind: "ExternalJob",
165-
APIVersion: "jobs.example.org/v1",
166-
},
167-
ObjectMeta: metav1.ObjectMeta{
168-
Namespace: "default",
169-
Name: "obj-1",
170-
},
171-
Spec: jobsv1.ExternalJobSpec{
172-
RunAt: "every 2 seconds",
173-
},
174-
}
154+
155+
v1Obj := makeV1Obj()
175156

176157
convReq := &apix.ConversionReview{
177158
TypeMeta: metav1.TypeMeta{},
@@ -220,7 +201,7 @@ var _ = Describe("Conversion Webhook", func() {
220201
convReview := doRequest(convReq)
221202

222203
Expect(convReview.Response.Result.Message).To(
223-
Equal("API Group apps/v1beta1, Kind=Deployment does not have any Hub defined"))
204+
Equal("*v1beta1.Deployment is not convertible to *v1.Deployment"))
224205
Expect(convReview.Response.ConvertedObjects).Should(BeEmpty())
225206
})
226207

pkg/webhook/conversion/response.go

-31
This file was deleted.

0 commit comments

Comments
 (0)