github.com/SamarSidharth/kpt@v0.0.0-20231122062228-c7d747ae3ace/commands/pkg/update/cmdupdate.go (about)

     1  // Copyright 2019 The kpt 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 update
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"os"
    21  	"path/filepath"
    22  	"strings"
    23  
    24  	docs "github.com/GoogleContainerTools/kpt/internal/docs/generated/pkgdocs"
    25  	"github.com/GoogleContainerTools/kpt/internal/errors"
    26  	"github.com/GoogleContainerTools/kpt/internal/pkg"
    27  	"github.com/GoogleContainerTools/kpt/internal/types"
    28  	"github.com/GoogleContainerTools/kpt/internal/util/argutil"
    29  	"github.com/GoogleContainerTools/kpt/internal/util/cmdutil"
    30  	"github.com/GoogleContainerTools/kpt/internal/util/pathutil"
    31  	"github.com/GoogleContainerTools/kpt/internal/util/update"
    32  	kptfilev1 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1"
    33  	"github.com/spf13/cobra"
    34  	"sigs.k8s.io/kustomize/kyaml/filesys"
    35  )
    36  
    37  // NewRunner returns a command runner.
    38  func NewRunner(ctx context.Context, parent string) *Runner {
    39  	r := &Runner{
    40  		ctx: ctx,
    41  	}
    42  	c := &cobra.Command{
    43  		Use:        "update [PKG_PATH@VERSION] [flags]",
    44  		Short:      docs.UpdateShort,
    45  		Long:       docs.UpdateShort + "\n" + docs.UpdateLong,
    46  		Example:    docs.UpdateExamples,
    47  		RunE:       r.runE,
    48  		Args:       cobra.MaximumNArgs(1),
    49  		PreRunE:    r.preRunE,
    50  		SuggestFor: []string{"rebase", "replace"},
    51  	}
    52  
    53  	c.Flags().StringVar(&r.strategy, "strategy", string(kptfilev1.ResourceMerge),
    54  		"the update strategy that will be used when updating the package. This will change "+
    55  			"the default strategy for the package -- must be one of: "+
    56  			strings.Join(kptfilev1.UpdateStrategiesAsStrings(), ","))
    57  	_ = c.RegisterFlagCompletionFunc("strategy", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
    58  		return kptfilev1.UpdateStrategiesAsStrings(), cobra.ShellCompDirectiveDefault
    59  	})
    60  	cmdutil.FixDocs("kpt", parent, c)
    61  	r.Command = c
    62  	return r
    63  }
    64  
    65  func NewCommand(ctx context.Context, parent string) *cobra.Command {
    66  	return NewRunner(ctx, parent).Command
    67  }
    68  
    69  // Runner contains the run function.
    70  // TODO, support listing versions
    71  type Runner struct {
    72  	ctx      context.Context
    73  	strategy string
    74  	Update   update.Command
    75  	Command  *cobra.Command
    76  }
    77  
    78  func (r *Runner) preRunE(_ *cobra.Command, args []string) error {
    79  	const op errors.Op = "cmdupdate.preRunE"
    80  	if len(args) == 0 {
    81  		args = append(args, pkg.CurDir)
    82  	}
    83  	if r.strategy == "" {
    84  		r.Update.Strategy = kptfilev1.ResourceMerge
    85  	} else {
    86  		r.Update.Strategy = kptfilev1.UpdateStrategyType(r.strategy)
    87  	}
    88  
    89  	parts := strings.Split(args[0], "@")
    90  	if len(parts) > 2 {
    91  		return errors.E(op, errors.InvalidParam, fmt.Errorf("at most 1 version permitted"))
    92  	}
    93  
    94  	resolvedPath, err := argutil.ResolveSymlink(r.ctx, parts[0])
    95  	if err != nil {
    96  		return err
    97  	}
    98  	absResolvedPath, _, err := pathutil.ResolveAbsAndRelPaths(resolvedPath)
    99  	if err != nil {
   100  		return err
   101  	}
   102  	p, err := pkg.New(filesys.FileSystemOrOnDisk{}, absResolvedPath)
   103  	if err != nil {
   104  		return errors.E(op, err)
   105  	}
   106  
   107  	r.Update.Pkg = p
   108  
   109  	// TODO: Make sure we handle this in a centralized library and do
   110  	// this consistently across all commands.
   111  	relPath, err := resolveRelPath(p.UniquePath)
   112  	if err != nil {
   113  		return errors.E(op, p.UniquePath, err)
   114  	}
   115  	if strings.HasPrefix(relPath, pkg.ParentDir) {
   116  		return errors.E(op, p.UniquePath, fmt.Errorf("package path must be under current working directory"))
   117  	}
   118  
   119  	if len(parts) > 1 {
   120  		r.Update.Ref = parts[1]
   121  	}
   122  	return nil
   123  }
   124  
   125  func (r *Runner) runE(_ *cobra.Command, _ []string) error {
   126  	const op errors.Op = "cmdupdate.runE"
   127  	if err := r.Update.Run(r.ctx); err != nil {
   128  		return errors.E(op, r.Update.Pkg.UniquePath, err)
   129  	}
   130  
   131  	return nil
   132  }
   133  
   134  func resolveRelPath(path types.UniquePath) (string, error) {
   135  	const op errors.Op = "cmdupdate.resolveRelPath"
   136  	cwd, err := os.Getwd()
   137  	if err != nil {
   138  		return "", errors.E(op, errors.IO,
   139  			fmt.Errorf("error looking up current working directory: %w", err))
   140  	}
   141  
   142  	relPath, err := filepath.Rel(cwd, path.String())
   143  	if err != nil {
   144  		return "", errors.E(op, errors.IO,
   145  			fmt.Errorf("error resolving the relative path: %w", err))
   146  	}
   147  	return relPath, nil
   148  }