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

     1  /*
     2  Copyright 2020 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  //go:deprecated This package has been deprecated
    18  package v2
    19  
    20  import (
    21  	"bufio"
    22  	"errors"
    23  	"fmt"
    24  	"os"
    25  
    26  	"github.com/spf13/pflag"
    27  
    28  	"sigs.k8s.io/kubebuilder/v3/pkg/config"
    29  	"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
    30  	"sigs.k8s.io/kubebuilder/v3/pkg/model/resource"
    31  	"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
    32  	"sigs.k8s.io/kubebuilder/v3/pkg/plugin/util"
    33  	goPlugin "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang"
    34  	"sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v2/scaffolds"
    35  )
    36  
    37  var _ plugin.CreateAPISubcommand = &createAPISubcommand{}
    38  
    39  type createAPISubcommand struct {
    40  	config config.Config
    41  
    42  	options *goPlugin.Options
    43  
    44  	resource *resource.Resource
    45  
    46  	// Check if we have to scaffold resource and/or controller
    47  	resourceFlag   *pflag.Flag
    48  	controllerFlag *pflag.Flag
    49  
    50  	// force indicates that the resource should be created even if it already exists
    51  	force bool
    52  
    53  	// runMake indicates whether to run make or not after scaffolding APIs
    54  	runMake bool
    55  }
    56  
    57  func (p *createAPISubcommand) UpdateMetadata(cliMeta plugin.CLIMetadata, subcmdMeta *plugin.SubcommandMetadata) {
    58  	subcmdMeta.Description = `Scaffold a Kubernetes API by writing a Resource definition and/or a Controller.
    59  
    60  If information about whether the resource and controller should be scaffolded
    61  was not explicitly provided, it will prompt the user if they should be.
    62  
    63  After the scaffold is written, the dependencies will be updated and
    64  make generate will be run.
    65  `
    66  	subcmdMeta.Examples = fmt.Sprintf(`  # Create a frigates API with Group: ship, Version: v1beta1 and Kind: Frigate
    67    %[1]s create api --group ship --version v1beta1 --kind Frigate
    68  
    69    # Edit the API Scheme
    70    nano api/v1beta1/frigate_types.go
    71  
    72    # Edit the Controller
    73    nano controllers/frigate/frigate_controller.go
    74  
    75    # Edit the Controller Test
    76    nano controllers/frigate/frigate_controller_test.go
    77  
    78    # Generate the manifests
    79    make manifests
    80  
    81    # Install CRDs into the Kubernetes cluster using kubectl apply
    82    make install
    83  
    84    # Regenerate code and run against the Kubernetes cluster configured by ~/.kube/config
    85    make run
    86  `, cliMeta.CommandName)
    87  }
    88  
    89  func (p *createAPISubcommand) BindFlags(fs *pflag.FlagSet) {
    90  	fs.BoolVar(&p.runMake, "make", true, "if true, run `make generate` after generating files")
    91  
    92  	fs.BoolVar(&p.force, "force", false,
    93  		"attempt to create resource even if it already exists")
    94  
    95  	p.options = &goPlugin.Options{CRDVersion: "v1beta1"}
    96  	// p.options.Plural can be set to specify an irregular plural form
    97  
    98  	fs.BoolVar(&p.options.DoAPI, "resource", true,
    99  		"if set, generate the resource without prompting the user")
   100  	p.resourceFlag = fs.Lookup("resource")
   101  	fs.BoolVar(&p.options.Namespaced, "namespaced", true, "resource is namespaced")
   102  
   103  	fs.BoolVar(&p.options.DoController, "controller", true,
   104  		"if set, generate the controller without prompting the user")
   105  	p.controllerFlag = fs.Lookup("controller")
   106  }
   107  
   108  func (p *createAPISubcommand) InjectConfig(c config.Config) error {
   109  	p.config = c
   110  
   111  	return nil
   112  }
   113  
   114  func (p *createAPISubcommand) InjectResource(res *resource.Resource) error {
   115  	p.resource = res
   116  
   117  	if p.resource.Group == "" {
   118  		return fmt.Errorf("group cannot be empty")
   119  	}
   120  
   121  	// Ask for API and Controller if not specified
   122  	reader := bufio.NewReader(os.Stdin)
   123  	if !p.resourceFlag.Changed {
   124  		fmt.Println("Create Resource [y/n]")
   125  		p.options.DoAPI = util.YesNo(reader)
   126  	}
   127  	if !p.controllerFlag.Changed {
   128  		fmt.Println("Create Controller [y/n]")
   129  		p.options.DoController = util.YesNo(reader)
   130  	}
   131  
   132  	p.options.UpdateResource(p.resource, p.config)
   133  
   134  	if err := p.resource.Validate(); err != nil {
   135  		return err
   136  	}
   137  
   138  	// In case we want to scaffold a resource API we need to do some checks
   139  	if p.options.DoAPI {
   140  		// Check that resource doesn't have the API scaffolded or flag force was set
   141  		if res, err := p.config.GetResource(p.resource.GVK); err == nil && res.HasAPI() && !p.force {
   142  			return errors.New("API resource already exists")
   143  		}
   144  
   145  		// Check that the provided group can be added to the project
   146  		if !p.config.IsMultiGroup() && p.config.ResourcesLength() != 0 && !p.config.HasGroup(p.resource.Group) {
   147  			return fmt.Errorf("multiple groups are not allowed by default, to enable multi-group visit %s",
   148  				"https://kubebuilder.io/migration/multi-group.html")
   149  		}
   150  	}
   151  
   152  	return nil
   153  }
   154  
   155  func (p *createAPISubcommand) Scaffold(fs machinery.Filesystem) error {
   156  	scaffolder := scaffolds.NewAPIScaffolder(p.config, *p.resource, p.force)
   157  	scaffolder.InjectFS(fs)
   158  	return scaffolder.Scaffold()
   159  }
   160  
   161  func (p *createAPISubcommand) PostScaffold() error {
   162  	err := util.RunCmd("Update dependencies", "go", "mod", "tidy")
   163  	if err != nil {
   164  		return err
   165  	}
   166  
   167  	if p.runMake && p.resource.HasAPI() {
   168  		err = util.RunCmd("Running make", "make", "generate")
   169  		if err != nil {
   170  			return err
   171  		}
   172  		fmt.Print("Next: implement your new API and generate the manifests (e.g. CRDs,CRs) with:\n$ make manifests \n")
   173  	}
   174  
   175  	return nil
   176  }