get.porter.sh/porter@v1.3.0/pkg/cnab/parameter_sources.go (about)

     1  package cnab
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  )
     8  
     9  const (
    10  	// ParameterSourcesExtensionShortHand is the short suffix of the ParameterSourcesExtensionKey.
    11  	ParameterSourcesExtensionShortHand = "parameter-sources"
    12  
    13  	// ParameterSourcesExtensionKey represents the full key for the Parameter Sources Extension.
    14  	ParameterSourcesExtensionKey = OfficialExtensionsPrefix + ParameterSourcesExtensionShortHand
    15  
    16  	// ParameterSourcesExtensionSchema represents the schema for the Docker Extension.
    17  	ParameterSourcesSchema = "https://cnab.io/v1/parameter-sources.schema.json"
    18  	// ParameterSourceTypeOutput defines a type of parameter source that is provided by a bundle output.
    19  	ParameterSourceTypeOutput = "output"
    20  	// ParameterSourceTypeDependencyOutput defines a type of parameter source that is provided by a bundle's dependency
    21  	// output.
    22  	ParameterSourceTypeDependencyOutput = "dependencies.output"
    23  )
    24  
    25  // ParameterSourcesExtension represents a required extension that specifies how
    26  // to default parameter values.
    27  var ParameterSourcesExtension = RequiredExtension{
    28  	Shorthand: ParameterSourcesExtensionShortHand,
    29  	Key:       ParameterSourcesExtensionKey,
    30  	Schema:    ParameterSourcesSchema,
    31  	Reader:    ParameterSourcesReader,
    32  }
    33  
    34  // ParameterSources describes the set of custom extension metadata associated
    35  // with the Parameter Sources extension
    36  type ParameterSources map[string]ParameterSource
    37  
    38  // SetParameterFromOutput creates an entry in the parameter sources section setting
    39  // the parameter's value using the specified output's value.
    40  func (ps *ParameterSources) SetParameterFromOutput(parameter string, output string) {
    41  	if *ps == nil {
    42  		*ps = ParameterSources{}
    43  	}
    44  
    45  	(*ps)[parameter] = ParameterSource{
    46  		Priority: []string{ParameterSourceTypeOutput},
    47  		Sources: ParameterSourceMap{
    48  			ParameterSourceTypeOutput: OutputParameterSource{OutputName: output},
    49  		},
    50  	}
    51  }
    52  
    53  // SetParameterFromDependencyOutput creates an entry in the parameter sources section setting
    54  // the parameter's value using the specified dependency's output value.
    55  func (ps *ParameterSources) SetParameterFromDependencyOutput(parameter string, dep string, output string) {
    56  	if *ps == nil {
    57  		*ps = ParameterSources{}
    58  	}
    59  
    60  	(*ps)[parameter] = ParameterSource{
    61  		Priority: []string{ParameterSourceTypeDependencyOutput},
    62  		Sources: ParameterSourceMap{
    63  			ParameterSourceTypeDependencyOutput: DependencyOutputParameterSource{
    64  				Dependency: dep,
    65  				OutputName: output},
    66  		},
    67  	}
    68  }
    69  
    70  type ParameterSource struct {
    71  	// Priority is an array of source types in the priority order that they should be used to
    72  	// populated the parameter.
    73  	Priority []string `json:"priority" mapstructure:"priority"`
    74  
    75  	// Sources is a map of key/value pairs of a source type and definition for
    76  	// the parameter value.
    77  	Sources ParameterSourceMap `json:"sources" mapstructure:"sources"`
    78  }
    79  
    80  // ListSourcesByPriority returns the parameter sources by the requested priority,
    81  // if none is specified, they are unsorted.
    82  func (s ParameterSource) ListSourcesByPriority() []ParameterSourceDefinition {
    83  	sources := make([]ParameterSourceDefinition, 0, len(s.Sources))
    84  	if len(s.Priority) == 0 {
    85  		for _, source := range s.Sources {
    86  			sources = append(sources, source)
    87  		}
    88  	} else {
    89  		for _, sourceType := range s.Priority {
    90  			sources = append(sources, s.Sources[sourceType])
    91  		}
    92  	}
    93  	return sources
    94  }
    95  
    96  type ParameterSourceMap map[string]ParameterSourceDefinition
    97  
    98  func (m *ParameterSourceMap) UnmarshalJSON(data []byte) error {
    99  	if *m == nil {
   100  		*m = ParameterSourceMap{}
   101  	}
   102  
   103  	var rawMap map[string]interface{}
   104  	err := json.Unmarshal(data, &rawMap)
   105  	if err != nil {
   106  		return err
   107  	}
   108  
   109  	for sourceKey, sourceDef := range rawMap {
   110  		rawDef, err := json.Marshal(sourceDef)
   111  		if err != nil {
   112  			return fmt.Errorf("error re-marshaling parameter source definition: %w", err)
   113  		}
   114  
   115  		switch sourceKey {
   116  		case ParameterSourceTypeOutput:
   117  			var output OutputParameterSource
   118  			err := json.Unmarshal(rawDef, &output)
   119  			if err != nil {
   120  				return fmt.Errorf("invalid parameter source definition for key %s: %w", sourceKey, err)
   121  			}
   122  			(*m)[ParameterSourceTypeOutput] = output
   123  		case ParameterSourceTypeDependencyOutput:
   124  			var depOutput DependencyOutputParameterSource
   125  			err := json.Unmarshal(rawDef, &depOutput)
   126  			if err != nil {
   127  				return fmt.Errorf("invalid parameter source definition for key %s: %w", sourceKey, err)
   128  			}
   129  			(*m)[ParameterSourceTypeDependencyOutput] = depOutput
   130  		default:
   131  			return fmt.Errorf("unsupported parameter source key %s", sourceKey)
   132  		}
   133  	}
   134  
   135  	return nil
   136  }
   137  
   138  type ParameterSourceDefinition interface {
   139  }
   140  
   141  // OutputParameterSource represents a parameter that is set using the value from
   142  // a bundle output.
   143  type OutputParameterSource struct {
   144  	OutputName string `json:"name" mapstructure:"name"`
   145  }
   146  
   147  // DependencyOutputParameterSource represents a parameter that is set using the value
   148  // from a bundle's dependency output.
   149  type DependencyOutputParameterSource struct {
   150  	Dependency string `json:"dependency" mapstructure:"dependency"`
   151  	OutputName string `json:"name" mapstructure:"name"`
   152  }
   153  
   154  // ReadParameterSources is a convenience method for returning a bonafide
   155  // ParameterSources reference after reading from the applicable section from
   156  // the provided bundle
   157  func (b ExtendedBundle) ReadParameterSources() (ParameterSources, error) {
   158  	raw, err := b.ParameterSourcesReader()
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  
   163  	ps, ok := raw.(ParameterSources)
   164  	if !ok {
   165  		return nil, errors.New("unable to read parameter sources extension data")
   166  	}
   167  
   168  	return ps, nil
   169  }
   170  
   171  // ParameterSourcesReader is a Reader for the ParameterSourcesExtension,
   172  // which reads from the applicable section in the provided bundle and
   173  // returns the raw data in the form of an interface
   174  func ParameterSourcesReader(bun ExtendedBundle) (interface{}, error) {
   175  	return bun.ParameterSourcesReader()
   176  }
   177  
   178  // ParameterSourcesReader is a Reader for the ParameterSourcesExtension,
   179  // which reads from the applicable section in the provided bundle and
   180  // returns the raw data in the form of an interface
   181  func (b ExtendedBundle) ParameterSourcesReader() (interface{}, error) {
   182  	data, ok := b.Custom[ParameterSourcesExtensionKey]
   183  	if !ok {
   184  		return nil, errors.New("attempted to read parameter sources from bundle but none are defined")
   185  	}
   186  
   187  	dataB, err := json.Marshal(data)
   188  	if err != nil {
   189  		return nil, fmt.Errorf("could not marshal the untyped %q extension data %q: %w",
   190  			ParameterSourcesExtensionKey, string(dataB), err)
   191  	}
   192  
   193  	ps := ParameterSources{}
   194  	err = json.Unmarshal(dataB, &ps)
   195  	if err != nil {
   196  		return nil, fmt.Errorf("could not unmarshal the %q extension %q: %w",
   197  			ParameterSourcesExtensionKey, string(dataB), err)
   198  	}
   199  
   200  	return ps, nil
   201  }
   202  
   203  // SupportsParameterSources checks if the bundle supports parameter sources.
   204  func (b ExtendedBundle) SupportsParameterSources() bool {
   205  	return b.SupportsExtension(ParameterSourcesExtensionKey)
   206  }
   207  
   208  // GetParameterSources checks if the parameter sources extension is present and returns its
   209  // extension configuration.
   210  func (e ProcessedExtensions) GetParameterSources() (ParameterSources, bool, error) {
   211  	rawExt, required := e[ParameterSourcesExtensionKey]
   212  
   213  	ext, ok := rawExt.(ParameterSources)
   214  	if !ok && required {
   215  		return ParameterSources{}, required, fmt.Errorf("unable to parse Parameter Sources extension config: %+v", rawExt)
   216  	}
   217  
   218  	return ext, required, nil
   219  }
   220  
   221  // HasParameterSources returns whether or not the bundle has parameter sources defined.
   222  func (b ExtendedBundle) HasParameterSources() bool {
   223  	_, ok := b.Custom[ParameterSourcesExtensionKey]
   224  	return ok
   225  }
   226  
   227  // ParameterHasSource determines if the specified parameter has a parameter
   228  // source defined.
   229  func (b ExtendedBundle) ParameterHasSource(paramName string) bool {
   230  	sources, err := b.ReadParameterSources()
   231  	if err != nil {
   232  		return false
   233  	}
   234  	_, hasSource := sources[paramName]
   235  	return hasSource
   236  }