mirror of https://github.com/k3d-io/k3d
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
128 lines
4.9 KiB
128 lines
4.9 KiB
/*
|
|
Copyright © 2020-2023 The k3d Author(s)
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
*/
|
|
package client
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/docker/go-connections/nat"
|
|
"github.com/sirupsen/logrus"
|
|
"sigs.k8s.io/yaml"
|
|
|
|
"github.com/k3d-io/k3d/v5/pkg/config/types"
|
|
config "github.com/k3d-io/k3d/v5/pkg/config/v1alpha5"
|
|
l "github.com/k3d-io/k3d/v5/pkg/logger"
|
|
"github.com/k3d-io/k3d/v5/pkg/runtimes"
|
|
k3d "github.com/k3d-io/k3d/v5/pkg/types"
|
|
"github.com/k3d-io/k3d/v5/pkg/util"
|
|
)
|
|
|
|
var (
|
|
ErrNodeAddPortsExists error = errors.New("port exists on target")
|
|
)
|
|
|
|
func TransformPorts(ctx context.Context, runtime runtimes.Runtime, cluster *k3d.Cluster, portsWithNodeFilters []config.PortWithNodeFilters) error {
|
|
nodeCount := len(cluster.Nodes)
|
|
nodeList := cluster.Nodes
|
|
|
|
for _, portWithNodeFilters := range portsWithNodeFilters {
|
|
l.Log().Tracef("inspecting port mapping for %s with nodefilters %s", portWithNodeFilters.Port, portWithNodeFilters.NodeFilters)
|
|
if len(portWithNodeFilters.NodeFilters) == 0 && nodeCount > 1 {
|
|
l.Log().Infof("portmapping '%s' lacks a nodefilter, but there's more than one node: defaulting to %s", portWithNodeFilters.Port, types.DefaultTargetsNodefiltersPortMappings)
|
|
portWithNodeFilters.NodeFilters = types.DefaultTargetsNodefiltersPortMappings
|
|
}
|
|
|
|
for _, f := range portWithNodeFilters.NodeFilters {
|
|
if strings.HasPrefix(f, "loadbalancer") {
|
|
l.Log().Infof("portmapping '%s' targets the loadbalancer: defaulting to %s", portWithNodeFilters.Port, types.DefaultTargetsNodefiltersPortMappings)
|
|
portWithNodeFilters.NodeFilters = types.DefaultTargetsNodefiltersPortMappings
|
|
break
|
|
}
|
|
}
|
|
|
|
filteredNodes, err := util.FilterNodesWithSuffix(nodeList, portWithNodeFilters.NodeFilters, "proxy", "direct") // TODO: move "proxy" and "direct" allowed suffices to constants
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for suffix, nodes := range filteredNodes {
|
|
// skip, if no nodes in filtered set, so we don't add portmappings with no targets in the backend
|
|
if len(nodes) == 0 {
|
|
continue
|
|
}
|
|
portmappings, err := nat.ParsePortSpec(portWithNodeFilters.Port)
|
|
if err != nil {
|
|
return fmt.Errorf("error parsing port spec '%s': %+v", portWithNodeFilters.Port, err)
|
|
}
|
|
|
|
if suffix == "proxy" || suffix == util.NodeFilterSuffixNone { // proxy is the default suffix for port mappings
|
|
if cluster.ServerLoadBalancer == nil {
|
|
return fmt.Errorf("port-mapping of type 'proxy' specified, but loadbalancer is disabled")
|
|
}
|
|
if err := addPortMappings(cluster.ServerLoadBalancer.Node, portmappings); err != nil {
|
|
return err
|
|
}
|
|
for _, pm := range portmappings {
|
|
if err := loadbalancerAddPortConfigs(cluster.ServerLoadBalancer, pm, nodes); err != nil {
|
|
return fmt.Errorf("error adding port config to loadbalancer: %w", err)
|
|
}
|
|
}
|
|
} else if suffix == "direct" {
|
|
if len(nodes) > 1 {
|
|
return fmt.Errorf("error: cannot apply a direct port-mapping (%s) to more than one node", portmappings)
|
|
}
|
|
for _, node := range nodes {
|
|
if err := addPortMappings(node, portmappings); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
} else if suffix != util.NodeFilterMapKeyAll {
|
|
return fmt.Errorf("error adding port mappings: unknown suffix %s", suffix)
|
|
}
|
|
}
|
|
}
|
|
|
|
// print generated loadbalancer config if exists
|
|
// (avoid segmentation fault if loadbalancer is disabled)
|
|
if l.Log().GetLevel() >= logrus.DebugLevel && cluster.ServerLoadBalancer != nil {
|
|
yamlized, err := yaml.Marshal(cluster.ServerLoadBalancer.Config)
|
|
if err != nil {
|
|
l.Log().Errorf("error printing loadbalancer config: %v", err)
|
|
} else {
|
|
l.Log().Debugf("generated loadbalancer config:\n%s", string(yamlized))
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func addPortMappings(node *k3d.Node, portmappings []nat.PortMapping) error {
|
|
if node.Ports == nil {
|
|
node.Ports = nat.PortMap{}
|
|
}
|
|
for _, pm := range portmappings {
|
|
node.Ports[pm.Port] = append(node.Ports[pm.Port], pm.Binding)
|
|
}
|
|
return nil
|
|
}
|
|
|