...

Source file src/edge-infra.dev/pkg/edge/api/services/channels/validate.go

Documentation: edge-infra.dev/pkg/edge/api/services/channels

     1  package channels
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"time"
     7  
     8  	"github.com/google/uuid"
     9  )
    10  
    11  const (
    12  	maxDatasyncOutageWindow     = 3 * 24 * time.Hour
    13  	MinExpireBufferDuration     = maxDatasyncOutageWindow
    14  	MinRotationIntervalDuration = maxDatasyncOutageWindow
    15  
    16  	// BearerTokenSecretSuffix is appended to channel names to create the secret that helm workloads mount.
    17  	BearerTokenSecretSuffix = "-encryption-token"
    18  
    19  	// validChannelNameLength ensures that the BearerTokenSecretSuffix can be appended to the channel name,
    20  	// without exceeding the 63 character maximum length for k8s for names.
    21  	validChannelNameLength = 63 - len(BearerTokenSecretSuffix)
    22  )
    23  
    24  var (
    25  	// The channel name must conform to k8s naming rules.
    26  	reValidChannelName = regexp.MustCompile("^[a-z]([-]?[a-z0-9])+$")
    27  
    28  	// reValidSecretManagerLink verifies the following format: "projects/${gcp_project_name}/secrets/${secret_manager_secret_resource_id}"
    29  	reValidSecretManagerLink = regexp.MustCompile("^projects/[a-z]([-]?[a-z0-9])+/secrets/[a-z]([-]?[a-z0-9])+$")
    30  )
    31  
    32  // Tiny sanity check in lieu of a dedicated test.
    33  func init() {
    34  	if secondStr != fmt.Sprintf("%d", time.Second) {
    35  		panic("secondStr does not equal time.Second")
    36  	}
    37  }
    38  
    39  /*
    40  	Channel validation
    41  */
    42  
    43  func (c *Channel) validateRequiredFields() error {
    44  	switch {
    45  	case c.Name == "":
    46  		return fmt.Errorf("missing Name")
    47  	case len(c.Name) > validChannelNameLength:
    48  		return fmt.Errorf("Name length must be less than %d characters", validChannelNameLength)
    49  	case !reValidChannelName.MatchString(c.Name):
    50  		return fmt.Errorf("invalid Name")
    51  	case c.Team == "":
    52  		return fmt.Errorf("missing Team")
    53  	case c.Description == "":
    54  		return fmt.Errorf("missing Description")
    55  	}
    56  	return nil
    57  }
    58  
    59  func (c *Channel) validateCreate() error {
    60  	// only set the optional fields when validating for creation.
    61  	c.setDefaults()
    62  
    63  	switch {
    64  	case c.ID != uuid.Nil:
    65  		return fmt.Errorf("ID is readonly")
    66  	case !c.CreatedAt.IsZero():
    67  		return fmt.Errorf("CreatedAt is readonly")
    68  	case c.ExpireBufferDuration < MinExpireBufferDuration:
    69  		return fmt.Errorf("ExpireBufferDuration must be greater than or equal to %v", MinExpireBufferDuration)
    70  	case c.RotationIntervalDuration < MinRotationIntervalDuration:
    71  		return fmt.Errorf("RotationIntervalDuration must be greater than or equal to %v", MinRotationIntervalDuration)
    72  	}
    73  	return c.validateRequiredFields()
    74  }
    75  
    76  func (c *Channel) validateScan() error {
    77  	// MinExpireBufferDuration and MinRotationIntervalDuration are only checked during Channel creation.
    78  	// These values can change, so they shouldn't be checked when Channels are scanned.
    79  	switch {
    80  	case c.ID == uuid.Nil:
    81  		return fmt.Errorf("missing ID")
    82  	case c.ExpireBufferDuration == 0:
    83  		return fmt.Errorf("missing ExpireBufferDuration")
    84  	case c.RotationIntervalDuration == 0:
    85  		return fmt.Errorf("missing RotationIntervalDuration")
    86  	case c.CreatedAt.IsZero():
    87  		return fmt.Errorf("missing CreatedAt")
    88  	}
    89  	return c.validateRequiredFields()
    90  }
    91  
    92  /*
    93  	ChannelUpdateRequest validation
    94  */
    95  
    96  func (r *ChannelUpdateRequest) validate() error {
    97  	if r.Team == nil && r.Description == nil && r.ExpireBufferDuration == nil && r.RotationIntervalDuration == nil {
    98  		return fmt.Errorf("nothing to update")
    99  	}
   100  
   101  	switch {
   102  	case r.Team != nil && *r.Team == "":
   103  		return fmt.Errorf("team is an empty string")
   104  	case r.Description != nil && *r.Description == "":
   105  		return fmt.Errorf("description is an empty string")
   106  	case r.ExpireBufferDuration != nil && *r.ExpireBufferDuration < MinExpireBufferDuration:
   107  		return fmt.Errorf("expire buffer duration must be greater than or equal to %v", MinExpireBufferDuration)
   108  	case r.RotationIntervalDuration != nil && *r.RotationIntervalDuration < MinRotationIntervalDuration:
   109  		return fmt.Errorf("rotation interval duration must be greater than or equal to %v", MinRotationIntervalDuration)
   110  	}
   111  	return nil
   112  }
   113  
   114  /*
   115  	ChannelKeyVersion validation
   116  */
   117  
   118  func (ckv *ChannelKeyVersion) validateRequiredFields() error {
   119  	switch {
   120  	case ckv.ChannelID == uuid.Nil:
   121  		return fmt.Errorf("missing ChannelID")
   122  	case ckv.BannerEdgeID == uuid.Nil:
   123  		return fmt.Errorf("missing BannerEdgeId")
   124  	case ckv.Version < 1:
   125  		return fmt.Errorf("Version must be greater than 0")
   126  	case ckv.SecretManagerLink == "":
   127  		return fmt.Errorf("missing SecretManagerLink")
   128  	case !reValidSecretManagerLink.MatchString(ckv.SecretManagerLink):
   129  		return fmt.Errorf("invalid SecretManagerLink format")
   130  	}
   131  	return nil
   132  }
   133  
   134  func (ckv *ChannelKeyVersion) validateCreate() error {
   135  	switch {
   136  	case ckv.ID != uuid.Nil:
   137  		return fmt.Errorf("ID is readonly")
   138  	case !ckv.CreatedAt.IsZero():
   139  		return fmt.Errorf("CreatedAt is readonly")
   140  	case !ckv.ExpireAt.IsZero():
   141  		return fmt.Errorf("ExpireAt is readonly")
   142  	case !ckv.RotateAt.IsZero():
   143  		return fmt.Errorf("RotateAt is readonly")
   144  	}
   145  	return ckv.validateRequiredFields()
   146  }
   147  
   148  func (ckv *ChannelKeyVersion) validateScan() error {
   149  	// The time checks ensure the query's `*_at` columns were scanned in the correct order.
   150  	// RotateAt can be zero.
   151  	switch {
   152  	case ckv.ID == uuid.Nil:
   153  		return fmt.Errorf("missing ID")
   154  	case ckv.CreatedAt.IsZero():
   155  		return fmt.Errorf("missing CreatedAt")
   156  	case ckv.ExpireAt.IsZero():
   157  		return fmt.Errorf("missing ExpireAt")
   158  	case ckv.CreatedAt.After(ckv.ExpireAt):
   159  		return fmt.Errorf("CreatedAt is greater than ExpireAt")
   160  	case ckv.RotateAt.After(ckv.ExpireAt):
   161  		return fmt.Errorf("RotateAt is greater than ExpireAt")
   162  	}
   163  	return ckv.validateRequiredFields()
   164  }
   165  
   166  /*
   167  	ParsedHelmConfigChannels
   168  */
   169  
   170  func (config *ParsedHelmConfigChannels) validateParsed() error {
   171  	for _, name := range config.Names() {
   172  		switch {
   173  		case name == "":
   174  			return fmt.Errorf("empty channel Name")
   175  		case len(name) > validChannelNameLength:
   176  			return fmt.Errorf("invalid channel Name with length greater than %d characters", validChannelNameLength)
   177  		case !reValidChannelName.MatchString(name):
   178  			return fmt.Errorf("invalid channel Name: %q", name)
   179  		}
   180  	}
   181  	return nil
   182  }
   183  

View as plain text