sigs.k8s.io/kubebuilder/v3@v3.14.0/hack/docs/internal/cronjob-tutorial/webhook_implementation.go (about)

     1  /*
     2  Copyright 2023 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package cronjob
    18  
    19  const WebhookIntro = `import (
    20  	"github.com/robfig/cron"
    21  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    22  	"k8s.io/apimachinery/pkg/runtime"
    23  	"k8s.io/apimachinery/pkg/runtime/schema"
    24  	validationutils "k8s.io/apimachinery/pkg/util/validation"
    25  	"k8s.io/apimachinery/pkg/util/validation/field"
    26  	ctrl "sigs.k8s.io/controller-runtime"
    27  	logf "sigs.k8s.io/controller-runtime/pkg/log"
    28  	"sigs.k8s.io/controller-runtime/pkg/webhook"
    29  	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
    30  )
    31  
    32  // +kubebuilder:docs-gen:collapse=Go imports
    33  
    34  /*
    35  Next, we'll setup a logger for the webhooks.
    36  */
    37  
    38  `
    39  
    40  const WebhookMarker = `/*
    41  Notice that we use kubebuilder markers to generate webhook manifests.
    42  This marker is responsible for generating a mutating webhook manifest.
    43  
    44  The meaning of each marker can be found [here](/reference/markers/webhook.md).
    45  */
    46  
    47  //+kubebuilder:webhook:path=/mutate-batch-tutorial-kubebuilder-io-v1-cronjob,mutating=true,failurePolicy=fail,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=create;update,versions=v1,name=mcronjob.kb.io,sideEffects=None,admissionReviewVersions=v1
    48  
    49  /*
    50  We use the` + " `" + `webhook.Defaulter` + "`" + ` interface to set defaults to our CRD.
    51  A webhook will automatically be served that calls this defaulting.
    52  
    53  The` + " `" + `Default` + "`" + ` method is expected to mutate the receiver, setting the defaults.
    54  */
    55  `
    56  
    57  const WebhookValidate = `	cronjoblog.Info("default", "name", r.Name)
    58  
    59  	if r.Spec.ConcurrencyPolicy == "" {
    60  		r.Spec.ConcurrencyPolicy = AllowConcurrent
    61  	}
    62  	if r.Spec.Suspend == nil {
    63  		r.Spec.Suspend = new(bool)
    64  	}
    65  	if r.Spec.SuccessfulJobsHistoryLimit == nil {
    66  		r.Spec.SuccessfulJobsHistoryLimit = new(int32)
    67  		*r.Spec.SuccessfulJobsHistoryLimit = 3
    68  	}
    69  	if r.Spec.FailedJobsHistoryLimit == nil {
    70  		r.Spec.FailedJobsHistoryLimit = new(int32)
    71  		*r.Spec.FailedJobsHistoryLimit = 1
    72  	}
    73  }
    74  
    75  /*
    76  This marker is responsible for generating a validating webhook manifest.
    77  */
    78  
    79  //+kubebuilder:webhook:verbs=create;update;delete,path=/validate-batch-tutorial-kubebuilder-io-v1-cronjob,mutating=false,failurePolicy=fail,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,versions=v1,name=vcronjob.kb.io,sideEffects=None,admissionReviewVersions=v1
    80  
    81  /*
    82  We can validate our CRD beyond what's possible with declarative
    83  validation. Generally, declarative validation should be sufficient, but
    84  sometimes more advanced use cases call for complex validation.
    85  
    86  For instance, we'll see below that we use this to validate a well-formed cron
    87  schedule without making up a long regular expression.
    88  
    89  If` + " `" + `webhook.Validator` + "`" + ` interface is implemented, a webhook will automatically be
    90  served that calls the validation.
    91  
    92  The` + " `" + `ValidateCreate` + "`" + `, ` + "`" + `ValidateUpdate` + "`" + ` and` + " `" + `ValidateDelete` + "`" + ` methods are expected
    93  to validate its receiver upon creation, update and deletion respectively.
    94  We separate out ValidateCreate from ValidateUpdate to allow behavior like making
    95  certain fields immutable, so that they can only be set on creation.
    96  ValidateDelete is also separated from ValidateUpdate to allow different
    97  validation behavior on deletion.
    98  Here, however, we just use the same shared validation for` + " `" + `ValidateCreate` + "`" + ` and
    99  ` + "`" + `ValidateUpdate` + "`" + `. And we do nothing in` + " `" + `ValidateDelete` + "`" + `, since we don't need to
   100  validate anything on deletion.
   101  */
   102  `
   103  
   104  const WebhookValidateSpec = `
   105  /*
   106  We validate the name and the spec of the CronJob.
   107  */
   108  
   109  func (r *CronJob) validateCronJob() error {
   110  	var allErrs field.ErrorList
   111  	if err := r.validateCronJobName(); err != nil {
   112  		allErrs = append(allErrs, err)
   113  	}
   114  	if err := r.validateCronJobSpec(); err != nil {
   115  		allErrs = append(allErrs, err)
   116  	}
   117  	if len(allErrs) == 0 {
   118  		return nil
   119  	}
   120  
   121  	return apierrors.NewInvalid(
   122  		schema.GroupKind{Group: "batch.tutorial.kubebuilder.io", Kind: "CronJob"},
   123  		r.Name, allErrs)
   124  }
   125  
   126  /*
   127  Some fields are declaratively validated by OpenAPI schema.
   128  You can find kubebuilder validation markers (prefixed
   129  with` + " `" + `// +kubebuilder:validation` + "`" + `) in the
   130  [Designing an API](api-design.md) section.
   131  You can find all of the kubebuilder supported markers for
   132  declaring validation by running` + " `" + `controller-gen crd -w` + "`" + `,
   133  or [here](/reference/markers/crd-validation.md).
   134  */
   135  
   136  func (r *CronJob) validateCronJobSpec() *field.Error {
   137  	// The field helpers from the kubernetes API machinery help us return nicely
   138  	// structured validation errors.
   139  	return validateScheduleFormat(
   140  		r.Spec.Schedule,
   141  		field.NewPath("spec").Child("schedule"))
   142  }
   143  
   144  /*
   145  We'll need to validate the [cron](https://en.wikipedia.org/wiki/Cron) schedule
   146  is well-formatted.
   147  */
   148  
   149  func validateScheduleFormat(schedule string, fldPath *field.Path) *field.Error {
   150  	if _, err := cron.ParseStandard(schedule); err != nil {
   151  		return field.Invalid(fldPath, schedule, err.Error())
   152  	}
   153  	return nil
   154  }
   155  
   156  /*
   157  Validating the length of a string field can be done declaratively by
   158  the validation schema.
   159  
   160  But the` + " `" + `ObjectMeta.Name` + "`" + ` field is defined in a shared package under
   161  the apimachinery repo, so we can't declaratively validate it using
   162  the validation schema.
   163  */
   164  
   165  func (r *CronJob) validateCronJobName() *field.Error {
   166  	if len(r.ObjectMeta.Name) > validationutils.DNS1035LabelMaxLength-11 {
   167  		// The job name length is 63 character like all Kubernetes objects
   168  		// (which must fit in a DNS subdomain). The cronjob controller appends
   169  		// a 11-character suffix to the cronjob (` + "`" + `-$TIMESTAMP` + "`" + `) when creating
   170  		// a job. The job name length limit is 63 characters. Therefore cronjob
   171  		// names must have length <= 63-11=52. If we don't validate this here,
   172  		// then job creation will fail later.
   173  		return field.Invalid(field.NewPath("metadata").Child("name"), r.Name, "must be no more than 52 characters")
   174  	}
   175  	return nil
   176  }
   177  
   178  // +kubebuilder:docs-gen:collapse=Validate object name`