sigs.k8s.io/kubebuilder/v3@v3.14.0/pkg/plugins/golang/v3/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 in favor of v4 18 package v3 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/v3/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 = "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 nano api/v1beta1/frigate_types.go 79 80 # Edit the Controller 81 nano controllers/frigate/frigate_controller.go 82 83 # Edit the Controller Test 84 nano controllers/frigate/frigate_controller_test.go 85 86 # Generate the manifests 87 make manifests 88 89 # Install CRDs into the Kubernetes cluster using kubectl apply 90 make install 91 92 # Regenerate code and run against the Kubernetes cluster configured by ~/.kube/config 93 make run 94 `, cliMeta.CommandName) 95 } 96 97 func (p *createAPISubcommand) BindFlags(fs *pflag.FlagSet) { 98 fs.BoolVar(&p.runMake, "make", true, "if true, run `make generate` after generating files") 99 100 fs.BoolVar(&p.force, "force", false, 101 "attempt to create resource even if it already exists") 102 103 p.options = &goPlugin.Options{} 104 105 fs.StringVar(&p.options.Plural, "plural", "", "resource irregular plural form") 106 107 fs.BoolVar(&p.options.DoAPI, "resource", true, 108 "if set, generate the resource without prompting the user") 109 p.resourceFlag = fs.Lookup("resource") 110 fs.StringVar(&p.options.CRDVersion, "crd-version", defaultCRDVersion, 111 "version of CustomResourceDefinition to scaffold. Options: [v1, v1beta1]") 112 fs.BoolVar(&p.options.Namespaced, "namespaced", true, "resource is namespaced") 113 114 fs.BoolVar(&p.options.DoController, "controller", true, 115 "if set, generate the controller without prompting the user") 116 p.controllerFlag = fs.Lookup("controller") 117 118 // (not required raise an error in this case) 119 // nolint:errcheck,gosec 120 fs.MarkDeprecated("crd-version", deprecateMsg) 121 } 122 123 func (p *createAPISubcommand) InjectConfig(c config.Config) error { 124 p.config = c 125 126 return nil 127 } 128 129 func (p *createAPISubcommand) InjectResource(res *resource.Resource) error { 130 p.resource = res 131 132 // TODO: re-evaluate whether y/n input still makes sense. We should probably always 133 // scaffold the resource and controller. 134 // Ask for API and Controller if not specified 135 reader := bufio.NewReader(os.Stdin) 136 if !p.resourceFlag.Changed { 137 fmt.Println("Create Resource [y/n]") 138 p.options.DoAPI = util.YesNo(reader) 139 } 140 if !p.controllerFlag.Changed { 141 fmt.Println("Create Controller [y/n]") 142 p.options.DoController = util.YesNo(reader) 143 } 144 145 p.options.UpdateResource(p.resource, p.config) 146 147 if err := p.resource.Validate(); err != nil { 148 return err 149 } 150 151 // In case we want to scaffold a resource API we need to do some checks 152 if p.options.DoAPI { 153 // Check that resource doesn't have the API scaffolded or flag force was set 154 if r, err := p.config.GetResource(p.resource.GVK); err == nil && r.HasAPI() && !p.force { 155 return errors.New("API resource already exists") 156 } 157 158 // Check that the provided group can be added to the project 159 if !p.config.IsMultiGroup() && p.config.ResourcesLength() != 0 && !p.config.HasGroup(p.resource.Group) { 160 return fmt.Errorf("multiple groups are not allowed by default, " + 161 "to enable multi-group visit https://kubebuilder.io/migration/multi-group.html") 162 } 163 164 // Check CRDVersion against all other CRDVersions in p.config for compatibility. 165 // nolint:staticcheck 166 if util.HasDifferentCRDVersion(p.config, p.resource.API.CRDVersion) { 167 return fmt.Errorf("only one CRD version can be used for all resources, cannot add %q", 168 p.resource.API.CRDVersion) 169 } 170 } 171 172 return nil 173 } 174 175 func (p *createAPISubcommand) PreScaffold(machinery.Filesystem) error { 176 // check if main.go is present in the root directory 177 if _, err := os.Stat(DefaultMainPath); os.IsNotExist(err) { 178 return fmt.Errorf("%s file should present in the root directory", DefaultMainPath) 179 } 180 181 return nil 182 } 183 184 func (p *createAPISubcommand) Scaffold(fs machinery.Filesystem) error { 185 scaffolder := scaffolds.NewAPIScaffolder(p.config, *p.resource, p.force) 186 scaffolder.InjectFS(fs) 187 return scaffolder.Scaffold() 188 } 189 190 func (p *createAPISubcommand) PostScaffold() error { 191 192 // Update the makefile to allow generate Webhooks to ensure backwards compatibility 193 // todo: it should be removed for go/v4 194 // nolint:lll,gosec 195 if p.resource.API.CRDVersion == "v1beta1" { 196 if err := applyScaffoldCustomizationsForVbeta1(); err != nil { 197 return err 198 } 199 } 200 201 err := util.RunCmd("Update dependencies", "go", "mod", "tidy") 202 if err != nil { 203 return err 204 } 205 if p.runMake && p.resource.HasAPI() { 206 err = util.RunCmd("Running make", "make", "generate") 207 if err != nil { 208 return err 209 } 210 fmt.Print("Next: implement your new API and generate the manifests (e.g. CRDs,CRs) with:\n$ make manifests\n") 211 } 212 213 return nil 214 }