github.com/mkimuram/operator-sdk@v0.7.1-0.20190410172100-52ad33a4bda0/cmd/operator-sdk/add/crd.go (about) 1 // Copyright 2018 The Operator-SDK Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package add 16 17 import ( 18 "fmt" 19 "os" 20 "path/filepath" 21 "strings" 22 23 "github.com/operator-framework/operator-sdk/internal/pkg/scaffold" 24 "github.com/operator-framework/operator-sdk/internal/pkg/scaffold/input" 25 "github.com/operator-framework/operator-sdk/internal/util/projutil" 26 27 log "github.com/sirupsen/logrus" 28 "github.com/spf13/cobra" 29 ) 30 31 // newAddCRDCmd - add crd command 32 func newAddCRDCmd() *cobra.Command { 33 crdCmd := &cobra.Command{ 34 Use: "crd", 35 Short: "Adds a Custom Resource Definition (CRD) and the Custom Resource (CR) files", 36 Long: `The operator-sdk add crd command will create a Custom Resource Definition (CRD) and the Custom Resource (CR) files for the specified api-version and kind. 37 38 Generated CRD filename: <project-name>/deploy/crds/<group>_<version>_<kind>_crd.yaml 39 Generated CR filename: <project-name>/deploy/crds/<group>_<version>_<kind>_cr.yaml 40 41 <project-name>/deploy path must already exist 42 --api-version and --kind are required flags to generate the new operator application. 43 `, 44 RunE: crdFunc, 45 } 46 crdCmd.Flags().StringVar(&apiVersion, "api-version", "", "Kubernetes apiVersion and has a format of $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1)") 47 if err := crdCmd.MarkFlagRequired("api-version"); err != nil { 48 log.Fatalf("Failed to mark `api-version` flag for `add crd` subcommand as required") 49 } 50 crdCmd.Flags().StringVar(&kind, "kind", "", "Kubernetes CustomResourceDefintion kind. (e.g AppService)") 51 if err := crdCmd.MarkFlagRequired("kind"); err != nil { 52 log.Fatalf("Failed to mark `kind` flag for `add crd` subcommand as required") 53 } 54 return crdCmd 55 } 56 57 func crdFunc(cmd *cobra.Command, args []string) error { 58 cfg := &input.Config{ 59 AbsProjectPath: projutil.MustGetwd(), 60 } 61 if len(args) != 0 { 62 return fmt.Errorf("command %s doesn't accept any arguments", cmd.CommandPath()) 63 } 64 if err := verifyCRDFlags(); err != nil { 65 return err 66 } 67 if err := verifyCRDDeployPath(); err != nil { 68 return err 69 } 70 71 log.Infof("Generating Custom Resource Definition (CRD) version %s for kind %s.", apiVersion, kind) 72 73 // generate CR/CRD file 74 resource, err := scaffold.NewResource(apiVersion, kind) 75 if err != nil { 76 return err 77 } 78 79 s := scaffold.Scaffold{} 80 err = s.Execute(cfg, 81 &scaffold.CRD{ 82 Input: input.Input{IfExistsAction: input.Skip}, 83 Resource: resource, 84 IsOperatorGo: projutil.IsOperatorGo(), 85 }, 86 &scaffold.CR{ 87 Input: input.Input{IfExistsAction: input.Skip}, 88 Resource: resource, 89 }, 90 ) 91 if err != nil { 92 return fmt.Errorf("crd scaffold failed: (%v)", err) 93 } 94 95 // update deploy/role.yaml for the given resource r. 96 if err := scaffold.UpdateRoleForResource(resource, cfg.AbsProjectPath); err != nil { 97 return fmt.Errorf("failed to update the RBAC manifest for the resource (%v, %v): (%v)", resource.APIVersion, resource.Kind, err) 98 } 99 100 log.Info("CRD generation complete.") 101 return nil 102 } 103 104 func verifyCRDFlags() error { 105 if len(apiVersion) == 0 { 106 return fmt.Errorf("value of --api-version must not have empty value") 107 } 108 if len(kind) == 0 { 109 return fmt.Errorf("value of --kind must not have empty value") 110 } 111 kindFirstLetter := string(kind[0]) 112 if kindFirstLetter != strings.ToUpper(kindFirstLetter) { 113 return fmt.Errorf("value of --kind must start with an uppercase letter") 114 } 115 if strings.Count(apiVersion, "/") != 1 { 116 return fmt.Errorf("value of --api-version has wrong format (%v); format must be $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1)", apiVersion) 117 } 118 return nil 119 } 120 121 // verifyCRDDeployPath checks if the path <project-name>/deploy sub-directory is exists, and that is rooted under $GOPATH 122 func verifyCRDDeployPath() error { 123 wd, err := os.Getwd() 124 if err != nil { 125 return fmt.Errorf("failed to determine the full path of the current directory: (%v)", err) 126 } 127 // check if the deploy sub-directory exist 128 _, err = os.Stat(filepath.Join(wd, scaffold.DeployDir)) 129 if err != nil { 130 return fmt.Errorf("the path (./%v) does not exist. run this command in your project directory", scaffold.DeployDir) 131 } 132 return nil 133 }