sigs.k8s.io/kubebuilder/v3@v3.14.0/pkg/plugins/golang/declarative/v1/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 v1
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  
    23  	log "github.com/sirupsen/logrus"
    24  
    25  	"sigs.k8s.io/kubebuilder/v3/pkg/config"
    26  	"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
    27  	"sigs.k8s.io/kubebuilder/v3/pkg/model/resource"
    28  	"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
    29  	"sigs.k8s.io/kubebuilder/v3/pkg/plugin/util"
    30  	"sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/declarative/v1/scaffolds"
    31  	goPluginV2 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v2"
    32  	goPluginV3 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v3"
    33  )
    34  
    35  const (
    36  	// kbDeclarativePattern is the sigs.k8s.io/kubebuilder-declarative-pattern version
    37  	kbDeclarativePatternForV2 = "v0.0.0-20200522144838-848d48e5b073"
    38  	kbDeclarativePatternForV3 = "18dbaf5fcd851e6adc3f2f8a8facb669a1420797"
    39  	kbDeclarativePatternForV4 = "9a410556b95de526e12acfe0d6f56fd35c0b0135"
    40  )
    41  
    42  var _ plugin.CreateAPISubcommand = &createAPISubcommand{}
    43  
    44  type createAPISubcommand struct {
    45  	config config.Config
    46  
    47  	resource *resource.Resource
    48  }
    49  
    50  func (p *createAPISubcommand) UpdateMetadata(cliMeta plugin.CLIMetadata, subcmdMeta *plugin.SubcommandMetadata) {
    51  	subcmdMeta.Description = `
    52  Scaffold a Kubernetes API by writing a Resource definition and a Controller.
    53  
    54  After the scaffold is written, the dependencies will be updated and
    55  make generate will be run.
    56  `
    57  	subcmdMeta.Examples = fmt.Sprintf(` # Create a frigates API with Group: ship, Version: v1beta1 and Kind: Frigate
    58    %[1]s create api --group ship --version v1beta1 --kind Frigate --resource --controller
    59  
    60    # Edit the API Scheme
    61    nano api/v1beta1/frigate_types.go
    62  
    63    # Edit the Controller Test
    64    nano controllers/frigate/frigate_controller_test.go
    65  
    66    # Generate the manifests
    67    make manifests
    68  
    69    # Install CRDs into the Kubernetes cluster using kubectl apply
    70    make install
    71  
    72    # Regenerate code and run against the Kubernetes cluster configured by ~/.kube/config
    73    make run
    74  `, cliMeta.CommandName)
    75  }
    76  
    77  func (p *createAPISubcommand) InjectConfig(c config.Config) error {
    78  	p.config = c
    79  
    80  	return nil
    81  }
    82  
    83  func (p *createAPISubcommand) InjectResource(res *resource.Resource) error {
    84  	p.resource = res
    85  
    86  	if !p.resource.HasAPI() || !p.resource.HasController() {
    87  		return plugin.ExitError{
    88  			Plugin: pluginName,
    89  			Reason: "declarative pattern is only supported when API and controller are scaffolded",
    90  		}
    91  	}
    92  
    93  	return nil
    94  }
    95  
    96  func (p *createAPISubcommand) Scaffold(fs machinery.Filesystem) error {
    97  	log.Println("updating scaffold with declarative pattern...")
    98  
    99  	scaffolder := scaffolds.NewAPIScaffolder(p.config, *p.resource)
   100  	scaffolder.InjectFS(fs)
   101  	err := scaffolder.Scaffold()
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	// Update Dockerfile
   107  	// nolint:staticcheck
   108  	err = updateDockerfile(plugin.IsLegacyLayout(p.config))
   109  	if err != nil {
   110  		return err
   111  	}
   112  
   113  	// Track the resources following a declarative approach
   114  	cfg := pluginConfig{}
   115  	if err := p.config.DecodePluginConfig(pluginKey, &cfg); errors.As(err, &config.UnsupportedFieldError{}) {
   116  		// Config doesn't support per-plugin configuration, so we can't track them
   117  	} else {
   118  		// Fail unless they key wasn't found, which just means it is the first resource tracked
   119  		if err != nil && !errors.As(err, &config.PluginKeyNotFoundError{}) {
   120  			return err
   121  		}
   122  
   123  		cfg.Resources = append(cfg.Resources, p.resource.GVK)
   124  		if err := p.config.EncodePluginConfig(pluginKey, cfg); err != nil {
   125  			return err
   126  		}
   127  	}
   128  
   129  	// Ensure that we are pinning sigs.k8s.io/kubebuilder-declarative-pattern version
   130  	// Just pin an old value for go/v2. It shows fine for now. However, we should improve/change it
   131  	// if we see that more rules based on the plugins version are required.
   132  	kbDeclarativePattern := kbDeclarativePatternForV4
   133  	for _, pluginKey := range p.config.GetPluginChain() {
   134  		if pluginKey == plugin.KeyFor(goPluginV2.Plugin{}) {
   135  			kbDeclarativePattern = kbDeclarativePatternForV2
   136  			break
   137  		}
   138  		if pluginKey == plugin.KeyFor(goPluginV3.Plugin{}) {
   139  			kbDeclarativePattern = kbDeclarativePatternForV3
   140  			break
   141  		}
   142  	}
   143  	err = util.RunCmd("Get declarative pattern", "go", "get",
   144  		"sigs.k8s.io/kubebuilder-declarative-pattern@"+kbDeclarativePattern)
   145  	if err != nil {
   146  		return err
   147  	}
   148  
   149  	return nil
   150  }