diff --git a/api/v1beta1/types.go b/api/v1beta1/types.go index 5cb066219e..2eff03988f 100644 --- a/api/v1beta1/types.go +++ b/api/v1beta1/types.go @@ -17,6 +17,7 @@ limitations under the License. package v1beta1 import ( + "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/utils/ptr" "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/optional" @@ -75,6 +76,24 @@ func (f *ImageFilter) IsZero() bool { return f.Name == nil && len(f.Tags) == 0 } +// Validate performs validation on [ImageParam], returning a list of field errors using the provided base path. +// It is intended to be used in the validation webhooks of resources containing [ImageParam]. +func (i *ImageParam) Validate(base field.Path) field.ErrorList { + var errors field.ErrorList + // Not possible to validate the image if it is missing + if i == nil { + errors = append(errors, field.Required(&base, "Image is required")) + return errors + } + if i.Filter.Name == nil || i.Filter.Tags == nil { + errors = append(errors, field.Required(base.Child("Image filter"), "Either Name or Tags of image are missing")) + } + if i.ImageRef.Name == "" { + errors = append(errors, field.Required(base.Child("ORC image Referecne"), "ORC image is missing")) + } + return errors +} + type ExternalRouterIPParam struct { // The FixedIP in the corresponding subnet FixedIP string `json:"fixedIP,omitempty"` diff --git a/pkg/webhooks/openstackmachine_webhook.go b/pkg/webhooks/openstackmachine_webhook.go index 601fef6473..6f06fc09dc 100644 --- a/pkg/webhooks/openstackmachine_webhook.go +++ b/pkg/webhooks/openstackmachine_webhook.go @@ -48,13 +48,18 @@ type openStackMachineWebhook struct{} var _ webhook.CustomValidator = &openStackMachineWebhook{} // ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type. -func (*openStackMachineWebhook) ValidateCreate(_ context.Context, objRaw runtime.Object) (admission.Warnings, error) { +func (webhook *openStackMachineWebhook) ValidateCreate(_ context.Context, objRaw runtime.Object) (admission.Warnings, error) { var allErrs field.ErrorList newObj, err := castToOpenStackMachine(objRaw) if err != nil { return nil, err } + err = webhook.validate(newObj) + if err != nil { + return nil, err + } + if newObj.Spec.RootVolume != nil && newObj.Spec.AdditionalBlockDevices != nil { for _, device := range newObj.Spec.AdditionalBlockDevices { if device.Name == "root" { @@ -73,7 +78,7 @@ func (*openStackMachineWebhook) ValidateCreate(_ context.Context, objRaw runtime } // ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type. -func (*openStackMachineWebhook) ValidateUpdate(_ context.Context, oldObjRaw, newObjRaw runtime.Object) (admission.Warnings, error) { +func (webhook *openStackMachineWebhook) ValidateUpdate(_ context.Context, oldObjRaw, newObjRaw runtime.Object) (admission.Warnings, error) { newObj, err := castToOpenStackMachine(newObjRaw) if err != nil { return nil, err @@ -121,7 +126,7 @@ func (*openStackMachineWebhook) ValidateUpdate(_ context.Context, oldObjRaw, new } // ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type. -func (*openStackMachineWebhook) ValidateDelete(_ context.Context, _ runtime.Object) (admission.Warnings, error) { +func (webhook *openStackMachineWebhook) ValidateDelete(_ context.Context, _ runtime.Object) (admission.Warnings, error) { return nil, nil } @@ -132,3 +137,14 @@ func castToOpenStackMachine(obj runtime.Object) (*infrav1.OpenStackMachine, erro } return cast, nil } + +func (webhook *openStackMachineWebhook) validate(newObj *infrav1.OpenStackMachine) error { + var allErrs field.ErrorList + + allErrs = append(allErrs, newObj.Spec.Image.Validate(*field.NewPath("Spec", "Image"))...) + + if len(allErrs) == 0 { + return nil + } + return apierrors.NewInvalid(infrav1.SchemeGroupVersion.WithKind("OpenStackMachine").GroupKind(), newObj.Name, allErrs) +} diff --git a/pkg/webhooks/openstackmachinetemplate_webhook.go b/pkg/webhooks/openstackmachinetemplate_webhook.go index 0e3b972b54..8e568b4f40 100644 --- a/pkg/webhooks/openstackmachinetemplate_webhook.go +++ b/pkg/webhooks/openstackmachinetemplate_webhook.go @@ -48,7 +48,7 @@ type openStackMachineTemplateWebhook struct{} var _ webhook.CustomValidator = &openStackMachineTemplateWebhook{} // ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type. -func (*openStackMachineTemplateWebhook) ValidateCreate(_ context.Context, objRaw runtime.Object) (admission.Warnings, error) { +func (webhook *openStackMachineTemplateWebhook) ValidateCreate(_ context.Context, objRaw runtime.Object) (admission.Warnings, error) { newObj, err := castToOpenStackMachineTemplate(objRaw) if err != nil { return nil, err @@ -56,6 +56,11 @@ func (*openStackMachineTemplateWebhook) ValidateCreate(_ context.Context, objRaw var allErrs field.ErrorList + err = webhook.validate(newObj) + if err != nil { + return nil, err + } + if newObj.Spec.Template.Spec.ProviderID != nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "template", "spec", "providerID"), "cannot be set in templates")) } @@ -64,7 +69,7 @@ func (*openStackMachineTemplateWebhook) ValidateCreate(_ context.Context, objRaw } // ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type. -func (*openStackMachineTemplateWebhook) ValidateUpdate(ctx context.Context, oldObjRaw, newObjRaw runtime.Object) (admission.Warnings, error) { +func (webhook *openStackMachineTemplateWebhook) ValidateUpdate(ctx context.Context, oldObjRaw, newObjRaw runtime.Object) (admission.Warnings, error) { var allErrs field.ErrorList oldObj, err := castToOpenStackMachineTemplate(oldObjRaw) if err != nil { @@ -92,7 +97,7 @@ func (*openStackMachineTemplateWebhook) ValidateUpdate(ctx context.Context, oldO } // ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type. -func (*openStackMachineTemplateWebhook) ValidateDelete(_ context.Context, _ runtime.Object) (admission.Warnings, error) { +func (webhook *openStackMachineTemplateWebhook) ValidateDelete(_ context.Context, _ runtime.Object) (admission.Warnings, error) { return nil, nil } @@ -103,3 +108,14 @@ func castToOpenStackMachineTemplate(obj runtime.Object) (*infrav1.OpenStackMachi } return cast, nil } + +func (webhook *openStackMachineTemplateWebhook) validate(newObj *infrav1.OpenStackMachineTemplate) error { + var allErrs field.ErrorList + + allErrs = append(allErrs, newObj.Spec.Template.Spec.Image.Validate(*field.NewPath("Spec", "Template", "Spec", "Image"))...) + + if len(allErrs) == 0 { + return nil + } + return apierrors.NewInvalid(infrav1.SchemeGroupVersion.WithKind("OpenStackMachineTemplate").GroupKind(), newObj.Name, allErrs) +} diff --git a/pkg/webhooks/openstackserver_webhook.go b/pkg/webhooks/openstackserver_webhook.go index 24bb17ea76..68a9cecb02 100644 --- a/pkg/webhooks/openstackserver_webhook.go +++ b/pkg/webhooks/openstackserver_webhook.go @@ -50,12 +50,16 @@ type openStackServerWebhook struct{} var _ webhook.CustomValidator = &openStackServerWebhook{} // ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type. -func (*openStackServerWebhook) ValidateCreate(_ context.Context, objRaw runtime.Object) (admission.Warnings, error) { +func (webhook *openStackServerWebhook) ValidateCreate(_ context.Context, objRaw runtime.Object) (admission.Warnings, error) { var allErrs field.ErrorList newObj, err := castToOpenStackServer(objRaw) if err != nil { return nil, err } + err = webhook.validate(newObj) + if err != nil { + return nil, err + } if newObj.Spec.RootVolume != nil && newObj.Spec.AdditionalBlockDevices != nil { for _, device := range newObj.Spec.AdditionalBlockDevices { @@ -75,7 +79,7 @@ func (*openStackServerWebhook) ValidateCreate(_ context.Context, objRaw runtime. } // ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type. -func (*openStackServerWebhook) ValidateUpdate(ctx context.Context, oldObjRaw, newObjRaw runtime.Object) (admission.Warnings, error) { +func (webhook *openStackServerWebhook) ValidateUpdate(ctx context.Context, oldObjRaw, newObjRaw runtime.Object) (admission.Warnings, error) { oldObj, err := castToOpenStackServer(oldObjRaw) if err != nil { return nil, err @@ -124,7 +128,7 @@ func (*openStackServerWebhook) ValidateUpdate(ctx context.Context, oldObjRaw, ne } // ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type. -func (*openStackServerWebhook) ValidateDelete(_ context.Context, _ runtime.Object) (admission.Warnings, error) { +func (webhook *openStackServerWebhook) ValidateDelete(_ context.Context, _ runtime.Object) (admission.Warnings, error) { return nil, nil } @@ -135,3 +139,14 @@ func castToOpenStackServer(obj runtime.Object) (*infrav1alpha1.OpenStackServer, } return cast, nil } + +func (webhook *openStackServerWebhook) validate(newObj *infrav1alpha1.OpenStackServer) error { + var allErrs field.ErrorList + + allErrs = append(allErrs, newObj.Spec.Image.Validate(*field.NewPath("Spec", "Image"))...) + + if len(allErrs) == 0 { + return nil + } + return apierrors.NewInvalid(infrav1.SchemeGroupVersion.WithKind("OpenStackServer").GroupKind(), newObj.Name, allErrs) +}