2020-04-28 22:00:30 +00:00
|
|
|
package proxy
|
|
|
|
|
|
|
|
import (
|
2021-03-08 22:10:00 +00:00
|
|
|
"context"
|
2024-03-19 22:01:36 +00:00
|
|
|
"net"
|
2020-04-28 22:00:30 +00:00
|
|
|
sysnet "net"
|
|
|
|
"net/url"
|
|
|
|
"strconv"
|
|
|
|
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
|
2022-03-02 23:47:27 +00:00
|
|
|
"github.com/k3s-io/k3s/pkg/agent/loadbalancer"
|
2020-04-28 22:00:30 +00:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Proxy interface {
|
|
|
|
Update(addresses []string)
|
2024-03-19 22:01:36 +00:00
|
|
|
SetAPIServerPort(port int, isIPv6 bool) error
|
2022-02-16 22:19:58 +00:00
|
|
|
SetSupervisorDefault(address string)
|
2022-04-15 00:31:07 +00:00
|
|
|
IsSupervisorLBEnabled() bool
|
2020-04-28 22:00:30 +00:00
|
|
|
SupervisorURL() string
|
|
|
|
SupervisorAddresses() []string
|
|
|
|
APIServerURL() string
|
2021-02-12 15:35:57 +00:00
|
|
|
IsAPIServerLBEnabled() bool
|
2024-11-15 22:11:47 +00:00
|
|
|
SetHealthCheck(address string, healthCheck loadbalancer.HealthCheckFunc)
|
2020-04-28 22:00:30 +00:00
|
|
|
}
|
|
|
|
|
2021-03-06 10:29:57 +00:00
|
|
|
// NewSupervisorProxy sets up a new proxy for retrieving supervisor and apiserver addresses. If
|
|
|
|
// lbEnabled is true, a load-balancer is started on the requested port to connect to the supervisor
|
|
|
|
// address, and the address of this local load-balancer is returned instead of the actual supervisor
|
|
|
|
// and apiserver addresses.
|
|
|
|
// NOTE: This is a proxy in the API sense - it returns either actual server URLs, or the URL of the
|
|
|
|
// local load-balancer. It is not actually responsible for proxying requests at the network level;
|
|
|
|
// this is handled by the load-balancers that the proxy optionally steers connections towards.
|
2022-03-31 09:49:30 +00:00
|
|
|
func NewSupervisorProxy(ctx context.Context, lbEnabled bool, dataDir, supervisorURL string, lbServerPort int, isIPv6 bool) (Proxy, error) {
|
2021-02-12 15:35:57 +00:00
|
|
|
p := proxy{
|
2021-03-06 10:29:57 +00:00
|
|
|
lbEnabled: lbEnabled,
|
2020-04-28 22:00:30 +00:00
|
|
|
dataDir: dataDir,
|
|
|
|
initialSupervisorURL: supervisorURL,
|
|
|
|
supervisorURL: supervisorURL,
|
|
|
|
apiServerURL: supervisorURL,
|
2021-02-12 15:35:57 +00:00
|
|
|
lbServerPort: lbServerPort,
|
2024-03-19 22:01:36 +00:00
|
|
|
context: ctx,
|
2020-04-28 22:00:30 +00:00
|
|
|
}
|
|
|
|
|
2021-03-06 10:29:57 +00:00
|
|
|
if lbEnabled {
|
2024-01-11 20:26:47 +00:00
|
|
|
if err := loadbalancer.SetHTTPProxy(supervisorURL); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-03-31 09:49:30 +00:00
|
|
|
lb, err := loadbalancer.New(ctx, dataDir, loadbalancer.SupervisorServiceName, supervisorURL, p.lbServerPort, isIPv6)
|
2020-04-28 22:00:30 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
p.supervisorLB = lb
|
2024-11-15 22:11:47 +00:00
|
|
|
p.supervisorURL = lb.LocalURL()
|
2020-04-28 22:00:30 +00:00
|
|
|
p.apiServerURL = p.supervisorURL
|
|
|
|
}
|
|
|
|
|
|
|
|
u, err := url.Parse(p.initialSupervisorURL)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "failed to parse %s", p.initialSupervisorURL)
|
|
|
|
}
|
|
|
|
p.fallbackSupervisorAddress = u.Host
|
|
|
|
p.supervisorPort = u.Port()
|
|
|
|
|
2024-06-03 20:40:09 +00:00
|
|
|
logrus.Debugf("Supervisor proxy using supervisor=%s apiserver=%s lb=%v", p.supervisorURL, p.apiServerURL, p.lbEnabled)
|
2021-02-12 15:35:57 +00:00
|
|
|
return &p, nil
|
2020-04-28 22:00:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type proxy struct {
|
2021-03-06 10:29:57 +00:00
|
|
|
dataDir string
|
|
|
|
lbEnabled bool
|
|
|
|
lbServerPort int
|
|
|
|
apiServerEnabled bool
|
2020-04-28 22:00:30 +00:00
|
|
|
|
2021-03-06 10:29:57 +00:00
|
|
|
apiServerURL string
|
2024-03-19 22:01:36 +00:00
|
|
|
apiServerPort string
|
2020-04-28 22:00:30 +00:00
|
|
|
supervisorURL string
|
|
|
|
supervisorPort string
|
2021-03-06 10:29:57 +00:00
|
|
|
initialSupervisorURL string
|
2020-04-28 22:00:30 +00:00
|
|
|
fallbackSupervisorAddress string
|
|
|
|
supervisorAddresses []string
|
|
|
|
|
2021-03-06 10:29:57 +00:00
|
|
|
apiServerLB *loadbalancer.LoadBalancer
|
|
|
|
supervisorLB *loadbalancer.LoadBalancer
|
2024-03-19 22:01:36 +00:00
|
|
|
context context.Context
|
2020-04-28 22:00:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *proxy) Update(addresses []string) {
|
|
|
|
apiServerAddresses := addresses
|
|
|
|
supervisorAddresses := addresses
|
|
|
|
|
|
|
|
if p.apiServerEnabled {
|
|
|
|
supervisorAddresses = p.setSupervisorPort(supervisorAddresses)
|
|
|
|
}
|
|
|
|
if p.apiServerLB != nil {
|
|
|
|
p.apiServerLB.Update(apiServerAddresses)
|
|
|
|
}
|
|
|
|
if p.supervisorLB != nil {
|
|
|
|
p.supervisorLB.Update(supervisorAddresses)
|
|
|
|
}
|
|
|
|
p.supervisorAddresses = supervisorAddresses
|
|
|
|
}
|
|
|
|
|
2024-11-15 22:11:47 +00:00
|
|
|
func (p *proxy) SetHealthCheck(address string, healthCheck loadbalancer.HealthCheckFunc) {
|
2024-03-19 22:01:36 +00:00
|
|
|
if p.supervisorLB != nil {
|
|
|
|
p.supervisorLB.SetHealthCheck(address, healthCheck)
|
|
|
|
}
|
|
|
|
|
|
|
|
if p.apiServerLB != nil {
|
|
|
|
host, _, _ := net.SplitHostPort(address)
|
|
|
|
address = net.JoinHostPort(host, p.apiServerPort)
|
|
|
|
p.apiServerLB.SetHealthCheck(address, healthCheck)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-28 22:00:30 +00:00
|
|
|
func (p *proxy) setSupervisorPort(addresses []string) []string {
|
|
|
|
var newAddresses []string
|
|
|
|
for _, address := range addresses {
|
|
|
|
h, _, err := sysnet.SplitHostPort(address)
|
|
|
|
if err != nil {
|
2020-09-21 16:56:03 +00:00
|
|
|
logrus.Errorf("Failed to parse address %s, dropping: %v", address, err)
|
2020-04-28 22:00:30 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
newAddresses = append(newAddresses, sysnet.JoinHostPort(h, p.supervisorPort))
|
|
|
|
}
|
|
|
|
return newAddresses
|
|
|
|
}
|
|
|
|
|
2021-03-06 10:29:57 +00:00
|
|
|
// SetAPIServerPort configures the proxy to return a different set of addresses for the apiserver,
|
|
|
|
// for use in cases where the apiserver is not running on the same port as the supervisor. If
|
|
|
|
// load-balancing is enabled, another load-balancer is started on a port one below the supervisor
|
|
|
|
// load-balancer, and the address of this load-balancer is returned instead of the actual apiserver
|
|
|
|
// addresses.
|
2024-03-19 22:01:36 +00:00
|
|
|
func (p *proxy) SetAPIServerPort(port int, isIPv6 bool) error {
|
2024-06-03 20:40:09 +00:00
|
|
|
if p.apiServerEnabled {
|
|
|
|
logrus.Debugf("Supervisor proxy apiserver port already set")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-04-28 22:00:30 +00:00
|
|
|
u, err := url.Parse(p.initialSupervisorURL)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "failed to parse server URL %s", p.initialSupervisorURL)
|
|
|
|
}
|
2024-03-19 22:01:36 +00:00
|
|
|
p.apiServerPort = strconv.Itoa(port)
|
|
|
|
u.Host = sysnet.JoinHostPort(u.Hostname(), p.apiServerPort)
|
2020-04-28 22:00:30 +00:00
|
|
|
|
2021-03-08 23:47:14 +00:00
|
|
|
if p.lbEnabled && p.apiServerLB == nil {
|
2021-02-12 15:35:57 +00:00
|
|
|
lbServerPort := p.lbServerPort
|
|
|
|
if lbServerPort != 0 {
|
2021-03-06 10:29:57 +00:00
|
|
|
lbServerPort = lbServerPort - 1
|
2021-02-12 15:35:57 +00:00
|
|
|
}
|
2024-06-03 20:40:09 +00:00
|
|
|
lb, err := loadbalancer.New(p.context, p.dataDir, loadbalancer.APIServerServiceName, u.String(), lbServerPort, isIPv6)
|
2020-04-28 22:00:30 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
p.apiServerLB = lb
|
2024-11-15 22:11:47 +00:00
|
|
|
p.apiServerURL = lb.LocalURL()
|
2024-06-03 20:40:09 +00:00
|
|
|
} else {
|
|
|
|
p.apiServerURL = u.String()
|
2020-04-28 22:00:30 +00:00
|
|
|
}
|
|
|
|
|
2024-06-03 20:40:09 +00:00
|
|
|
logrus.Debugf("Supervisor proxy apiserver port changed; apiserver=%s lb=%v", p.apiServerURL, p.lbEnabled)
|
|
|
|
p.apiServerEnabled = true
|
2020-04-28 22:00:30 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-02-16 22:19:58 +00:00
|
|
|
// SetSupervisorDefault updates the default (fallback) address for the connection to the
|
|
|
|
// supervisor. This is most useful on k3s nodes without apiservers, where the local
|
|
|
|
// supervisor must be used to bootstrap the agent config, but then switched over to
|
|
|
|
// another node running an apiserver once one is available.
|
|
|
|
func (p *proxy) SetSupervisorDefault(address string) {
|
|
|
|
host, port, err := sysnet.SplitHostPort(address)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Errorf("Failed to parse address %s, dropping: %v", address, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if p.apiServerEnabled {
|
|
|
|
port = p.supervisorPort
|
|
|
|
address = sysnet.JoinHostPort(host, port)
|
|
|
|
}
|
|
|
|
p.fallbackSupervisorAddress = address
|
|
|
|
if p.supervisorLB == nil {
|
|
|
|
p.supervisorURL = "https://" + address
|
|
|
|
} else {
|
|
|
|
p.supervisorLB.SetDefault(address)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-15 00:31:07 +00:00
|
|
|
func (p *proxy) IsSupervisorLBEnabled() bool {
|
|
|
|
return p.supervisorLB != nil
|
|
|
|
}
|
|
|
|
|
2020-04-28 22:00:30 +00:00
|
|
|
func (p *proxy) SupervisorURL() string {
|
|
|
|
return p.supervisorURL
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *proxy) SupervisorAddresses() []string {
|
|
|
|
if len(p.supervisorAddresses) > 0 {
|
|
|
|
return p.supervisorAddresses
|
|
|
|
}
|
|
|
|
return []string{p.fallbackSupervisorAddress}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *proxy) APIServerURL() string {
|
|
|
|
return p.apiServerURL
|
|
|
|
}
|
2021-02-12 15:35:57 +00:00
|
|
|
|
|
|
|
func (p *proxy) IsAPIServerLBEnabled() bool {
|
|
|
|
return p.apiServerLB != nil
|
|
|
|
}
|