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 }