Skip to content

Commit f02ac99

Browse files
Merge pull request #1767 from awgreene/fix-conversion-webhooks
BUG 1872584: Fix conversion webhooks
2 parents 026fa7a + 5b7f265 commit f02ac99

File tree

18 files changed

+269
-188
lines changed

18 files changed

+269
-188
lines changed

deploy/chart/crds/0000_50_olm_00-clusterserviceversions.crd.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8824,6 +8824,11 @@ spec:
88248824
type: string
88258825
sideEffects:
88268826
type: string
8827+
targetPort:
8828+
anyOf:
8829+
- type: integer
8830+
- type: string
8831+
x-kubernetes-int-or-string: true
88278832
timeoutSeconds:
88288833
type: integer
88298834
format: int32
@@ -8834,6 +8839,7 @@ spec:
88348839
enum:
88358840
- ValidatingAdmissionWebhook
88368841
- MutatingAdmissionWebhook
8842+
- ConversionWebhook
88378843
webhookPath:
88388844
type: string
88398845
status:

deploy/ocp/manifests/0.16.1/0000_50_olm_00-clusterserviceversions.crd.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8826,6 +8826,11 @@ spec:
88268826
type: string
88278827
sideEffects:
88288828
type: string
8829+
targetPort:
8830+
anyOf:
8831+
- type: integer
8832+
- type: string
8833+
x-kubernetes-int-or-string: true
88298834
timeoutSeconds:
88308835
type: integer
88318836
format: int32
@@ -8836,6 +8841,7 @@ spec:
88368841
enum:
88378842
- ValidatingAdmissionWebhook
88388843
- MutatingAdmissionWebhook
8844+
- ConversionWebhook
88398845
webhookPath:
88408846
type: string
88418847
status:

deploy/upstream/manifests/0.16.1/0000_50_olm_00-clusterserviceversions.crd.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8826,6 +8826,11 @@ spec:
88268826
type: string
88278827
sideEffects:
88288828
type: string
8829+
targetPort:
8830+
anyOf:
8831+
- type: integer
8832+
- type: string
8833+
x-kubernetes-int-or-string: true
88298834
timeoutSeconds:
88308835
type: integer
88318836
format: int32
@@ -8836,6 +8841,7 @@ spec:
88368841
enum:
88378842
- ValidatingAdmissionWebhook
88388843
- MutatingAdmissionWebhook
8844+
- ConversionWebhook
88398845
webhookPath:
88408846
type: string
88418847
status:

deploy/upstream/quickstart/crds.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9025,6 +9025,11 @@ spec:
90259025
type: string
90269026
sideEffects:
90279027
type: string
9028+
targetPort:
9029+
anyOf:
9030+
- type: integer
9031+
- type: string
9032+
x-kubernetes-int-or-string: true
90289033
timeoutSeconds:
90299034
type: integer
90309035
format: int32
@@ -9035,6 +9040,7 @@ spec:
90359040
enum:
90369041
- ValidatingAdmissionWebhook
90379042
- MutatingAdmissionWebhook
9043+
- ConversionWebhook
90389044
webhookPath:
90399045
type: string
90409046
status:

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ require (
2323
github.com/onsi/gomega v1.9.0
2424
github.com/openshift/api v0.0.0-20200331152225-585af27e34fd
2525
github.com/openshift/client-go v0.0.0-20200326155132-2a6cd50aedd0
26-
github.com/operator-framework/api v0.3.12
26+
github.com/operator-framework/api v0.3.15
2727
github.com/operator-framework/operator-registry v1.13.6
2828
github.com/otiai10/copy v1.2.0
2929
github.com/pkg/errors v0.9.1

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -597,8 +597,8 @@ github.com/openshift/client-go v0.0.0-20200326155132-2a6cd50aedd0 h1:kMiuiZXH1Gd
597597
github.com/openshift/client-go v0.0.0-20200326155132-2a6cd50aedd0/go.mod h1:uUQ4LClRO+fg5MF/P6QxjMCb1C9f7Oh4RKepftDnEJE=
598598
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
599599
github.com/operator-framework/api v0.3.7-0.20200602203552-431198de9fc2/go.mod h1:Xbje9x0SHmh0nihE21kpesB38vk3cyxnE6JdDS8Jo1Q=
600-
github.com/operator-framework/api v0.3.12 h1:In6bSbDr5zVOb5ats7TBMnl1h0hCIjkAxXkh+MXFJps=
601-
github.com/operator-framework/api v0.3.12/go.mod h1:Xbje9x0SHmh0nihE21kpesB38vk3cyxnE6JdDS8Jo1Q=
600+
github.com/operator-framework/api v0.3.15 h1:gVVx9y8o9RpY8+bzSCo+WLi+LZylYI8zp4Gtlei8Mac=
601+
github.com/operator-framework/api v0.3.15/go.mod h1:Xbje9x0SHmh0nihE21kpesB38vk3cyxnE6JdDS8Jo1Q=
602602
github.com/operator-framework/operator-registry v1.13.6 h1:h/dIjQQS7uneQNRifrSz7h0xg4Xyjg6C9f6XZofbMPg=
603603
github.com/operator-framework/operator-registry v1.13.6/go.mod h1:YhnIzOVjRU2ZwZtzt+fjcjW8ujJaSFynBEu7QVKaSdU=
604604
github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k=

manifests/0000_50_olm_00-clusterserviceversions.crd.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8824,6 +8824,11 @@ spec:
88248824
type: string
88258825
sideEffects:
88268826
type: string
8827+
targetPort:
8828+
anyOf:
8829+
- type: integer
8830+
- type: string
8831+
x-kubernetes-int-or-string: true
88278832
timeoutSeconds:
88288833
type: integer
88298834
format: int32
@@ -8834,6 +8839,7 @@ spec:
88348839
enum:
88358840
- ValidatingAdmissionWebhook
88368841
- MutatingAdmissionWebhook
8842+
- ConversionWebhook
88378843
webhookPath:
88388844
type: string
88398845
status:

pkg/controller/install/certresources.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,18 @@ func (i *webhookDescriptionWithCAPEM) getServicePort() corev1.ServicePort {
125125
if i.webhookDescription.ContainerPort > 0 {
126126
containerPort = int(i.webhookDescription.ContainerPort)
127127
}
128+
129+
// Before users could set TargetPort in the CSV, OLM just set its
130+
// value to the containerPort. This change keeps OLM backwards compatible
131+
// if the TargetPort is not set in the CSV.
132+
targetPort := intstr.FromInt(containerPort)
133+
if i.webhookDescription.TargetPort != nil {
134+
targetPort = *i.webhookDescription.TargetPort
135+
}
128136
return corev1.ServicePort{
129137
Name: strconv.Itoa(containerPort),
130138
Port: int32(containerPort),
131-
TargetPort: intstr.FromInt(containerPort),
139+
TargetPort: targetPort,
132140
}
133141
}
134142

pkg/controller/install/webhook.go

Lines changed: 47 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ func (i *StrategyDeploymentInstaller) createOrUpdateWebhook(caPEM []byte, desc v
6969
i.createOrUpdateValidatingWebhook(ogNamespacelabelSelector, caPEM, desc)
7070
case v1alpha1.MutatingAdmissionWebhook:
7171
i.createOrUpdateMutatingWebhook(ogNamespacelabelSelector, caPEM, desc)
72-
72+
case v1alpha1.ConversionWebhook:
73+
i.createOrUpdateConversionWebhook(caPEM, desc)
7374
}
7475
return nil
7576
}
@@ -102,14 +103,6 @@ func (i *StrategyDeploymentInstaller) createOrUpdateMutatingWebhook(ogNamespacel
102103
log.Errorf("Webhooks: Error creating MutatingWebhookConfiguration: %v", err)
103104
return err
104105
}
105-
106-
// If dealing with a Conversion Webhook, make sure that the operator only supports the AllNamespace installMode.
107-
if len(desc.ConversionCRDs) != 0 && isSingletonOperator(i) {
108-
if err := createOrUpdateConversionCRDs(desc, webhook.Webhooks[0].ClientConfig, i); err != nil {
109-
return err
110-
}
111-
}
112-
return nil
113106
}
114107
for _, webhook := range existingWebhooks.Items {
115108
// Update the list of webhooks
@@ -123,13 +116,6 @@ func (i *StrategyDeploymentInstaller) createOrUpdateMutatingWebhook(ogNamespacel
123116
log.Warnf("could not update MutatingWebhookConfiguration %s", webhook.GetName())
124117
return err
125118
}
126-
127-
// If dealing with a Conversion Webhook, make sure that the operator only supports the AllNamespace installMode.
128-
if len(desc.ConversionCRDs) != 0 && isSingletonOperator(i) {
129-
if err := createOrUpdateConversionCRDs(desc, webhook.Webhooks[0].ClientConfig, i); err != nil {
130-
return err
131-
}
132-
}
133119
}
134120

135121
return nil
@@ -164,13 +150,6 @@ func (i *StrategyDeploymentInstaller) createOrUpdateValidatingWebhook(ogNamespac
164150
return err
165151
}
166152

167-
// If dealing with a Conversion Webhook, make sure that the operator only supports the AllNamespace installMode.
168-
if len(desc.ConversionCRDs) != 0 && isSingletonOperator(i) {
169-
if err := createOrUpdateConversionCRDs(desc, webhook.Webhooks[0].ClientConfig, i); err != nil {
170-
return err
171-
}
172-
}
173-
174153
return nil
175154
}
176155
for _, webhook := range existingWebhooks.Items {
@@ -185,25 +164,13 @@ func (i *StrategyDeploymentInstaller) createOrUpdateValidatingWebhook(ogNamespac
185164
log.Warnf("could not update ValidatingWebhookConfiguration %s", webhook.GetName())
186165
return err
187166
}
188-
189-
// If dealing with a Conversion Webhook, make sure that the operator only supports the AllNamespace installMode.
190-
if len(desc.ConversionCRDs) != 0 && isSingletonOperator(i) {
191-
if err := createOrUpdateConversionCRDs(desc, webhook.Webhooks[0].ClientConfig, i); err != nil {
192-
return err
193-
}
194-
}
195167
}
196168

197169
return nil
198170
}
199171

200172
// check if csv supports only AllNamespaces install mode
201-
func isSingletonOperator(i *StrategyDeploymentInstaller) bool {
202-
// get csv
203-
csv, err := i.strategyClient.GetOpLister().OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(i.owner.GetNamespace()).Get(i.owner.GetName())
204-
if err != nil {
205-
log.Infof("CSV not found, error: %s", err.Error())
206-
}
173+
func isSingletonOperator(csv v1alpha1.ClusterServiceVersion) bool {
207174
// check if AllNamespaces is supported and other install modes are not supported
208175
for _, installMode := range csv.Spec.InstallModes {
209176
if installMode.Type == v1alpha1.InstallModeTypeAllNamespaces && !installMode.Supported {
@@ -216,34 +183,39 @@ func isSingletonOperator(i *StrategyDeploymentInstaller) bool {
216183
return true
217184
}
218185

219-
func createOrUpdateConversionCRDs(desc v1alpha1.WebhookDescription, clientConfig admissionregistrationv1.WebhookClientConfig, i *StrategyDeploymentInstaller) error {
186+
func (i *StrategyDeploymentInstaller) createOrUpdateConversionWebhook(caPEM []byte, desc v1alpha1.WebhookDescription) error {
220187
// get a list of owned CRDs
221-
csv, err := i.strategyClient.GetOpLister().OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(i.owner.GetNamespace()).Get(i.owner.GetName())
222-
if err != nil {
223-
log.Infof("CSV not found, error: %s", err.Error())
188+
csv, ok := i.owner.(*v1alpha1.ClusterServiceVersion)
189+
if !ok {
190+
return fmt.Errorf("ConversionWebhook owner must be a ClusterServiceVersion")
191+
}
192+
193+
if !isSingletonOperator(*csv) {
194+
return fmt.Errorf("CSVs with conversion webhooks must support only AllNamespaces")
195+
}
196+
197+
if len(desc.ConversionCRDs) == 0 {
198+
return fmt.Errorf("Conversion Webhook must have at least one CRD specified")
224199
}
225-
ownedCRDs := csv.Spec.CustomResourceDefinitions.Owned
226200

227201
// iterate over all the ConversionCRDs
228-
for _, ConversionCRD := range desc.ConversionCRDs {
229-
// check if CRD exists on cluster
230-
crd, err := i.strategyClient.GetOpClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), ConversionCRD, metav1.GetOptions{})
202+
for _, conversionCRD := range desc.ConversionCRDs {
203+
// Get existing CRD on cluster
204+
crd, err := i.strategyClient.GetOpClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), conversionCRD, metav1.GetOptions{})
231205
if err != nil {
232-
log.Infof("CRD not found %s, error: %s", ConversionCRD, err.Error())
206+
return fmt.Errorf("Unable to get CRD %s specified in Conversion Webhook: %v", conversionCRD, err)
233207
}
234-
log.Infof("Found conversionCRDs %s", ConversionCRD)
235208

236209
// check if this CRD is an owned CRD
237210
foundCRD := false
238-
for _, ownedCRD := range ownedCRDs {
239-
if ownedCRD.Name == crd.GetName() {
240-
log.Infof("CSV %q owns CRD %q", csv.GetName(), crd.GetName())
211+
for _, ownedCRD := range csv.Spec.CustomResourceDefinitions.Owned {
212+
if ownedCRD.Name == conversionCRD {
241213
foundCRD = true
214+
break
242215
}
243216
}
244217
if !foundCRD {
245-
log.Infof("CSV %q does not own CRD %q", csv.GetName(), crd.GetName())
246-
return nil
218+
return fmt.Errorf("CSV %s does not own CRD %s", csv.GetName(), conversionCRD)
247219
}
248220

249221
// crd.Spec.Conversion.Strategy specifies how custom resources are converted between versions.
@@ -256,54 +228,41 @@ func createOrUpdateConversionCRDs(desc v1alpha1.WebhookDescription, clientConfig
256228
// By default the strategy is none
257229
// Reference:
258230
// - https://v1-15.docs.kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definition-versioning/#specify-multiple-versions
259-
if crd.Spec.PreserveUnknownFields == true && crd.Spec.Conversion.Strategy == "Webhook" {
260-
log.Infof("crd.Spec.PreserveUnknownFields must be false to let API Server call webhook to do the conversion.")
261-
return nil
231+
if crd.Spec.PreserveUnknownFields != false {
232+
return fmt.Errorf("crd.Spec.PreserveUnknownFields must be false to let API Server call webhook to do the conversion")
262233
}
263234

264235
// Conversion WebhookClientConfig should not be set when Strategy is None
265236
// https://v1-15.docs.kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definition-versioning/#specify-multiple-versions
266237
// Conversion WebhookClientConfig needs to be set when Strategy is None
267238
// https://v1-15.docs.kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definition-versioning/#configure-customresourcedefinition-to-use-conversion-webhooks
268-
if crd.Spec.Conversion.Strategy == "Webhook" {
269-
log.Infof("Updating CRD")
270-
271-
// use user defined path for CRD conversion webhook, else set default value
272-
conversionWebhookPath := "/"
273-
if crd.Spec.Conversion.Webhook.ClientConfig.Service.Path != nil {
274-
conversionWebhookPath = *crd.Spec.Conversion.Webhook.ClientConfig.Service.Path
275-
}
276239

277-
// use user defined port, else set it to admission webhook's port
278-
conversionWebhookPort := *clientConfig.Service.Port
279-
if crd.Spec.Conversion.Webhook.ClientConfig.Service.Port != nil {
280-
conversionWebhookPort = *crd.Spec.Conversion.Webhook.ClientConfig.Service.Port
281-
}
240+
// use user defined path for CRD conversion webhook, else set default value
241+
conversionWebhookPath := "/"
242+
if desc.WebhookPath != nil {
243+
conversionWebhookPath = *desc.WebhookPath
244+
}
282245

283-
// use user defined ConversionReviewVersions
284-
conversionWebhookConversionReviewVersions := crd.Spec.Conversion.Webhook.ConversionReviewVersions
285-
286-
// Override Name, Namespace, and CABundle
287-
crd.Spec.Conversion = &apiextensionsv1.CustomResourceConversion{
288-
Strategy: "Webhook",
289-
Webhook: &apiextensionsv1.WebhookConversion{
290-
ClientConfig: &apiextensionsv1.WebhookClientConfig{
291-
Service: &apiextensionsv1.ServiceReference{
292-
Namespace: clientConfig.Service.Namespace,
293-
Name: clientConfig.Service.Name,
294-
Path: &conversionWebhookPath,
295-
Port: &conversionWebhookPort,
296-
},
297-
CABundle: clientConfig.CABundle,
246+
// Override Name, Namespace, and CABundle
247+
crd.Spec.Conversion = &apiextensionsv1.CustomResourceConversion{
248+
Strategy: "Webhook",
249+
Webhook: &apiextensionsv1.WebhookConversion{
250+
ClientConfig: &apiextensionsv1.WebhookClientConfig{
251+
Service: &apiextensionsv1.ServiceReference{
252+
Namespace: i.owner.GetNamespace(),
253+
Name: desc.DomainName() + "-service",
254+
Path: &conversionWebhookPath,
255+
Port: &desc.ContainerPort,
298256
},
299-
ConversionReviewVersions: conversionWebhookConversionReviewVersions,
257+
CABundle: caPEM,
300258
},
301-
}
259+
ConversionReviewVersions: desc.AdmissionReviewVersions,
260+
},
261+
}
302262

303-
// update CRD conversion Specs
304-
if _, err = i.strategyClient.GetOpClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{}); err != nil {
305-
log.Infof("CRD %s could not be updated, error: %s", ConversionCRD, err.Error())
306-
}
263+
// update CRD conversion Specs
264+
if _, err = i.strategyClient.GetOpClient().ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{}); err != nil {
265+
return fmt.Errorf("Error updating CRD with Conversion info: %v", err)
307266
}
308267
}
309268

pkg/controller/operators/olm/apiservices.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -553,9 +553,22 @@ func (a *Operator) areWebhooksAvailable(csv *v1alpha1.ClusterServiceVersion) (bo
553553
return false, err
554554
}
555555
webhookCount = len(webhookList.Items)
556+
case v1alpha1.ConversionWebhook:
557+
for _, conversionCRD := range desc.ConversionCRDs {
558+
// check if CRD exists on cluster
559+
crd, err := a.opClient.ApiextensionsInterface().ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), conversionCRD, metav1.GetOptions{})
560+
if err != nil {
561+
log.Infof("CRD not found %v, error: %s", desc, err.Error())
562+
return false, err
563+
}
564+
565+
if crd.Spec.Conversion.Strategy != "Webhook" || crd.Spec.Conversion.Webhook == nil || crd.Spec.Conversion.Webhook.ClientConfig == nil && crd.Spec.Conversion.Webhook.ClientConfig.CABundle == nil {
566+
return false, fmt.Errorf("ConversionWebhook not ready")
567+
}
568+
webhookCount++
569+
}
556570
}
557571
if webhookCount == 0 {
558-
a.logger.Info("Expected Webhook does not exist")
559572
return false, nil
560573
}
561574
}

0 commit comments

Comments
 (0)