github.com/jenkins-x/jx/v2@v2.1.155/pkg/cmd/step/create/step_create_templated_config.go (about)

     1  package create
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"path/filepath"
     8  	"text/template"
     9  
    10  	"github.com/jenkins-x/jx-logging/pkg/log"
    11  	"github.com/jenkins-x/jx/v2/pkg/cmd/helper"
    12  	"github.com/jenkins-x/jx/v2/pkg/cmd/opts"
    13  	"github.com/jenkins-x/jx/v2/pkg/cmd/opts/step"
    14  	"github.com/jenkins-x/jx/v2/pkg/cmd/templates"
    15  	"github.com/jenkins-x/jx/v2/pkg/config"
    16  	"github.com/jenkins-x/jx/v2/pkg/helm"
    17  	"github.com/jenkins-x/jx/v2/pkg/util"
    18  	"github.com/pkg/errors"
    19  	"github.com/spf13/cobra"
    20  	"k8s.io/helm/pkg/chartutil"
    21  )
    22  
    23  var (
    24  	createTemplatedConfigLong = templates.LongDesc(`
    25  		Creates a config file from a Go template file and a jx requirements file
    26  `)
    27  
    28  	createTemplatedConfigExample = templates.Examples(`
    29  		# creates a config file from a template file and a jx requirements file
    30  		jx step create templated config -t config.tmpl.yml -c config.yml
    31  `)
    32  )
    33  
    34  const (
    35  	templateFileOption    = "template-file"
    36  	parametersFileOption  = "parameters-file"
    37  	requirementsDirOption = "requirements-dir"
    38  	configFileOption      = "config-file"
    39  )
    40  
    41  // StepCreateTemplatedConfigOptions command line flags
    42  type StepCreateTemplatedConfigOptions struct {
    43  	step.StepOptions
    44  
    45  	TemplateFile    string
    46  	ParametersFile  string
    47  	RequirementsDir string
    48  	ConfigFile      string
    49  }
    50  
    51  // NewCmdStepCreateTemplatedConfig Creates a new Command object
    52  func NewCmdStepCreateTemplatedConfig(commonOpts *opts.CommonOptions) *cobra.Command {
    53  	options := &StepCreateTemplatedConfigOptions{
    54  		StepOptions: step.StepOptions{
    55  			CommonOptions: commonOpts,
    56  		},
    57  	}
    58  
    59  	cmd := &cobra.Command{
    60  		Use:     "templated config",
    61  		Short:   "Create a YAML config file from a Go template file and a jx requirements file",
    62  		Long:    createTemplatedConfigLong,
    63  		Example: createTemplatedConfigExample,
    64  		Run: func(cmd *cobra.Command, args []string) {
    65  			options.Cmd = cmd
    66  			options.Args = args
    67  			err := options.Run()
    68  			helper.CheckErr(err)
    69  		},
    70  	}
    71  
    72  	cmd.Flags().StringVarP(&options.TemplateFile, templateFileOption, "t", "", "The template file used to render the config YAML file")
    73  	cmd.Flags().StringVarP(&options.ParametersFile, parametersFileOption, "p", "", "The values file used as parameters in the template file")
    74  	cmd.Flags().StringVarP(&options.RequirementsDir, requirementsDirOption, "r", ".", "The jx requirements file directory")
    75  	cmd.Flags().StringVarP(&options.ConfigFile, configFileOption, "c", "", "The rendered config YAML file")
    76  
    77  	return cmd
    78  }
    79  
    80  func (o *StepCreateTemplatedConfigOptions) checkFlags() error {
    81  	if o.TemplateFile == "" {
    82  		return util.MissingArgument(templateFileOption)
    83  	}
    84  
    85  	if o.ParametersFile != "" {
    86  		parametersFile := filepath.Base(o.ParametersFile)
    87  		if parametersFile != helm.ParametersYAMLFile {
    88  			return fmt.Errorf("provided parameters file %q must be named %q", parametersFile, helm.ParametersYAMLFile)
    89  		}
    90  	}
    91  
    92  	if exists, err := util.FileExists(o.TemplateFile); err != nil || !exists {
    93  		return fmt.Errorf("template file %q provided in option %q does not exist", o.TemplateFile, templateFileOption)
    94  	}
    95  
    96  	if o.ConfigFile == "" {
    97  		// Override the rendered config file with the template file if it is not specified
    98  		o.ConfigFile = o.TemplateFile
    99  	}
   100  
   101  	return nil
   102  }
   103  
   104  // Run implements this command
   105  func (o *StepCreateTemplatedConfigOptions) Run() error {
   106  	if err := o.checkFlags(); err != nil {
   107  		return err
   108  	}
   109  	requirements, _, err := config.LoadRequirementsConfig(o.RequirementsDir, config.DefaultFailOnValidationError)
   110  	if err != nil {
   111  		return errors.Wrapf(err, "loading requirements file form dir %q", o.RequirementsDir)
   112  	}
   113  	data, err := o.renderTemplate(requirements)
   114  	if err != nil {
   115  		return errors.Wrapf(err, "rendering the config template using the requirements from dir %q", o.RequirementsDir)
   116  	}
   117  	if err := ioutil.WriteFile(o.ConfigFile, data, util.DefaultFileWritePermissions); err != nil {
   118  		return errors.Wrapf(err, "writing the rendered config into file %q", o.ConfigFile)
   119  	}
   120  
   121  	log.Logger().Infof("Saved the rendered configuration into %s file", o.ConfigFile)
   122  
   123  	return nil
   124  }
   125  
   126  func (o *StepCreateTemplatedConfigOptions) renderTemplate(requirements *config.RequirementsConfig) ([]byte, error) {
   127  	templateName := filepath.Base(o.TemplateFile)
   128  	tmpl, err := template.New(templateName).Option("missingkey=error").ParseFiles(o.TemplateFile)
   129  	if err != nil {
   130  		return nil, errors.Wrap(err, "parsing the template file")
   131  	}
   132  	requirementsMap, err := requirements.ToMap()
   133  	if err != nil {
   134  		return nil, errors.Wrapf(err, "converting requirements into a map: %v", requirements)
   135  	}
   136  	params, err := o.parameters()
   137  	if err != nil {
   138  		return nil, errors.Wrapf(err, "loading the parameter values")
   139  	}
   140  
   141  	tmplData := map[string]interface{}{
   142  		"Requirements": requirementsMap,
   143  		"Parameters":   params,
   144  	}
   145  
   146  	var buf bytes.Buffer
   147  	err = tmpl.Execute(&buf, tmplData)
   148  	if err != nil {
   149  		return nil, errors.Wrapf(err, "rendering the template file: %s", o.TemplateFile)
   150  	}
   151  	return buf.Bytes(), nil
   152  }
   153  
   154  // parameters loads the parameters from file without solving the secrets URIs
   155  func (o *StepCreateTemplatedConfigOptions) parameters() (chartutil.Values, error) {
   156  	if o.ParametersFile == "" {
   157  		return chartutil.Values{}, nil
   158  	}
   159  	paramsDir := filepath.Dir(o.ParametersFile)
   160  	return helm.LoadParameters(paramsDir, nil)
   161  }