get.porter.sh/porter@v1.3.0/pkg/storage/parameterset.go (about)

     1  package storage
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  	"time"
     8  
     9  	"get.porter.sh/porter/pkg/cnab"
    10  	"get.porter.sh/porter/pkg/schema"
    11  	"get.porter.sh/porter/pkg/secrets"
    12  	"get.porter.sh/porter/pkg/tracing"
    13  	"github.com/cnabio/cnab-go/bundle"
    14  	"go.opentelemetry.io/otel/attribute"
    15  )
    16  
    17  const (
    18  	INTERNAL_PARAMETERER_SET = "internal-parameter-set"
    19  )
    20  
    21  var _ Document = ParameterSet{}
    22  
    23  // ParameterSet represents a collection of parameters and their
    24  // sources/strategies for value resolution
    25  type ParameterSet struct {
    26  	ParameterSetSpec `yaml:",inline"`
    27  	Status           ParameterSetStatus `json:"status" yaml:"status" toml:"status"`
    28  }
    29  
    30  // ParameterSetSpec represents the set of user-modifiable fields on a ParameterSet.
    31  type ParameterSetSpec struct {
    32  	// SchemaType helps when we export the definition so editors can detect the type of document, it's not used by porter.
    33  	SchemaType string `json:"schemaType,omitempty" yaml:"schemaType,omitempty"`
    34  
    35  	// SchemaVersion is the version of the parameter-set schema.
    36  	SchemaVersion cnab.SchemaVersion `json:"schemaVersion" yaml:"schemaVersion" toml:"schemaVersion"`
    37  
    38  	// Namespace to which the credential set is scoped.
    39  	Namespace string `json:"namespace" yaml:"namespace" toml:"namespace"`
    40  
    41  	// Name is the name of the parameter set.
    42  	Name string `json:"name" yaml:"name" toml:"name"`
    43  
    44  	// Labels applied to the parameter set.
    45  	Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty" toml:"labels,omitempty"`
    46  
    47  	// Parameters is a list of parameter specs.
    48  	Parameters secrets.StrategyList `json:"parameters" yaml:"parameters" toml:"parameters"`
    49  }
    50  
    51  // ParameterSetStatus contains additional status metadata that has been set by Porter.
    52  type ParameterSetStatus struct {
    53  	// Created timestamp of the parameter set.
    54  	Created time.Time `json:"created" yaml:"created" toml:"created"`
    55  
    56  	// Modified timestamp of the parameter set.
    57  	Modified time.Time `json:"modified" yaml:"modified" toml:"modified"`
    58  }
    59  
    60  // NewParameterSet creates a new ParameterSet with the required fields initialized.
    61  func NewParameterSet(namespace string, name string, params ...secrets.SourceMap) ParameterSet {
    62  	now := time.Now()
    63  	ps := ParameterSet{
    64  		ParameterSetSpec: ParameterSetSpec{
    65  			SchemaType:    SchemaTypeParameterSet,
    66  			SchemaVersion: DefaultParameterSetSchemaVersion,
    67  			Namespace:     namespace,
    68  			Name:          name,
    69  			Parameters:    params,
    70  		},
    71  		Status: ParameterSetStatus{
    72  			Created:  now,
    73  			Modified: now,
    74  		},
    75  	}
    76  
    77  	return ps
    78  }
    79  
    80  // NewInternalParameterSet creates a new internal ParameterSet with the required fields initialized.
    81  func NewInternalParameterSet(namespace string, name string, params ...secrets.SourceMap) ParameterSet {
    82  	return NewParameterSet(namespace, INTERNAL_PARAMETERER_SET+"-"+name, params...)
    83  }
    84  
    85  func (s ParameterSet) DefaultDocumentFilter() map[string]interface{} {
    86  	return map[string]interface{}{"namespace": s.Namespace, "name": s.Name}
    87  }
    88  
    89  func (s *ParameterSet) Validate(ctx context.Context, strategy schema.CheckStrategy) error {
    90  	_, span := tracing.StartSpan(ctx,
    91  		attribute.String("parameterSet", s.String()),
    92  		attribute.String("schemaVersion", string(s.SchemaVersion)),
    93  		attribute.String("defaultSchemaVersion", string(DefaultParameterSetSchemaVersion)))
    94  	defer span.EndSpan()
    95  
    96  	// Before we can validate, get our resource in a consistent state
    97  	// 1. Check if we know what to do with this version of the resource
    98  	if warnOnly, err := schema.ValidateSchemaVersion(strategy, SupportedParameterSetSchemaVersions, string(s.SchemaVersion), DefaultParameterSetSemverSchemaVersion); err != nil {
    99  		if warnOnly {
   100  			span.Warn(err.Error())
   101  		} else {
   102  			return span.Error(err)
   103  		}
   104  	}
   105  
   106  	// 2. Check if they passed in the right resource type
   107  	if s.SchemaType != "" && !strings.EqualFold(s.SchemaType, SchemaTypeParameterSet) {
   108  		return span.Errorf("invalid schemaType %s, expected %s", s.SchemaType, SchemaTypeParameterSet)
   109  	}
   110  
   111  	// Default the schemaType before importing into the database if it's not set already
   112  	// SchemaType isn't really used by our code, it's a type hint for editors, but this will ensure we are consistent in our persisted documents
   113  	if s.SchemaType == "" {
   114  		s.SchemaType = SchemaTypeParameterSet
   115  	}
   116  
   117  	// OK! Now we can do resource specific validations
   118  	return nil
   119  }
   120  
   121  func (s ParameterSet) String() string {
   122  	return fmt.Sprintf("%s/%s", s.Namespace, s.Name)
   123  }
   124  
   125  // HasParameter determines if the specified parameter is defined in the set.
   126  func (s ParameterSet) HasParameter(name string) bool {
   127  	for _, param := range s.Parameters {
   128  		if param.Name == name {
   129  			return true
   130  		}
   131  	}
   132  
   133  	return false
   134  }
   135  
   136  // ValidateBundle compares the given parameters with the spec in the bundle.
   137  //
   138  // This will result in an error only when the following conditions are true:
   139  // - a parameter in the spec is not present in the given set
   140  // - the parameters is required
   141  // - the parameter applies to the specified action
   142  //
   143  // It is allowed for spec to specify both an env var and a file. In such case, if
   144  // the given set provides either, it will be considered valid.
   145  func (s ParameterSet) ValidateBundle(spec map[string]bundle.Parameter, action string) error {
   146  	for name, param := range spec {
   147  		if !param.AppliesTo(action) {
   148  			continue
   149  		}
   150  
   151  		if !s.HasParameter(name) && param.Required {
   152  			return fmt.Errorf(`parameter "%s" is required`, name)
   153  		}
   154  	}
   155  	return nil
   156  }
   157  
   158  // Keys returns the names of all the parameters in the set.
   159  func (s ParameterSet) Keys() []string {
   160  	keys := make([]string, len(s.Parameters))
   161  	for _, param := range s.Parameters {
   162  		keys = append(keys, param.Name)
   163  	}
   164  	return keys
   165  }