github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/internal/commands/config_run_image_mirrors.go (about)

     1  package commands
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strings"
     7  
     8  	"github.com/pkg/errors"
     9  	"github.com/spf13/cobra"
    10  
    11  	"github.com/buildpacks/pack/internal/config"
    12  	"github.com/buildpacks/pack/internal/stringset"
    13  	"github.com/buildpacks/pack/internal/style"
    14  	"github.com/buildpacks/pack/pkg/logging"
    15  )
    16  
    17  var mirrors []string
    18  
    19  func ConfigRunImagesMirrors(logger logging.Logger, cfg config.Config, cfgPath string) *cobra.Command {
    20  	cmd := &cobra.Command{
    21  		Use:   "run-image-mirrors",
    22  		Short: "List, add and remove run image mirrors",
    23  		Args:  cobra.MaximumNArgs(3),
    24  		RunE: logError(logger, func(cmd *cobra.Command, args []string) error {
    25  			listRunImageMirror(args, logger, cfg)
    26  			return nil
    27  		}),
    28  	}
    29  
    30  	listCmd := generateListCmd(cmd.Use, logger, cfg, listRunImageMirror)
    31  	listCmd.Long = "List all run image mirrors. If a run image is provided, it will return "
    32  	listCmd.Use = "list [<run-image>]"
    33  	listCmd.Example = "pack config run-image-mirrors list"
    34  	cmd.AddCommand(listCmd)
    35  
    36  	addCmd := generateAdd("mirror for a run image", logger, cfg, cfgPath, addRunImageMirror)
    37  	addCmd.Use = "add <image> [-m <mirror...]"
    38  	addCmd.Long = "Set mirrors to other repositories for a given run image"
    39  	addCmd.Example = "pack config run-image-mirrors add cnbs/sample-stack-run:bionic --mirror index.docker.io/cnbs/sample-stack-run:bionic --mirror gcr.io/cnbs/sample-stack-run:bionic"
    40  	addCmd.Flags().StringSliceVarP(&mirrors, "mirror", "m", nil, "Run image mirror"+stringSliceHelp("mirror"))
    41  	cmd.AddCommand(addCmd)
    42  
    43  	rmCmd := generateRemove("mirror for a run image", logger, cfg, cfgPath, removeRunImageMirror)
    44  	rmCmd.Use = "remove <image> [-m <mirror...]"
    45  	rmCmd.Long = "Remove mirrors for a given run image. If specific mirrors are passed, they will be removed. " +
    46  		"If no mirrors are provided, all mirrors for the given run image will be removed from the config."
    47  	rmCmd.Example = "pack config run-image-mirrors remove cnbs/sample-stack-run:bionic"
    48  	rmCmd.Flags().StringSliceVarP(&mirrors, "mirror", "m", nil, "Run image mirror"+stringSliceHelp("mirror"))
    49  	cmd.AddCommand(rmCmd)
    50  
    51  	AddHelpFlag(cmd, "run-image-mirrors")
    52  	return cmd
    53  }
    54  
    55  func addRunImageMirror(args []string, logger logging.Logger, cfg config.Config, cfgPath string) error {
    56  	runImage := args[0]
    57  	if len(mirrors) == 0 {
    58  		logger.Infof("No run image mirrors were provided.")
    59  		return nil
    60  	}
    61  
    62  	newMirrors := mirrors
    63  	for _, image := range cfg.RunImages {
    64  		if image.Image == runImage {
    65  			newMirrors = append(newMirrors, image.Mirrors...)
    66  			break
    67  		}
    68  	}
    69  
    70  	cfg = config.SetRunImageMirrors(cfg, runImage, dedupAndSortSlice(newMirrors))
    71  	if err := config.Write(cfg, cfgPath); err != nil {
    72  		return errors.Wrapf(err, "failed to write to %s", cfgPath)
    73  	}
    74  
    75  	for _, mirror := range mirrors {
    76  		logger.Infof("Run Image %s configured with mirror %s", style.Symbol(runImage), style.Symbol(mirror))
    77  	}
    78  	return nil
    79  }
    80  
    81  func removeRunImageMirror(args []string, logger logging.Logger, cfg config.Config, cfgPath string) error {
    82  	image := args[0]
    83  
    84  	idx := -1
    85  	for i, runImage := range cfg.RunImages {
    86  		if runImage.Image == image {
    87  			idx = i
    88  		}
    89  	}
    90  
    91  	if idx == -1 || len(cfg.RunImages) == 0 {
    92  		// Run Image wasn't found
    93  		logger.Infof("No run image mirrors have been set for %s", style.Symbol(image))
    94  		return nil
    95  	}
    96  
    97  	mirrorsMap := stringset.FromSlice(mirrors)
    98  	var newMirrors []string
    99  	for _, currMirror := range cfg.RunImages[idx].Mirrors {
   100  		if _, ok := mirrorsMap[currMirror]; !ok {
   101  			newMirrors = append(newMirrors, currMirror)
   102  		}
   103  	}
   104  
   105  	if len(newMirrors) == 0 || len(mirrors) == 0 {
   106  		lastImageIdx := len(cfg.RunImages) - 1
   107  		cfg.RunImages[idx] = cfg.RunImages[lastImageIdx]
   108  		cfg.RunImages = cfg.RunImages[:lastImageIdx]
   109  	} else {
   110  		cfg = config.SetRunImageMirrors(cfg, image, newMirrors)
   111  	}
   112  
   113  	if err := config.Write(cfg, cfgPath); err != nil {
   114  		return errors.Wrapf(err, "failed to write to %s", cfgPath)
   115  	}
   116  	if len(mirrors) == 0 {
   117  		logger.Infof("Removed all run image mirrors for %s", style.Symbol(image))
   118  	} else {
   119  		logger.Infof("Removed mirrors %s for %s", strings.Join(mirrors, ", "), style.Symbol(image))
   120  	}
   121  
   122  	return nil
   123  }
   124  
   125  func listRunImageMirror(args []string, logger logging.Logger, cfg config.Config) {
   126  	var (
   127  		reqImage string
   128  		found    = false
   129  	)
   130  
   131  	if len(args) > 0 {
   132  		reqImage = args[0]
   133  	}
   134  
   135  	buf := strings.Builder{}
   136  	buf.WriteString("Run Image Mirrors:\n")
   137  	for _, runImage := range cfg.RunImages {
   138  		if (reqImage != "" && runImage.Image == reqImage) || reqImage == "" {
   139  			found = true
   140  			buf.WriteString(fmt.Sprintf("  %s:\n", style.Symbol(runImage.Image)))
   141  			for _, mirror := range runImage.Mirrors {
   142  				buf.WriteString(fmt.Sprintf("    %s\n", mirror))
   143  			}
   144  		}
   145  	}
   146  
   147  	if !found {
   148  		suffix := ""
   149  		if reqImage != "" {
   150  			suffix = fmt.Sprintf("for %s", style.Symbol(reqImage))
   151  		}
   152  		logger.Infof("No run image mirrors have been set %s", suffix)
   153  	} else {
   154  		logger.Info(buf.String())
   155  	}
   156  }
   157  
   158  func dedupAndSortSlice(slice []string) []string {
   159  	set := stringset.FromSlice(slice)
   160  	var newSlice []string
   161  	for s := range set {
   162  		newSlice = append(newSlice, s)
   163  	}
   164  	sort.Strings(newSlice)
   165  	return newSlice
   166  }