github.com/verrazzano/verrazzano@v1.7.0/tools/charts-manager/vcm/cmd/pull/pull.go (about)

     1  // Copyright (c) 2023, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package pull
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/spf13/cobra"
    13  	vcmhelpers "github.com/verrazzano/verrazzano/tools/charts-manager/vcm/cmd/helpers"
    14  	"github.com/verrazzano/verrazzano/tools/charts-manager/vcm/pkg/constants"
    15  	"github.com/verrazzano/verrazzano/tools/charts-manager/vcm/pkg/fs"
    16  	"github.com/verrazzano/verrazzano/tools/charts-manager/vcm/pkg/helm"
    17  	cmdhelpers "github.com/verrazzano/verrazzano/tools/vz/cmd/helpers"
    18  	"github.com/verrazzano/verrazzano/tools/vz/pkg/helpers"
    19  )
    20  
    21  const (
    22  	CommandName             = "pull"
    23  	helpShort               = "Pulls an upstream chart/version"
    24  	helpLong                = `The command 'pull' pulls an upstream chart from a given helm repo url and unpacks that to a charts directory.`
    25  	ErrPatchVersionNotFound = "unable to find version to patch, error %v"
    26  	ErrPatchNotGenerated    = "unable to generate patch file, error %v"
    27  	ErrPatchNotApplied      = "unable to apply patch file %s, error %v"
    28  	ErrPatchNotRemoved      = "unable to remove patch file at %v, error %v"
    29  	ErrPatchReview          = "please review patch file at %s and applied changes"
    30  )
    31  
    32  func buildExample() string {
    33  	examples := []string{fmt.Sprintf(constants.CommandWithFlagExampleFormat+" "+
    34  		constants.FlagExampleFormat+" "+
    35  		constants.FlagExampleFormat+" "+
    36  		constants.FlagExampleFormat,
    37  		CommandName, constants.FlagChartName, constants.FlagChartShorthand, constants.FlagChartExampleKeycloak,
    38  		constants.FlagVersionName, constants.FlagPatchVersionShorthand, constants.FlagVersionExample210,
    39  		constants.FlagDirName, constants.FlagDirShorthand, constants.FlagDirExampleLocal,
    40  		constants.FlagRepoName, constants.FlagRepoShorthand, constants.FlagRepoExampleCodecentric)}
    41  
    42  	examples = append(examples, "\n")
    43  	examples = append(examples, fmt.Sprintf(constants.CommandWithFlagExampleFormat, examples[len(examples)-1],
    44  		constants.FlagTargetVersionName, constants.FlagChartShorthand, constants.FlagTargetVersionExample002))
    45  
    46  	examples = append(examples, "\n")
    47  	examples = append(examples, fmt.Sprintf(constants.CommandWithFlagExampleFormat, examples[len(examples)-1],
    48  		constants.FlagUpstreamProvenanceName, constants.FlagUpstreamProvenanceShorthand, constants.FlagUpstreamProvenanceDefault))
    49  
    50  	examples = append(examples, "\n")
    51  	examples = append(examples, fmt.Sprintf(constants.CommandWithFlagExampleFormat, examples[len(examples)-1],
    52  		constants.FlagPatchName, constants.FlagPatchShorthand, constants.FlagPatchDefault))
    53  
    54  	examples = append(examples, "\n")
    55  	examples = append(examples, fmt.Sprintf(constants.CommandWithFlagExampleFormat, examples[len(examples)-1],
    56  		constants.FlagPatchVersionName, constants.FlagPatchVersionShorthand, constants.FlagPatchVersionExample001))
    57  
    58  	return fmt.Sprintln(examples)
    59  }
    60  
    61  // NewCmdPull creates a new instance of pull cmd.
    62  func NewCmdPull(vzHelper helpers.VZHelper, inHfs fs.ChartFileSystem, inHelmConfig helm.HelmConfig) *cobra.Command {
    63  	cmd := cmdhelpers.NewCommand(vzHelper, CommandName, helpShort, helpLong)
    64  	cmd.RunE = func(cmd *cobra.Command, args []string) error {
    65  		var hfs fs.ChartFileSystem
    66  		var helmConfig helm.HelmConfig
    67  		if inHfs == nil {
    68  			hfs = fs.HelmChartFileSystem{}
    69  		} else {
    70  			hfs = inHfs
    71  		}
    72  
    73  		if inHelmConfig == nil {
    74  			var err error
    75  			helmConfig, err = helm.NewHelmConfig(vzHelper)
    76  			if err != nil {
    77  				return fmt.Errorf("unable to init helm config, error %v", err)
    78  			}
    79  		} else {
    80  			helmConfig = inHelmConfig
    81  		}
    82  
    83  		return runCmdPull(cmd, vzHelper, hfs, helmConfig)
    84  	}
    85  	cmd.Example = buildExample()
    86  	cmd.PersistentFlags().StringP(constants.FlagChartName, constants.FlagChartShorthand, "", constants.FlagChartUsage)
    87  	cmd.PersistentFlags().StringP(constants.FlagVersionName, constants.FlagVersionShorthand, "", constants.FlagVersionUsage)
    88  	cmd.PersistentFlags().StringP(constants.FlagDirName, constants.FlagDirShorthand, "", constants.FlagDirUsage)
    89  	cmd.PersistentFlags().StringP(constants.FlagRepoName, constants.FlagRepoShorthand, "", constants.FlagRepoUsage)
    90  	cmd.PersistentFlags().StringP(constants.FlagTargetVersionName, constants.FlagTargetVersionShorthand, "", constants.FlagTargetVersionUsage)
    91  	cmd.PersistentFlags().BoolP(constants.FlagUpstreamProvenanceName, constants.FlagUpstreamProvenanceShorthand, constants.FlagUpstreamProvenanceDefault, constants.FlagUpstreamProvenanceUsage)
    92  	cmd.PersistentFlags().BoolP(constants.FlagPatchName, constants.FlagPatchShorthand, constants.FlagPatchDefault, constants.FlagPatchUsage)
    93  	cmd.PersistentFlags().StringP(constants.FlagPatchVersionName, constants.FlagPatchVersionShorthand, "", constants.FlagPatchVersionUsage)
    94  
    95  	return cmd
    96  }
    97  
    98  // runCmdPull - run the "vcm pull" command to pull a chart/version from a helm repo and unpack into a chart dierctory with either the same version
    99  // or a target version. Also saves the original chart in a upstream directory and applies any changes that were done to prevopus versions of
   100  // this chart in the same charts directory.
   101  func runCmdPull(cmd *cobra.Command, vzHelper helpers.VZHelper, hfs fs.ChartFileSystem, helmConfig helm.HelmConfig) error {
   102  	chart, err := vcmhelpers.GetMandatoryStringFlagValueOrError(cmd, constants.FlagChartName, constants.FlagChartShorthand)
   103  	if err != nil {
   104  		return err
   105  	}
   106  
   107  	version, err := vcmhelpers.GetMandatoryStringFlagValueOrError(cmd, constants.FlagVersionName, constants.FlagVersionShorthand)
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	chartsDir, err := vcmhelpers.GetMandatoryStringFlagValueOrError(cmd, constants.FlagDirName, constants.FlagDirShorthand)
   113  	if err != nil {
   114  		return err
   115  	}
   116  
   117  	repo, err := vcmhelpers.GetMandatoryStringFlagValueOrError(cmd, constants.FlagRepoName, constants.FlagRepoShorthand)
   118  	if err != nil {
   119  		return err
   120  	}
   121  
   122  	targetVersion, err := cmd.PersistentFlags().GetString(constants.FlagTargetVersionName)
   123  	if err != nil {
   124  		return err
   125  	}
   126  
   127  	if targetVersion == "" {
   128  		targetVersion = version
   129  	}
   130  
   131  	if len(strings.TrimSpace(targetVersion)) == 0 {
   132  		return fmt.Errorf(vcmhelpers.ErrFormatNotEmpty, constants.FlagTargetVersionName)
   133  	}
   134  
   135  	saveUpstream, err := cmd.PersistentFlags().GetBool(constants.FlagUpstreamProvenanceName)
   136  	if err != nil {
   137  		return err
   138  	}
   139  
   140  	patchDiffs, err := cmd.PersistentFlags().GetBool(constants.FlagPatchName)
   141  	if err != nil {
   142  		return err
   143  	}
   144  
   145  	var patchVersion string
   146  	if patchDiffs {
   147  		patchVersion, err = cmd.PersistentFlags().GetString(constants.FlagPatchVersionName)
   148  		if err != nil {
   149  			return err
   150  		}
   151  	}
   152  
   153  	fmt.Fprintf(vzHelper.GetOutputStream(), "Adding/Updtaing %s chart repo with url %s..\n", chart, repo)
   154  	repoName, err := helmConfig.AddAndUpdateChartRepo(chart, repo)
   155  	if err != nil {
   156  		return err
   157  	}
   158  
   159  	fmt.Fprintf(vzHelper.GetOutputStream(), "Pulling %s chart version %s to target version %s..\n", chart, version, targetVersion)
   160  	err = helmConfig.DownloadChart(chart, repoName, version, targetVersion, chartsDir)
   161  	if err != nil {
   162  		return err
   163  	}
   164  
   165  	err = hfs.RearrangeChartDirectory(chartsDir, chart, targetVersion)
   166  	if err != nil {
   167  		return err
   168  	}
   169  
   170  	fmt.Fprintf(vzHelper.GetOutputStream(), "Pulled chart %s version %s to target version %s from %s to %s/%s/%s.\n", chart, version, targetVersion, repo, chartsDir, chart, targetVersion)
   171  	if saveUpstream {
   172  		fmt.Fprintf(vzHelper.GetOutputStream(), "Saving upstream chart..\n")
   173  		err = hfs.SaveUpstreamChart(chartsDir, chart, version, targetVersion)
   174  		if err != nil {
   175  			return err
   176  		}
   177  
   178  		upstreamChartDir, err := filepath.Abs(fmt.Sprintf("%s/../provenance/%s/upstreams/%s", chartsDir, chart, version))
   179  		if err != nil {
   180  			return err
   181  		}
   182  
   183  		fmt.Fprintf(vzHelper.GetOutputStream(), "Upstream chart saved to %s.\n", upstreamChartDir)
   184  		fmt.Fprintf(vzHelper.GetOutputStream(), "Saving chart provenance file..\n")
   185  		chartProvenance, err := helmConfig.GetChartProvenance(chart, repo, version)
   186  		if err != nil {
   187  			return err
   188  		}
   189  
   190  		err = hfs.SaveChartProvenance(chartsDir, chartProvenance, chart, targetVersion)
   191  		if err != nil {
   192  			return err
   193  		}
   194  
   195  		provenanceFile, err := filepath.Abs(fmt.Sprintf("%s/../provenance/%s/%s.yaml", chartsDir, chart, targetVersion))
   196  		if err != nil {
   197  			return err
   198  		}
   199  
   200  		fmt.Fprintf(vzHelper.GetOutputStream(), "Upstream provenance manifest created in %s.\n", provenanceFile)
   201  	}
   202  
   203  	if patchDiffs {
   204  		if patchVersion == "" {
   205  			patchVersion, err = hfs.FindChartVersionToPatch(chartsDir, chart, targetVersion)
   206  			if err != nil {
   207  				return fmt.Errorf(ErrPatchVersionNotFound, err)
   208  			}
   209  		}
   210  
   211  		if patchVersion != "" {
   212  			patchFile, err := hfs.GeneratePatchFile(chartsDir, chart, patchVersion)
   213  			if err != nil {
   214  				return fmt.Errorf(ErrPatchNotGenerated, err)
   215  			}
   216  
   217  			if patchFile == "" {
   218  				fmt.Fprintf(vzHelper.GetOutputStream(), "Nothing to patch from previous version.\n")
   219  				return nil
   220  			}
   221  
   222  			rejectsFileGenerated, err := hfs.ApplyPatchFile(chartsDir, vzHelper, chart, targetVersion, patchFile)
   223  			if err != nil {
   224  				return fmt.Errorf(ErrPatchNotApplied, patchFile, err)
   225  			}
   226  
   227  			if !rejectsFileGenerated {
   228  				fmt.Fprintf(vzHelper.GetOutputStream(), "Any diffs from version %s has been applied.\n", patchVersion)
   229  				err = os.Remove(patchFile)
   230  				if err != nil {
   231  					return fmt.Errorf(ErrPatchNotRemoved, patchFile, err)
   232  				}
   233  			} else {
   234  				return fmt.Errorf(ErrPatchReview, patchFile)
   235  			}
   236  		}
   237  	}
   238  	return nil
   239  }