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