github.com/grafana/tanka@v0.26.1-0.20240506093700-c22cfc35c21a/cmd/tk/export.go (about)

     1  package main
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"regexp"
     7  	"runtime"
     8  
     9  	"github.com/go-clix/cli"
    10  
    11  	"github.com/grafana/tanka/pkg/process"
    12  	"github.com/grafana/tanka/pkg/spec/v1alpha1"
    13  	"github.com/grafana/tanka/pkg/tanka"
    14  )
    15  
    16  func exportCmd() *cli.Command {
    17  	args := workflowArgs
    18  	args.Validator = cli.ArgsMin(2)
    19  
    20  	cmd := &cli.Command{
    21  		Use:   "export <outputDir> <path> [<path>...]",
    22  		Short: "export environments found in path(s)",
    23  		Args:  args,
    24  	}
    25  
    26  	format := cmd.Flags().String(
    27  		"format",
    28  		"{{.apiVersion}}.{{.kind}}-{{or .metadata.name .metadata.generateName}}",
    29  		"https://tanka.dev/exporting#filenames",
    30  	)
    31  
    32  	extension := cmd.Flags().String("extension", "yaml", "File extension")
    33  	parallel := cmd.Flags().IntP("parallel", "p", 8, "Number of environments to process in parallel")
    34  	cachePath := cmd.Flags().StringP("cache-path", "c", "", "Local file path where cached evaluations should be stored")
    35  	cacheEnvs := cmd.Flags().StringArrayP("cache-envs", "e", nil, "Regexes which define which environment should be cached (if caching is enabled)")
    36  	ballastBytes := cmd.Flags().Int("mem-ballast-size-bytes", 0, "Size of memory ballast to allocate. This may improve performance for large environments.")
    37  
    38  	merge := cmd.Flags().Bool("merge", false, "Allow merging with existing directory")
    39  	if err := cmd.Flags().MarkDeprecated("merge", "use --merge-strategy=fail-on-conflicts instead"); err != nil {
    40  		panic(err)
    41  	}
    42  	mergeStrategy := cmd.Flags().String("merge-strategy", "", "What to do when exporting to an existing directory. The default setting is to disallow exporting to an existing directory. Values: 'fail-on-conflicts', 'replace-envs'")
    43  	mergeDeletedEnvs := cmd.Flags().StringArray("merge-deleted-envs", nil, "Tanka main files that have been deleted. This is used when using a merge strategy to also delete the files of these deleted environments.")
    44  
    45  	vars := workflowFlags(cmd.Flags())
    46  	getJsonnetOpts := jsonnetFlags(cmd.Flags())
    47  	getLabelSelector := labelSelectorFlag(cmd.Flags())
    48  
    49  	recursive := cmd.Flags().BoolP("recursive", "r", false, "Look recursively for Tanka environments")
    50  
    51  	cmd.Run = func(cmd *cli.Command, args []string) error {
    52  		// Allocate a block of memory to alter GC behaviour. See https://github.com/golang/go/issues/23044
    53  		ballast := make([]byte, *ballastBytes)
    54  		defer runtime.KeepAlive(ballast)
    55  
    56  		filters, err := process.StrExps(vars.targets...)
    57  		if err != nil {
    58  			return err
    59  		}
    60  
    61  		opts := tanka.ExportEnvOpts{
    62  			Format:    *format,
    63  			Extension: *extension,
    64  			Opts: tanka.Opts{
    65  				JsonnetImplementation: vars.jsonnetImplementation,
    66  				JsonnetOpts:           getJsonnetOpts(),
    67  				Filters:               filters,
    68  				Name:                  vars.name,
    69  			},
    70  			Selector:         getLabelSelector(),
    71  			Parallelism:      *parallel,
    72  			MergeDeletedEnvs: *mergeDeletedEnvs,
    73  		}
    74  
    75  		if opts.MergeStrategy, err = determineMergeStrategy(*merge, *mergeStrategy); err != nil {
    76  			return err
    77  		}
    78  
    79  		opts.Opts.CachePath = *cachePath
    80  		for _, expr := range *cacheEnvs {
    81  			regex, err := regexp.Compile(expr)
    82  			if err != nil {
    83  				return err
    84  			}
    85  			opts.Opts.CachePathRegexes = append(opts.Opts.CachePathRegexes, regex)
    86  		}
    87  
    88  		var exportEnvs []*v1alpha1.Environment
    89  		// find possible environments
    90  		if *recursive {
    91  			// get absolute path to Environment
    92  			envs, err := tanka.FindEnvsFromPaths(args[1:], tanka.FindOpts{Selector: opts.Selector, Parallelism: opts.Parallelism})
    93  			if err != nil {
    94  				return err
    95  			}
    96  
    97  			for _, env := range envs {
    98  				if opts.Opts.Name != "" && opts.Opts.Name != env.Metadata.Name {
    99  					continue
   100  				}
   101  				exportEnvs = append(exportEnvs, env)
   102  			}
   103  		} else {
   104  			if len(args[1:]) > 1 {
   105  				return fmt.Errorf("recursive flag is required when exporting multiple environments")
   106  			}
   107  
   108  			// validate environment
   109  			env, err := tanka.Peek(args[1], opts.Opts)
   110  			if err != nil {
   111  				switch err.(type) {
   112  				case tanka.ErrMultipleEnvs:
   113  					fmt.Println("Please use --name to export a single environment or --recursive to export multiple environments.")
   114  					return err
   115  				default:
   116  					return err
   117  				}
   118  			}
   119  
   120  			exportEnvs = append(exportEnvs, env)
   121  		}
   122  
   123  		// export them
   124  		return tanka.ExportEnvironments(exportEnvs, args[0], &opts)
   125  	}
   126  	return cmd
   127  }
   128  
   129  // `--merge` is deprecated in favor of `--merge-strategy`. However, merge has to keep working for now.
   130  func determineMergeStrategy(deprecatedMergeFlag bool, mergeStrategy string) (tanka.ExportMergeStrategy, error) {
   131  	if deprecatedMergeFlag && mergeStrategy != "" {
   132  		return "", errors.New("cannot use --merge and --merge-strategy at the same time")
   133  	}
   134  	if deprecatedMergeFlag {
   135  		return tanka.ExportMergeStrategyFailConflicts, nil
   136  	}
   137  
   138  	switch strategy := tanka.ExportMergeStrategy(mergeStrategy); strategy {
   139  	case tanka.ExportMergeStrategyFailConflicts, tanka.ExportMergeStrategyReplaceEnvs, tanka.ExportMergeStrategyNone:
   140  		return strategy, nil
   141  	}
   142  
   143  	return "", fmt.Errorf("invalid merge strategy: %q", mergeStrategy)
   144  }