Philipp Sauter 6b23deddcf
feat: support custom ports for connecting to apid from talosctl
Users can now add a port suffix to the endpoints used by talosctl. Either
in the CLI flag or the ~/.talos/config. The default port is still 50000.

Signed-off-by: Philipp Sauter <philipp.sauter@siderolabs.com>
2022-08-11 16:52:46 +02:00

117 lines
2.8 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package resolver
import (
"math/rand"
"net"
"strconv"
"strings"
"google.golang.org/grpc/resolver"
"github.com/talos-systems/talos/pkg/machinery/generic/slices"
)
// RoundRobinResolverScheme is a scheme to use in grpc.Dial for the round-robin gRPC resolver.
// This resolver requires that all endpoints have a port appended.
// To ensure this, use EnsureEndpointsHavePorts before constructing a connection string.
const RoundRobinResolverScheme = "talosroundrobin"
func init() {
resolver.Register(&roundRobinResolverBuilder{
scheme: RoundRobinResolverScheme,
})
}
type roundRobinResolverBuilder struct {
scheme string
}
// Build implements resolver.Builder.
func (b *roundRobinResolverBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
r := &roundRobinResolver{
target: target,
cc: cc,
}
if err := r.start(); err != nil {
return nil, err
}
return r, nil
}
// Build implements resolver.Builder.
func (b *roundRobinResolverBuilder) Scheme() string {
return b.scheme
}
type roundRobinResolver struct {
target resolver.Target
cc resolver.ClientConn
}
// EnsureEndpointsHavePorts returns the list of endpoints with default port appended to those addresses that don't have a port.
func EnsureEndpointsHavePorts(endpoints []string, defaultPort int) []string {
return slices.Map(endpoints, func(endpoint string) string {
_, _, err := net.SplitHostPort(endpoint)
if err != nil {
return net.JoinHostPort(endpoint, strconv.Itoa(defaultPort))
}
return endpoint
})
}
func (r *roundRobinResolver) start() error {
var addrs []resolver.Address //nolint:prealloc
//nolint:staticcheck
endpoints := strings.Split(r.target.Endpoint, ",")
for _, addr := range endpoints {
serverName := addr
host, _, err := net.SplitHostPort(serverName)
if err == nil {
serverName = host
}
addrs = append(addrs, resolver.Address{
ServerName: serverName,
Addr: addr,
})
}
// shuffle the list in case client does just one request
rand.Shuffle(len(addrs), func(i, j int) {
addrs[i], addrs[j] = addrs[j], addrs[i]
})
serviceConfigJSON := `{
"loadBalancingConfig": [{
"round_robin": {}
}]
}`
parsedServiceConfig := r.cc.ParseServiceConfig(serviceConfigJSON)
if parsedServiceConfig.Err != nil {
return parsedServiceConfig.Err
}
return r.cc.UpdateState(resolver.State{
Addresses: addrs,
ServiceConfig: parsedServiceConfig,
})
}
// ResolveNow implements resolver.Resolver.
func (r *roundRobinResolver) ResolveNow(o resolver.ResolveNowOptions) {}
// ResolveNow implements resolver.Resolver.
func (r *roundRobinResolver) Close() {}