github.com/caos/orbos@v1.5.14-0.20221103111702-e6cd0cea7ad4/internal/operator/orbiter/kinds/loadbalancers/dynamic/desired.go (about)

     1  //go:generate goderive .
     2  
     3  package dynamic
     4  
     5  import (
     6  	"errors"
     7  	"fmt"
     8  
     9  	"gopkg.in/yaml.v3"
    10  
    11  	"github.com/caos/orbos/internal/operator/orbiter"
    12  	"github.com/caos/orbos/mntr"
    13  	"github.com/caos/orbos/pkg/tree"
    14  )
    15  
    16  type Desired struct {
    17  	Common *tree.Common `yaml:",inline"`
    18  	Spec   map[string][]*VIP
    19  }
    20  
    21  func (d *Desired) UnmarshalYAML(node *yaml.Node) (err error) {
    22  	defer func() {
    23  		err = mntr.ToUserError(err)
    24  		d.Common.OverwriteVersion("v2")
    25  	}()
    26  	switch d.Common.Version() {
    27  	case "v2":
    28  		type latest Desired
    29  		l := latest{}
    30  		if err := node.Decode(&l); err != nil {
    31  			return err
    32  		}
    33  		d.Spec = l.Spec
    34  		return nil
    35  	case "v1":
    36  		v1 := &DesiredV1{}
    37  		if err := node.Decode(v1); err != nil {
    38  			return err
    39  		}
    40  
    41  		d.Spec = v1tov2(v1).Spec
    42  		return nil
    43  	case "v0":
    44  		v0 := &DesiredV0{}
    45  		if err := node.Decode(v0); err != nil {
    46  			return err
    47  		}
    48  
    49  		d.Spec = v1tov2(v0tov1(v0)).Spec
    50  		return nil
    51  	}
    52  	return fmt.Errorf("version %s for kind %s is not supported", d.Common.Version, d.Common.Kind)
    53  }
    54  
    55  func (d *Desired) Validate() (err error) {
    56  
    57  	defer func() {
    58  		err = mntr.ToUserError(err)
    59  	}()
    60  
    61  	ips := make([]string, 0)
    62  
    63  	for pool, vips := range d.Spec {
    64  		if len(vips) == 0 {
    65  			return fmt.Errorf("pool %s has no virtual ip configured", pool)
    66  		}
    67  		for _, vip := range vips {
    68  			if err := vip.validate(); err != nil {
    69  				return fmt.Errorf("configuring vip for pool %s failed: %w", pool, err)
    70  			}
    71  			if vip != nil && vip.IP != "" {
    72  				ips = append(ips, vip.IP)
    73  			}
    74  		}
    75  	}
    76  
    77  	if len(deriveUnique(ips)) != len(ips) {
    78  		return errors.New("duplicate ips configured")
    79  	}
    80  
    81  	return nil
    82  }
    83  
    84  type VIP struct {
    85  	IP        string `yaml:",omitempty"`
    86  	Transport []*Transport
    87  }
    88  
    89  func (v *VIP) validate() (err error) {
    90  
    91  	defer func() {
    92  		err = mntr.ToUserError(err)
    93  	}()
    94  
    95  	if len(v.Transport) == 0 {
    96  		return fmt.Errorf("vip %s has no transport configured", v.IP)
    97  	}
    98  
    99  	for _, source := range v.Transport {
   100  		if err := source.validate(); err != nil {
   101  			return fmt.Errorf("configuring sources for vip %s failed: %w", v.IP, err)
   102  		}
   103  	}
   104  
   105  	return nil
   106  }
   107  
   108  type HealthChecks struct {
   109  	Protocol string
   110  	Path     string
   111  	Code     uint16
   112  }
   113  
   114  func (h *HealthChecks) validate() error {
   115  	if h.Protocol == "" {
   116  		return mntr.ToUserError(errors.New("no protocol configured"))
   117  	}
   118  	return nil
   119  }
   120  
   121  func (s *Transport) validate() (err error) {
   122  
   123  	defer func() {
   124  		if err != nil {
   125  			mntr.ToUserError(fmt.Errorf("source %s is invalid: %w", s.Name, err))
   126  		}
   127  	}()
   128  
   129  	if s.Name == "" {
   130  		return fmt.Errorf("source with port %d has no name", s.FrontendPort)
   131  	}
   132  
   133  	if err := s.FrontendPort.validate(); err != nil {
   134  		return fmt.Errorf("configuring frontend port failed: %w", err)
   135  	}
   136  
   137  	if err := s.BackendPort.validate(); err != nil {
   138  		return fmt.Errorf("configuring backend port failed: %w", err)
   139  	}
   140  
   141  	if s.FrontendPort == s.BackendPort {
   142  		return errors.New("frontend port and backend port must not be equal")
   143  	}
   144  
   145  	for _, cidr := range s.Whitelist {
   146  		if err := cidr.Validate(); err != nil {
   147  			return err
   148  		}
   149  	}
   150  
   151  	if len(s.BackendPools) < 1 {
   152  		return errors.New("at least one target pool is needed")
   153  	}
   154  
   155  	if err := s.HealthChecks.validate(); err != nil {
   156  		return fmt.Errorf("configuring health checks failed: %w", err)
   157  	}
   158  
   159  	return nil
   160  }
   161  
   162  type Transport struct {
   163  	Name         string
   164  	FrontendPort Port
   165  	BackendPort  Port
   166  	BackendPools []string
   167  	Whitelist    []*orbiter.CIDR
   168  	//	DownstreamProxies []*orbiter.IPAddress
   169  	HealthChecks  HealthChecks
   170  	ProxyProtocol *bool
   171  }
   172  
   173  type Port uint16
   174  
   175  func (p Port) validate() error {
   176  	if p == 0 {
   177  		return mntr.ToUserError(fmt.Errorf("port %d is not allowed", p))
   178  	}
   179  	return nil
   180  }