github.com/docker/app@v0.9.1-beta3.0.20210611140623-a48f773ab002/internal/bundle/parameters.go (about)

     1  package bundle
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"strings"
     9  
    10  	"github.com/deislabs/cnab-go/bundle"
    11  	"github.com/docker/app/internal"
    12  	"github.com/docker/app/internal/packager"
    13  	"github.com/docker/app/internal/store"
    14  	"github.com/docker/app/types/parameters"
    15  	cliopts "github.com/docker/cli/opts"
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  // MergeBundleConfig is the actual parameters and bundle parameters to be merged
    20  type MergeBundleConfig struct {
    21  	bundle *bundle.Bundle
    22  	params map[string]string
    23  	stderr io.Writer
    24  }
    25  
    26  // MergeBundleOpt is a functional option of the bundle parameter merge function
    27  type MergeBundleOpt func(c *MergeBundleConfig) error
    28  
    29  func WithFileParameters(parametersFiles []string) MergeBundleOpt {
    30  	return func(c *MergeBundleConfig) error {
    31  		p, err := parameters.LoadFiles(parametersFiles)
    32  		if err != nil {
    33  			return err
    34  		}
    35  		for k, v := range p.Flatten() {
    36  			c.params[k] = v
    37  		}
    38  		return nil
    39  	}
    40  }
    41  
    42  func WithCommandLineParameters(overrides []string) MergeBundleOpt {
    43  	return func(c *MergeBundleConfig) error {
    44  		d := cliopts.ConvertKVStringsToMap(overrides)
    45  		for k, v := range d {
    46  			c.params[k] = v
    47  		}
    48  		return nil
    49  	}
    50  }
    51  
    52  func WithLabels(labels []string) MergeBundleOpt {
    53  	return func(c *MergeBundleConfig) error {
    54  		for _, l := range labels {
    55  			if strings.HasPrefix(l, internal.Namespace) {
    56  				return errors.Errorf("labels cannot start with %q", internal.Namespace)
    57  			}
    58  		}
    59  		l := packager.DockerAppArgs{
    60  			Labels: cliopts.ConvertKVStringsToMap(labels),
    61  		}
    62  		out, err := json.Marshal(l)
    63  		if err != nil {
    64  			return err
    65  		}
    66  		if _, ok := c.bundle.Parameters[internal.ParameterArgs]; ok {
    67  			c.params[internal.ParameterArgs] = string(out)
    68  		}
    69  		return nil
    70  	}
    71  }
    72  
    73  func WithSendRegistryAuth(sendRegistryAuth bool) MergeBundleOpt {
    74  	return func(c *MergeBundleConfig) error {
    75  		if _, ok := c.bundle.Definitions[internal.ParameterShareRegistryCredsName]; ok {
    76  			val := "false"
    77  			if sendRegistryAuth {
    78  				val = "true"
    79  			}
    80  			c.params[internal.ParameterShareRegistryCredsName] = val
    81  		}
    82  		return nil
    83  	}
    84  }
    85  
    86  func WithOrchestratorParameters(orchestrator string, kubeNamespace string) MergeBundleOpt {
    87  	return func(c *MergeBundleConfig) error {
    88  		if _, ok := c.bundle.Definitions[internal.ParameterOrchestratorName]; ok {
    89  			c.params[internal.ParameterOrchestratorName] = orchestrator
    90  		}
    91  		if _, ok := c.bundle.Definitions[internal.ParameterKubernetesNamespaceName]; ok {
    92  			c.params[internal.ParameterKubernetesNamespaceName] = kubeNamespace
    93  		}
    94  		return nil
    95  	}
    96  }
    97  
    98  func WithErrorWriter(w io.Writer) MergeBundleOpt {
    99  	return func(c *MergeBundleConfig) error {
   100  		c.stderr = w
   101  		return nil
   102  	}
   103  }
   104  
   105  // MergeBundleParameters merges current, provided and bundle default parameters
   106  func MergeBundleParameters(installation *store.Installation, ops ...MergeBundleOpt) error {
   107  	bndl := installation.Bundle
   108  	if installation.Parameters == nil {
   109  		installation.Parameters = make(map[string]interface{})
   110  	}
   111  	userParams := map[string]string{}
   112  	cfg := &MergeBundleConfig{
   113  		bundle: bndl,
   114  		params: userParams,
   115  		stderr: os.Stderr,
   116  	}
   117  
   118  	for _, op := range ops {
   119  		if err := op(cfg); err != nil {
   120  			return err
   121  		}
   122  	}
   123  	mergedValues, err := matchAndMergeParametersDefinition(installation.Parameters, cfg)
   124  	if err != nil {
   125  		return err
   126  	}
   127  	installation.Parameters, err = bundle.ValuesOrDefaults(mergedValues, bndl)
   128  	return err
   129  }
   130  
   131  func matchAndMergeParametersDefinition(currentValues map[string]interface{}, cfg *MergeBundleConfig) (map[string]interface{}, error) {
   132  	mergedValues := make(map[string]interface{})
   133  	for k, v := range currentValues {
   134  		mergedValues[k] = v
   135  	}
   136  	for k, v := range cfg.params {
   137  		param, ok := cfg.bundle.Parameters[k]
   138  		if !ok {
   139  			fmt.Fprintf(cfg.stderr, "Warning: parameter %q is not defined in the bundle\n", k)
   140  			continue
   141  		}
   142  		definition, ok := cfg.bundle.Definitions[param.Definition]
   143  		if !ok {
   144  			return nil, fmt.Errorf("invalid bundle: definition not found for parameter %q", k)
   145  		}
   146  		value, err := definition.ConvertValue(v)
   147  		if err != nil {
   148  			return nil, errors.Wrapf(err, "invalid value for parameter %q", k)
   149  		}
   150  		valErrors, err := definition.Validate(value)
   151  		if valErrors != nil {
   152  			errs := make([]string, len(valErrors))
   153  			for i, v := range valErrors {
   154  				errs[i] = v.Error
   155  			}
   156  			errMsg := strings.Join(errs, ", ")
   157  			return nil, errors.Wrapf(fmt.Errorf(errMsg), "invalid value for parameter %q", k)
   158  		}
   159  		if err != nil {
   160  			return nil, errors.Wrapf(err, "invalid value for parameter %q", k)
   161  		}
   162  		mergedValues[k] = value
   163  	}
   164  	return mergedValues, nil
   165  }