Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 35 additions & 7 deletions pkg/cli/connect_helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"errors"
"fmt"
"io"
"net"
"net/url"
"os"
"os/exec"
"os/signal"
Expand Down Expand Up @@ -392,14 +394,10 @@ func (cmd *connectHelm) getVClusterKubeConfig(ctx context.Context, vclusterName

cluster.Server = cmd.Server
} else {
splitted := strings.Split(cluster.Server, ":")
if len(splitted) != 3 {
return nil, fmt.Errorf("unexpected server in kubeconfig: %s", cluster.Server)
cluster.Server, port, err = portForwardServer(cluster.Server, cmd.LocalPort)
if err != nil {
return nil, err
}

port = splitted[2]
splitted[2] = strconv.Itoa(cmd.LocalPort)
cluster.Server = strings.Join(splitted, ":")
}
}

Expand Down Expand Up @@ -458,6 +456,36 @@ func (cmd *connectHelm) getVClusterKubeConfig(ctx context.Context, vclusterName
return kubeConfig, nil
}

func portForwardServer(server string, localPort int) (string, string, error) {
parsed, err := url.Parse(server)
if err != nil || parsed.Scheme == "" || parsed.Host == "" {
return "", "", fmt.Errorf("unexpected server in kubeconfig: %s", server)
}

remotePort, err := serverPort(parsed)
if err != nil {
return "", "", err
}

parsed.Host = net.JoinHostPort(parsed.Hostname(), strconv.Itoa(localPort))
return parsed.String(), remotePort, nil
}

func serverPort(parsed *url.URL) (string, error) {
if parsed.Port() != "" {
return parsed.Port(), nil
}

switch parsed.Scheme {
case "https":
return "443", nil
case "http":
return "80", nil
default:
return "", fmt.Errorf("unexpected server in kubeconfig: %s", parsed.String())
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same error message for both failure cases might be on purpose, right? Could be made more explicit by making the error text a const.

Suggested change
return "", fmt.Errorf("unexpected server in kubeconfig: %s", parsed.String())
return "", fmt.Errorf("unexpected server scheme in kubeconfig: %s", parsed.String())

}
}

func getServiceAccountClientAndName(kubeConfig clientcmdapi.Config, options *ConnectOptions) (kubernetes.Interface, string, string, error) {
vKubeClient, err := getLocalVClusterClient(kubeConfig, options)
if err != nil {
Expand Down
53 changes: 53 additions & 0 deletions pkg/cli/connect_helm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,56 @@ func TestExchangeContextName(t *testing.T) {
assert.DeepEqual(t, newConfig, testCase.expectedConfig)
}
}

func TestPortForwardServer(t *testing.T) {
tests := []struct {
name string
server string
localPort int
expectedServer string
expectedPort string
}{
{
name: "server with explicit port",
server: "https://example.com:443",
localPort: 10443,
expectedServer: "https://example.com:10443",
expectedPort: "443",
},
{
name: "server without explicit port",
server: "https://example.com",
localPort: 10443,
expectedServer: "https://example.com:10443",
expectedPort: "443",
},
{
name: "http server without explicit port",
server: "http://example.com",
localPort: 10080,
expectedServer: "http://example.com:10080",
expectedPort: "80",
},
{
name: "ipv6 server with explicit port",
server: "https://[2001:db8::1]:9443",
localPort: 10443,
expectedServer: "https://[2001:db8::1]:10443",
expectedPort: "9443",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server, port, err := portForwardServer(tt.server, tt.localPort)
assert.NilError(t, err)
assert.Equal(t, server, tt.expectedServer)
assert.Equal(t, port, tt.expectedPort)
})
}
}

func TestPortForwardServerInvalid(t *testing.T) {
_, _, err := portForwardServer("example.com", 10443)
assert.Error(t, err, "unexpected server in kubeconfig: example.com")
}