sigs.k8s.io/kubebuilder/v3@v3.14.0/pkg/plugins/golang/v4/webhook.go (about)

     1  /*
     2  Copyright 2022 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 v4
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"github.com/spf13/pflag"
    23  
    24  	"sigs.k8s.io/kubebuilder/v3/pkg/config"
    25  	"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
    26  	"sigs.k8s.io/kubebuilder/v3/pkg/model/resource"
    27  	"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
    28  	pluginutil "sigs.k8s.io/kubebuilder/v3/pkg/plugin/util"
    29  	goPlugin "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang"
    30  	"sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v4/scaffolds"
    31  )
    32  
    33  // defaultWebhookVersion is the default mutating/validating webhook config API version to scaffold.
    34  const defaultWebhookVersion = "v1"
    35  
    36  var _ plugin.CreateWebhookSubcommand = &createWebhookSubcommand{}
    37  
    38  type createWebhookSubcommand struct {
    39  	config config.Config
    40  	// For help text.
    41  	commandName string
    42  
    43  	options *goPlugin.Options
    44  
    45  	resource *resource.Resource
    46  
    47  	// force indicates that the resource should be created even if it already exists
    48  	force bool
    49  }
    50  
    51  func (p *createWebhookSubcommand) UpdateMetadata(cliMeta plugin.CLIMetadata, subcmdMeta *plugin.SubcommandMetadata) {
    52  	p.commandName = cliMeta.CommandName
    53  
    54  	subcmdMeta.Description = `Scaffold a webhook for an API resource. You can choose to scaffold defaulting,
    55  validating and/or conversion webhooks.
    56  `
    57  	subcmdMeta.Examples = fmt.Sprintf(`  # Create defaulting and validating webhooks for Group: ship, Version: v1beta1
    58    # and Kind: Frigate
    59    %[1]s create webhook --group ship --version v1beta1 --kind Frigate --defaulting --programmatic-validation
    60  
    61    # Create conversion webhook for Group: ship, Version: v1beta1
    62    # and Kind: Frigate
    63    %[1]s create webhook --group ship --version v1beta1 --kind Frigate --conversion
    64  `, cliMeta.CommandName)
    65  }
    66  
    67  func (p *createWebhookSubcommand) BindFlags(fs *pflag.FlagSet) {
    68  	p.options = &goPlugin.Options{}
    69  
    70  	fs.StringVar(&p.options.Plural, "plural", "", "resource irregular plural form")
    71  
    72  	fs.BoolVar(&p.options.DoDefaulting, "defaulting", false,
    73  		"if set, scaffold the defaulting webhook")
    74  	fs.BoolVar(&p.options.DoValidation, "programmatic-validation", false,
    75  		"if set, scaffold the validating webhook")
    76  	fs.BoolVar(&p.options.DoConversion, "conversion", false,
    77  		"if set, scaffold the conversion webhook")
    78  
    79  	fs.BoolVar(&p.force, "force", false,
    80  		"attempt to create resource even if it already exists")
    81  }
    82  
    83  func (p *createWebhookSubcommand) InjectConfig(c config.Config) error {
    84  	p.config = c
    85  	// go/v4 no longer supports v1beta1 option
    86  	p.options.WebhookVersion = defaultWebhookVersion
    87  	return nil
    88  }
    89  
    90  func (p *createWebhookSubcommand) InjectResource(res *resource.Resource) error {
    91  	p.resource = res
    92  
    93  	p.options.UpdateResource(p.resource, p.config)
    94  
    95  	if err := p.resource.Validate(); err != nil {
    96  		return err
    97  	}
    98  
    99  	if !p.resource.HasDefaultingWebhook() && !p.resource.HasValidationWebhook() && !p.resource.HasConversionWebhook() {
   100  		return fmt.Errorf("%s create webhook requires at least one of --defaulting,"+
   101  			" --programmatic-validation and --conversion to be true", p.commandName)
   102  	}
   103  
   104  	// check if resource exist to create webhook
   105  	if r, err := p.config.GetResource(p.resource.GVK); err != nil {
   106  		return fmt.Errorf("%s create webhook requires a previously created API ", p.commandName)
   107  	} else if r.Webhooks != nil && !r.Webhooks.IsEmpty() && !p.force {
   108  		return fmt.Errorf("webhook resource already exists")
   109  	}
   110  
   111  	return nil
   112  }
   113  
   114  func (p *createWebhookSubcommand) Scaffold(fs machinery.Filesystem) error {
   115  	scaffolder := scaffolds.NewWebhookScaffolder(p.config, *p.resource, p.force)
   116  	scaffolder.InjectFS(fs)
   117  	return scaffolder.Scaffold()
   118  }
   119  
   120  func (p *createWebhookSubcommand) PostScaffold() error {
   121  	err := pluginutil.RunCmd("Update dependencies", "go", "mod", "tidy")
   122  	if err != nil {
   123  		return err
   124  	}
   125  
   126  	err = pluginutil.RunCmd("Running make", "make", "generate")
   127  	if err != nil {
   128  		return err
   129  	}
   130  	fmt.Print("Next: implement your new Webhook and generate the manifests with:\n$ make manifests\n")
   131  
   132  	return nil
   133  }