github.com/mkimuram/operator-sdk@v0.7.1-0.20190410172100-52ad33a4bda0/cmd/operator-sdk/add/api.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  
    20  	"github.com/operator-framework/operator-sdk/cmd/operator-sdk/internal/genutil"
    21  	"github.com/operator-framework/operator-sdk/internal/pkg/scaffold"
    22  	"github.com/operator-framework/operator-sdk/internal/pkg/scaffold/input"
    23  	"github.com/operator-framework/operator-sdk/internal/util/projutil"
    24  
    25  	log "github.com/sirupsen/logrus"
    26  	"github.com/spf13/cobra"
    27  )
    28  
    29  var (
    30  	apiVersion string
    31  	kind       string
    32  	headerFile string
    33  )
    34  
    35  func newAddApiCmd() *cobra.Command {
    36  	apiCmd := &cobra.Command{
    37  		Use:   "api",
    38  		Short: "Adds a new api definition under pkg/apis",
    39  		Long: `operator-sdk add api --kind=<kind> --api-version=<group/version> creates the
    40  api definition for a new custom resource under pkg/apis. This command must be
    41  run from the project root directory. If the api already exists at
    42  pkg/apis/<group>/<version> then the command will not overwrite and return an
    43  error.
    44  
    45  This command runs Kubernetes deepcopy and OpenAPI V3 generators on tagged
    46  types in all paths under pkg/apis. Go code is generated under
    47  pkg/apis/<group>/<version>/zz_generated.{deepcopy,openapi}.go. CRD's are
    48  generated, or updated if they exist for a particular group + version + kind,
    49  under deploy/crds/<group>_<version>_<kind>_crd.yaml; OpenAPI V3 validation YAML
    50  is generated as a 'validation' object.
    51  
    52  Example:
    53  	$ operator-sdk add api --api-version=app.example.com/v1alpha1 --kind=AppService
    54  	$ tree pkg/apis
    55  	pkg/apis/
    56  	├── addtoscheme_app_appservice.go
    57  	├── apis.go
    58  	└── app
    59  		└── v1alpha1
    60  			├── doc.go
    61  			├── register.go
    62  			├── appservice_types.go
    63  			├── zz_generated.deepcopy.go
    64  			├── zz_generated.openapi.go
    65  	$ tree deploy/crds
    66  	├── deploy/crds/app_v1alpha1_appservice_cr.yaml
    67  	├── deploy/crds/app_v1alpha1_appservice_crd.yaml
    68  `,
    69  		RunE: apiRun,
    70  	}
    71  
    72  	apiCmd.Flags().StringVar(&apiVersion, "api-version", "", "Kubernetes APIVersion that has a format of $GROUP_NAME/$VERSION (e.g app.example.com/v1alpha1)")
    73  	if err := apiCmd.MarkFlagRequired("api-version"); err != nil {
    74  		log.Fatalf("Failed to mark `api-version` flag for `add api` subcommand as required")
    75  	}
    76  	apiCmd.Flags().StringVar(&kind, "kind", "", "Kubernetes resource Kind name. (e.g AppService)")
    77  	if err := apiCmd.MarkFlagRequired("kind"); err != nil {
    78  		log.Fatalf("Failed to mark `kind` flag for `add api` subcommand as required")
    79  	}
    80  	apiCmd.Flags().StringVar(&headerFile, "header-file", "", "Path to file containing headers for generated files.")
    81  
    82  	return apiCmd
    83  }
    84  
    85  func apiRun(cmd *cobra.Command, args []string) error {
    86  	projutil.MustInProjectRoot()
    87  
    88  	// Only Go projects can add apis.
    89  	if err := projutil.CheckGoProjectCmd(cmd); err != nil {
    90  		return err
    91  	}
    92  
    93  	log.Infof("Generating api version %s for kind %s.", apiVersion, kind)
    94  
    95  	// Create and validate new resource.
    96  	r, err := scaffold.NewResource(apiVersion, kind)
    97  	if err != nil {
    98  		return err
    99  	}
   100  
   101  	absProjectPath := projutil.MustGetwd()
   102  
   103  	cfg := &input.Config{
   104  		Repo:           projutil.CheckAndGetProjectGoPkg(),
   105  		AbsProjectPath: absProjectPath,
   106  	}
   107  
   108  	s := &scaffold.Scaffold{}
   109  	err = s.Execute(cfg,
   110  		&scaffold.Types{Resource: r},
   111  		&scaffold.AddToScheme{Resource: r},
   112  		&scaffold.Register{Resource: r},
   113  		&scaffold.Doc{Resource: r},
   114  		&scaffold.CR{Resource: r},
   115  		&scaffold.CRD{Resource: r, IsOperatorGo: projutil.IsOperatorGo()},
   116  	)
   117  	if err != nil {
   118  		return fmt.Errorf("api scaffold failed: (%v)", err)
   119  	}
   120  
   121  	// update deploy/role.yaml for the given resource r.
   122  	if err := scaffold.UpdateRoleForResource(r, absProjectPath); err != nil {
   123  		return fmt.Errorf("failed to update the RBAC manifest for the resource (%v, %v): (%v)", r.APIVersion, r.Kind, err)
   124  	}
   125  
   126  	// Run k8s codegen for deepcopy
   127  	if err := genutil.K8sCodegen(headerFile); err != nil {
   128  		return err
   129  	}
   130  
   131  	// Generate a validation spec for the new CRD.
   132  	if err := genutil.OpenAPIGen(headerFile); err != nil {
   133  		return err
   134  	}
   135  
   136  	log.Info("API generation complete.")
   137  	return nil
   138  }