Skip to content

Commit

Permalink
Validate dbPurge.schedule
Browse files Browse the repository at this point in the history
Implements: OSPRH-104
  • Loading branch information
gibizer authored and openshift-merge-bot[bot] committed Feb 29, 2024
1 parent 6a99bf2 commit 3ee92ad
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 2 deletions.
1 change: 1 addition & 0 deletions api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.20

require (
github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.20240227073850-ffd41379d759
github.com/robfig/cron/v3 v3.0.1
k8s.io/api v0.28.7
k8s.io/apimachinery v0.28.7
k8s.io/utils v0.0.0-20240102154912-e7106e64919e
Expand Down
2 changes: 2 additions & 0 deletions api/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdO
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
Expand Down
21 changes: 21 additions & 0 deletions api/v1beta1/nova_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ package v1beta1
import (
"fmt"

"github.com/robfig/cron/v3"

apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand Down Expand Up @@ -191,6 +193,10 @@ func (r *NovaSpec) ValidateCellTemplates(basePath *field.Path) field.ErrorList {
cell.MetadataServiceTemplate.ValidateDefaultConfigOverwrite(
cellPath.Child("metadataServiceTemplate"))...)

errors = append(
errors,
cell.DBPurge.Validate(cellPath.Child("dbPurge"))...)

if name == Cell0Name {
errors = append(
errors,
Expand Down Expand Up @@ -301,3 +307,18 @@ func (r *Nova) ValidateDelete() (admission.Warnings, error) {
// TODO(user): fill in your validation logic upon object deletion.
return nil, nil
}

// Validate the field values
func (r *NovaCellDBPurge) Validate(basePath *field.Path) field.ErrorList {
var errors field.ErrorList
// k8s uses the same cron lib to validate the schedule of the CronJob
// https://github.com/kubernetes/kubernetes/blob/master/pkg/apis/batch/validation/validation.go
if _, err := cron.ParseStandard(*r.Schedule); err != nil {
errors = append(
errors,
field.Invalid(
basePath.Child("schedule"), r.Schedule, err.Error()),
)
}
return errors
}
5 changes: 5 additions & 0 deletions api/v1beta1/novacell_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,11 @@ func (r *NovaCellSpec) validate(basePath *field.Path) field.ErrorList {
errors, ValidateCellName(
basePath.Child("cellName"), r.CellName)...,
)
errors = append(
errors,
r.DBPurge.Validate(basePath.Child("dbPurge"))...,
)

return errors
}

Expand Down
23 changes: 21 additions & 2 deletions api/v1beta1/novaconductor_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ limitations under the License.
package v1beta1

import (
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
Expand Down Expand Up @@ -80,15 +83,31 @@ var _ webhook.Validator = &NovaConductor{}
func (r *NovaConductor) ValidateCreate() (admission.Warnings, error) {
novaconductorlog.Info("validate create", "name", r.Name)

// TODO(user): fill in your validation logic upon object creation.
errors := r.Spec.DBPurge.Validate(
field.NewPath("spec").Child("dbPurge"))

if len(errors) != 0 {
novaconductorlog.Info("validation failed", "name", r.Name)
return nil, apierrors.NewInvalid(
schema.GroupKind{Group: "nova.openstack.org", Kind: "NovaConductor"},
r.Name, errors)
}
return nil, nil
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *NovaConductor) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
novaconductorlog.Info("validate update", "name", r.Name)

// TODO(user): fill in your validation logic upon object update.
errors := r.Spec.DBPurge.Validate(
field.NewPath("spec").Child("dbPurge"))

if len(errors) != 0 {
novaconductorlog.Info("validation failed", "name", r.Name)
return nil, apierrors.NewInvalid(
schema.GroupKind{Group: "nova.openstack.org", Kind: "NovaConductor"},
r.Name, errors)
}
return nil, nil
}

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ require (
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/mod v0.15.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdO
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
Expand Down
98 changes: 98 additions & 0 deletions test/functional/validation_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -927,4 +927,102 @@ var _ = Describe("Nova validation", func() {
),
)
})
It("rejects NovaConductor with wrong dbPurge.Schedule", func() {
spec := GetDefaultNovaConductorSpec(cell1)
spec["dbPurge"] = map[string]interface{}{
"schedule": "* * * *",
}
raw := map[string]interface{}{
"apiVersion": "nova.openstack.org/v1beta1",
"kind": "NovaConductor",
"metadata": map[string]interface{}{
"name": cell1.ConductorName.Name,
"namespace": novaNames.Namespace,
},
"spec": spec,
}

unstructuredObj := &unstructured.Unstructured{Object: raw}
_, err := controllerutil.CreateOrPatch(
ctx, k8sClient, unstructuredObj, func() error { return nil })

Expect(err).Should(HaveOccurred())
var statusError *k8s_errors.StatusError
Expect(errors.As(err, &statusError)).To(BeTrue())
Expect(statusError.ErrStatus.Details.Kind).To(Equal("NovaConductor"))
Expect(statusError.ErrStatus.Message).To(
ContainSubstring(
"invalid: spec.dbPurge.schedule: " +
"Invalid value: \"* * * *\": " +
"expected exactly 5 fields, found 4: [* * * *]",
),
)
})
It("rejects NovaCell with wrong dbPurge.Schedule", func() {
spec := GetDefaultNovaCellSpec(cell1)

spec["dbPurge"] = map[string]interface{}{
"schedule": "* * * * * 1",
}

raw := map[string]interface{}{
"apiVersion": "nova.openstack.org/v1beta1",
"kind": "NovaCell",
"metadata": map[string]interface{}{
"name": cell1.CellCRName.Name,
"namespace": novaNames.Namespace,
},
"spec": spec,
}

unstructuredObj := &unstructured.Unstructured{Object: raw}
_, err := controllerutil.CreateOrPatch(
ctx, k8sClient, unstructuredObj, func() error { return nil })

Expect(err).Should(HaveOccurred())
var statusError *k8s_errors.StatusError
Expect(errors.As(err, &statusError)).To(BeTrue())
Expect(statusError.ErrStatus.Details.Kind).To(Equal("NovaCell"))
Expect(statusError.ErrStatus.Message).To(
ContainSubstring(
"invalid: spec.dbPurge.schedule: " +
"Invalid value: \"* * * * * 1\": " +
"expected exactly 5 fields, found 6: [* * * * * 1]",
),
)
})
It("rejects Nova with wrong dbPurge.Schedule in cellTemplate", func() {
spec := GetDefaultNovaSpec()
cell0 := GetDefaultNovaCellTemplate()
cell1 := GetDefaultNovaCellTemplate()
cell1["dbPurge"] = map[string]interface{}{
"schedule": "@dailyX",
}
spec["cellTemplates"] = map[string]interface{}{"cell0": cell0, "cell1": cell1}
raw := map[string]interface{}{
"apiVersion": "nova.openstack.org/v1beta1",
"kind": "Nova",
"metadata": map[string]interface{}{
"name": novaNames.NovaName.Name,
"namespace": novaNames.Namespace,
},
"spec": spec,
}
unstructuredObj := &unstructured.Unstructured{Object: raw}
_, err := controllerutil.CreateOrPatch(
ctx, k8sClient, unstructuredObj, func() error { return nil })

Expect(err).Should(HaveOccurred())
var statusError *k8s_errors.StatusError
Expect(errors.As(err, &statusError)).To(BeTrue())
Expect(statusError.ErrStatus.Details.Kind).To(Equal("Nova"))
Expect(statusError.ErrStatus.Message).To(
ContainSubstring(
"invalid: spec.cellTemplates[cell1]." +
"dbPurge.schedule: " +
"Invalid value: \"@dailyX\": " +
"unrecognized descriptor: @dailyX",
),
)
})
})

0 comments on commit 3ee92ad

Please sign in to comment.