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  }