From a43878b115ac67c98679e71797980fe94dd9d0f1 Mon Sep 17 00:00:00 2001 From: Antonio Ojea Date: Tue, 1 Dec 2020 15:37:01 +0100 Subject: [PATCH] podman hostport info for 2.2.0+ --- .../internal/providers/podman/provider.go | 123 +++++++++++------- 1 file changed, 78 insertions(+), 45 deletions(-) diff --git a/pkg/cluster/internal/providers/podman/provider.go b/pkg/cluster/internal/providers/podman/provider.go index 83d0e78765..8cdd1519db 100644 --- a/pkg/cluster/internal/providers/podman/provider.go +++ b/pkg/cluster/internal/providers/podman/provider.go @@ -26,6 +26,7 @@ import ( "strings" "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/version" "sigs.k8s.io/kind/pkg/cluster/nodes" "sigs.k8s.io/kind/pkg/cluster/nodeutils" @@ -185,13 +186,79 @@ func (p *provider) GetAPIServerEndpoint(cluster string) (string, error) { return "", errors.Wrap(err, "failed to get api server endpoint") } - // retrieve the specific port mapping using podman inspect + // TODO: get rid of this once podman settles on how to get the port mapping using podman inspect + // This is only used to get the Kubeconfig server field + v, err := getPodmanVersion() + if err != nil { + return "", errors.Wrap(err, "failed to check podman version") + } + if v.LessThan(version.MustParseSemantic("2.2.0")) { + cmd := exec.Command( + "podman", "inspect", + "--format", + "{{ json .NetworkSettings.Ports }}", + n.String(), + ) + lines, err := exec.OutputLines(cmd) + if err != nil { + return "", errors.Wrap(err, "failed to get api server port") + } + if len(lines) != 1 { + return "", errors.Errorf("network details should only be one line, got %d lines", len(lines)) + } + + // portMapping19 maps to the standard CNI portmapping capability used in podman 1.9 + // see: https://github.com/containernetworking/cni/blob/spec-v0.4.0/CONVENTIONS.md + type portMapping19 struct { + HostPort int32 `json:"hostPort"` + ContainerPort int32 `json:"containerPort"` + Protocol string `json:"protocol"` + HostIP string `json:"hostIP"` + } + // portMapping20 maps to the podman 2.0 portmap type + // see: https://github.com/containers/podman/blob/05988fc74fc25f2ad2256d6e011dfb7ad0b9a4eb/libpod/define/container_inspect.go#L134-L143 + type portMapping20 struct { + HostPort string `json:"HostPort"` + HostIP string `json:"HostIp"` + } + + portMappings20 := make(map[string][]portMapping20) + if err := json.Unmarshal([]byte(lines[0]), &portMappings20); err == nil { + for k, v := range portMappings20 { + protocol := "tcp" + parts := strings.Split(k, "/") + if len(parts) == 2 { + protocol = strings.ToLower(parts[1]) + } + containerPort, err := strconv.Atoi(parts[0]) + if err != nil { + return "", err + } + for _, pm := range v { + if containerPort == common.APIServerInternalPort && protocol == "tcp" { + return net.JoinHostPort(pm.HostIP, pm.HostPort), nil + } + } + } + } + var portMappings19 []portMapping19 + if err := json.Unmarshal([]byte(lines[0]), &portMappings19); err != nil { + return "", errors.Errorf("invalid network details: %v", err) + } + for _, pm := range portMappings19 { + if pm.ContainerPort == common.APIServerInternalPort && pm.Protocol == "tcp" { + return net.JoinHostPort(pm.HostIP, strconv.Itoa(int(pm.HostPort))), nil + } + } + } + // TODO: hack until https://github.com/containers/podman/issues/8444 is resolved cmd := exec.Command( "podman", "inspect", "--format", - "{{ json .NetworkSettings.Ports }}", + "{{range .NetworkSettings.Ports }}{{range .}}{{.HostIP}}/{{.HostPort}}{{end}}{{end}}", n.String(), ) + lines, err := exec.OutputLines(cmd) if err != nil { return "", errors.Wrap(err, "failed to get api server port") @@ -199,52 +266,18 @@ func (p *provider) GetAPIServerEndpoint(cluster string) (string, error) { if len(lines) != 1 { return "", errors.Errorf("network details should only be one line, got %d lines", len(lines)) } - - // portMapping19 maps to the standard CNI portmapping capability used in podman 1.9 - // see: https://github.com/containernetworking/cni/blob/spec-v0.4.0/CONVENTIONS.md - type portMapping19 struct { - HostPort int32 `json:"hostPort"` - ContainerPort int32 `json:"containerPort"` - Protocol string `json:"protocol"` - HostIP string `json:"hostIP"` + // output is in the format IP/Port + parts := strings.Split(strings.TrimSpace(lines[0]), "/") + if len(parts) != 2 { + return "", errors.Errorf("network details should be in the format IP/Port, received: %s", parts) } - // portMapping20 maps to the podman 2.0 portmap type - // see: https://github.com/containers/podman/blob/05988fc74fc25f2ad2256d6e011dfb7ad0b9a4eb/libpod/define/container_inspect.go#L134-L143 - type portMapping20 struct { - HostPort string `json:"HostPort"` - HostIP string `json:"HostIp"` - } - - portMappings20 := make(map[string][]portMapping20) - if err := json.Unmarshal([]byte(lines[0]), &portMappings20); err == nil { - for k, v := range portMappings20 { - protocol := "tcp" - parts := strings.Split(k, "/") - if len(parts) == 2 { - protocol = strings.ToLower(parts[1]) - } - containerPort, err := strconv.Atoi(parts[0]) - if err != nil { - return "", err - } - for _, pm := range v { - if containerPort == common.APIServerInternalPort && protocol == "tcp" { - return net.JoinHostPort(pm.HostIP, pm.HostPort), nil - } - } - } - } - var portMappings19 []portMapping19 - if err := json.Unmarshal([]byte(lines[0]), &portMappings19); err != nil { - return "", errors.Errorf("invalid network details: %v", err) - } - for _, pm := range portMappings19 { - if pm.ContainerPort == common.APIServerInternalPort && pm.Protocol == "tcp" { - return net.JoinHostPort(pm.HostIP, strconv.Itoa(int(pm.HostPort))), nil - } + host := parts[0] + port, err := strconv.Atoi(parts[1]) + if err != nil { + return "", errors.Errorf("network port not an integer: %v", err) } - return "", errors.Errorf("unable to find apiserver endpoint information") + return net.JoinHostPort(host, strconv.Itoa(port)), nil } // GetAPIServerInternalEndpoint is part of the providers.Provider interface