...

Source file src/edge-infra.dev/pkg/f8n/warehouse/cluster/providers.go

Documentation: edge-infra.dev/pkg/f8n/warehouse/cluster

     1  package cluster
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sort"
     7  	"strings"
     8  
     9  	wh "edge-infra.dev/pkg/f8n/warehouse"
    10  	"edge-infra.dev/pkg/f8n/warehouse/oci"
    11  )
    12  
    13  // Public errors
    14  var (
    15  	// ErrNoProviders occurs when no providers can be parsed from the value
    16  	// of AnnotationProviders or an empty set of cluster providers are passed
    17  	// to a packaging function.
    18  	ErrNoProviders = errors.New("at least one cluster provider is required")
    19  	// ErrUnsupportedProvider occurs when an invalid set of providers are passed
    20  	// to a packaging function or parsed from the value of AnnotationProviders.
    21  	ErrUnsupportedProvider = errors.New("provider is not supported by artifact")
    22  )
    23  
    24  // Provider represents a K8s cluster provider, e.g., GKE.
    25  type (
    26  	Provider  string
    27  	Providers []Provider
    28  )
    29  
    30  // Built-in cluster providers
    31  const (
    32  	Generic Provider = "generic"
    33  	GKE     Provider = "gke"
    34  )
    35  
    36  // BuiltInProviders returns the cluster providers that Warehouse understands
    37  // out-of-the-box. User-provided values are merged with these.
    38  func BuiltInProviders() Providers {
    39  	return []Provider{GKE, Generic}
    40  }
    41  
    42  // String is a convenience function for casting to a string
    43  func (p Provider) String() string {
    44  	return string(p)
    45  }
    46  
    47  // Set implements flag.Value.
    48  func (p *Provider) Set(v string) error {
    49  	np := Provider(v)
    50  	*p = np
    51  	return nil
    52  }
    53  
    54  // Set implements [flag.Getter]
    55  func (p *Provider) Get() any {
    56  	return p
    57  }
    58  
    59  // Type implements [edge-infra.dev/pkg/lib/cli/rags.TypedValue]
    60  func (p *Provider) Type() string {
    61  	return "provider(s)"
    62  }
    63  
    64  // IsValid returns an error if the input string is not one of the configured
    65  // providers or the Providers is empty. Empty strings are not checked.
    66  func (p Providers) IsValid(provider Provider) error {
    67  	if len(p) == 0 {
    68  		return ErrNoProviders
    69  	}
    70  	if provider == "" {
    71  		return nil
    72  	}
    73  	for _, p := range p {
    74  		if p == provider {
    75  			return nil
    76  		}
    77  	}
    78  	return fmt.Errorf(
    79  		"invalid cluster provider %s: expected one of %s", provider, p,
    80  	)
    81  }
    82  
    83  // OCIAnnotations converts the set of cluster providers to the OCI annotation
    84  // added to Pallet artifacts.
    85  //
    86  // Note: Callers are expected to validate the Providers.
    87  func (p Providers) OCIAnnotations() map[string]string {
    88  	return map[string]string{wh.AnnotationClusterProviders: p.String()}
    89  }
    90  
    91  func (p Providers) StrArray() []string {
    92  	s := make([]string, len(p))
    93  	for i, provider := range p {
    94  		s[i] = string(provider)
    95  	}
    96  	sort.Strings(s)
    97  	return s
    98  }
    99  
   100  func (p Providers) String() string {
   101  	return strings.Join(p.StrArray(), ",")
   102  }
   103  
   104  // Set implements flag.Value. It accepts comma separated strings and accumulates
   105  // values from repeated occurrences of the flag. It also handles edge cases like
   106  // trailing and leading commas.
   107  func (p *Providers) Set(v string) error {
   108  	v = strings.TrimSuffix(v, ",")
   109  	for _, provider := range strings.Split(strings.Trim(v, ","), ",") {
   110  		*p = append(*p, Provider(provider))
   111  	}
   112  	return nil
   113  }
   114  
   115  func (p *Providers) Get() any {
   116  	return p
   117  }
   118  
   119  // Assert that Capabilities implements the sort interface
   120  var _ sort.Interface = Providers{}
   121  
   122  func (p Providers) Len() int           { return len(p) }
   123  func (p Providers) Less(i, j int) bool { return p[i] < p[j] }
   124  func (p Providers) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
   125  
   126  // ProvidersFromAnnotations parses the K8s cluster providers from an OCI annotation
   127  // map. It returns an error if the annotation is not present, is invalid
   128  // or is empty.
   129  func ProvidersFromAnnotations(a map[string]string) (Providers, error) {
   130  	str, ok := a[wh.AnnotationClusterProviders]
   131  	if !ok {
   132  		return nil, fmt.Errorf("%w: cluster providers annotation (%s) not present",
   133  			oci.ErrInvalidArtifact, wh.AnnotationClusterProviders)
   134  	}
   135  
   136  	providers := ProvidersFromString(str)
   137  
   138  	if len(providers) == 0 {
   139  		return nil, ErrNoProviders
   140  	}
   141  
   142  	return providers, nil
   143  }
   144  
   145  // ProvidersFromString parses K8s cluster providers from a comma-separated
   146  // string value.
   147  func ProvidersFromString(s string) Providers {
   148  	tokens := strings.Split(strings.Trim(s, ","), ",")
   149  	p := make([]Provider, 0, len(tokens))
   150  	for _, cap := range strings.Split(strings.Trim(s, ","), ",") {
   151  		p = append(p, Provider(cap))
   152  	}
   153  	return p
   154  }
   155  

View as plain text