Skip to content

Commit 2574c9d

Browse files
authored
Merge pull request #3817 from telepresenceio/thallgren/rancher-desktop
Make telepresence connect --docker work with Rancher Desktop
2 parents 9fbc93f + 25971e9 commit 2574c9d

File tree

7 files changed

+94
-8
lines changed

7 files changed

+94
-8
lines changed

CHANGELOG.yml

+13
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,19 @@ items:
144144
body: >-
145145
The clusterID was deprecated some time ago, and replaced by the ID of the namespace where the traffic-manager
146146
is installed.
147+
- type: bugfix
148+
title: Make telepresence connect --docker work with Rancher Desktop
149+
body: >-
150+
Rancher Desktop will start a K3s control-plane and typically expose the
151+
Kubernetes API server at `127.0.0.1:6443`. Telepresence can connect to
152+
this cluster when running on the host, but the address is not available
153+
when connecting in docker mode.
154+
155+
The problem is solved by ensuring that the Kubernetes API server address used when
156+
doing a `telepresence connect --docker` is swapped from 127.0.0.1 to the
157+
internal address of the control-plane node. This works because that
158+
address is available to other docker containers, and the Kubernetes API
159+
server is configured with a certificate that accepts it.
147160
- type: bugfix
148161
title: Rename charts/telepresence to charts/telepresence-oss.
149162
body: >-

charts/telepresence-oss/templates/agentInjectorWebhook.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ already managed by some other traffic-manager.
4343
{{- $rqMatch = not (has $val .values) }}
4444
{{- else if eq .operator "Exists" }}
4545
{{- $rqMatch = not (eq $val "") }}
46+
{{- else if eq .operator "DoesNotExist" }}
47+
{{- $rqMatch = eq $val "" }}
4648
{{- else }}
4749
{{- fail printf "unsupported labelSelectorOperator %s" .operator}}
4850
{{- end }}

docs/release-notes.md

+7
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,13 @@ The traffic-agent configuration was moved into a pod-annotation. This avoids syn
126126
The clusterID was deprecated some time ago, and replaced by the ID of the namespace where the traffic-manager is installed.
127127
</div>
128128

129+
## <div style="display:flex;"><img src="images/bugfix.png" alt="bugfix" style="width:30px;height:fit-content;"/><div style="display:flex;margin-left:7px;">Make telepresence connect --docker work with Rancher Desktop</div></div>
130+
<div style="margin-left: 15px">
131+
132+
Rancher Desktop will start a K3s control-plane and typically expose the Kubernetes API server at `127.0.0.1:6443`. Telepresence can connect to this cluster when running on the host, but the address is not available when connecting in docker mode.
133+
The problem is solved by ensuring that the Kubernetes API server address used when doing a `telepresence connect --docker` is swapped from 127.0.0.1 to the internal address of the control-plane node. This works because that address is available to other docker containers, and the Kubernetes API server is configured with a certificate that accepts it.
134+
</div>
135+
129136
## <div style="display:flex;"><img src="images/bugfix.png" alt="bugfix" style="width:30px;height:fit-content;"/><div style="display:flex;margin-left:7px;">Rename charts/telepresence to charts/telepresence-oss.</div></div>
130137
<div style="margin-left: 15px">
131138

docs/release-notes.mdx

+7
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,13 @@ The traffic-agent configuration was moved into a pod-annotation. This avoids syn
132132
The clusterID was deprecated some time ago, and replaced by the ID of the namespace where the traffic-manager is installed.
133133
</Body>
134134
</Note>
135+
<Note>
136+
<Title type="bugfix">Make telepresence connect --docker work with Rancher Desktop</Title>
137+
<Body>
138+
Rancher Desktop will start a K3s control-plane and typically expose the Kubernetes API server at `127.0.0.1:6443`. Telepresence can connect to this cluster when running on the host, but the address is not available when connecting in docker mode.
139+
The problem is solved by ensuring that the Kubernetes API server address used when doing a `telepresence connect --docker` is swapped from 127.0.0.1 to the internal address of the control-plane node. This works because that address is available to other docker containers, and the Kubernetes API server is configured with a certificate that accepts it.
140+
</Body>
141+
</Note>
135142
<Note>
136143
<Title type="bugfix">Rename charts/telepresence to charts/telepresence-oss.</Title>
137144
<Body>

pkg/client/cli/connect/connector.go

+16-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"google.golang.org/grpc/codes"
2121
"google.golang.org/grpc/status"
2222
"google.golang.org/protobuf/types/known/emptypb"
23+
"k8s.io/client-go/kubernetes"
2324

2425
"github.com/datawire/dlib/dlog"
2526
"github.com/telepresenceio/telepresence/rpc/v2/common"
@@ -34,6 +35,7 @@ import (
3435
"github.com/telepresenceio/telepresence/v2/pkg/errcat"
3536
"github.com/telepresenceio/telepresence/v2/pkg/filelocation"
3637
"github.com/telepresenceio/telepresence/v2/pkg/ioutil"
38+
"github.com/telepresenceio/telepresence/v2/pkg/k8sapi"
3739
"github.com/telepresenceio/telepresence/v2/pkg/proc"
3840
)
3941

@@ -252,7 +254,20 @@ func launchConnectorDaemon(ctx context.Context, connectorDaemon string, required
252254
_ = fh.Close()
253255
}
254256
ctx = docker.EnableClient(ctx)
255-
conn, err = docker.LaunchDaemon(ctx, daemonID)
257+
258+
// An initialized kubernetes interface is required by LaunchDaemon, because it is necessary
259+
// when checking if the containerized daemon is connecting to a k3s control plane node.
260+
var kc *client.Kubeconfig
261+
ctx, kc, err = client.NewKubeconfig(ctx, cr.KubeFlags, cr.ManagerNamespace)
262+
if err != nil {
263+
return ctx, err
264+
}
265+
var ki *kubernetes.Clientset
266+
ki, err = kubernetes.NewForConfig(kc.RestConfig)
267+
if err != nil {
268+
return ctx, err
269+
}
270+
conn, err = docker.LaunchDaemon(k8sapi.WithK8sInterface(ctx, ki), daemonID)
256271
} else {
257272
args := []string{connectorDaemon, "connector-foreground"}
258273
if cr.UserDaemonProfilingPort > 0 {

pkg/client/docker/daemon.go

+45-4
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ import (
2222
"github.com/go-json-experiment/json"
2323
"google.golang.org/grpc"
2424
"google.golang.org/grpc/credentials/insecure"
25-
runtime2 "k8s.io/apimachinery/pkg/runtime"
25+
core "k8s.io/api/core/v1"
26+
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
27+
"k8s.io/apimachinery/pkg/labels"
28+
"k8s.io/apimachinery/pkg/runtime"
2629
"k8s.io/client-go/tools/clientcmd/api"
2730

2831
"github.com/datawire/dlib/dlog"
@@ -34,6 +37,7 @@ import (
3437
"github.com/telepresenceio/telepresence/v2/pkg/errcat"
3538
"github.com/telepresenceio/telepresence/v2/pkg/filelocation"
3639
"github.com/telepresenceio/telepresence/v2/pkg/iputil"
40+
"github.com/telepresenceio/telepresence/v2/pkg/k8sapi"
3741
"github.com/telepresenceio/telepresence/v2/pkg/proc"
3842
"github.com/telepresenceio/telepresence/v2/pkg/routing"
3943
"github.com/telepresenceio/telepresence/v2/pkg/shellquote"
@@ -277,7 +281,7 @@ func handleLocalK8s(ctx context.Context, daemonID *daemon.Identifier, config *ap
277281
return nil
278282
}
279283
isMinikube := false
280-
if ex, ok := cl.Extensions["cluster_info"].(*runtime2.Unknown); ok {
284+
if ex, ok := cl.Extensions["cluster_info"].(*runtime.Unknown); ok {
281285
var data map[string]any
282286
isMinikube = json.Unmarshal(ex.Raw, &data) == nil && data["provider"] == "minikube.sigs.k8s.io"
283287
}
@@ -304,7 +308,12 @@ func handleLocalK8s(ctx context.Context, daemonID *daemon.Identifier, config *ap
304308
if isMinikube {
305309
hostPort, network = detectMinikube(ctx, cjs, addrPort, cc.Cluster)
306310
} else {
307-
hostPort, network = detectKind(ctx, cjs, addrPort)
311+
addr = detectK3sControlPlaneNode(ctx)
312+
if addr.IsValid() {
313+
hostPort = netip.AddrPortFrom(addr, uint16(port))
314+
} else {
315+
hostPort, network = detectKind(ctx, cjs, addrPort)
316+
}
308317
}
309318
if hostPort.IsValid() {
310319
dlog.Debugf(ctx, "hostPort %s, network %s", hostPort, network)
@@ -366,7 +375,7 @@ func LaunchDaemon(ctx context.Context, daemonID *daemon.Identifier) (conn *grpc.
366375
// This may happen if the daemon has died (and hence, we never discovered it), but
367376
// the container still hasn't died. Let's sleep for a short while and retry.
368377
if i < 6 {
369-
dtime.SleepWithContext(ctx, time.Duration(i)*200*time.Millisecond)
378+
dtime.SleepWithContext(ctx, time.Duration(i)*500*time.Millisecond)
370379
continue
371380
}
372381
if stopAttempted {
@@ -486,6 +495,38 @@ func useMinikubeNetwork(ctx context.Context, addr netip.Addr) bool {
486495
return false
487496
}
488497

498+
// detectK3sControlPlaneNode returns the internal IP of the k3s control-plane node, if
499+
// such a node is found, or an invalid address otherwise.
500+
func detectK3sControlPlaneNode(ctx context.Context) (addr netip.Addr) {
501+
ki := k8sapi.GetK8sInterface(ctx)
502+
le := labels.SelectorFromSet(map[string]string{
503+
"node-role.kubernetes.io/control-plane": "true",
504+
"node-role.kubernetes.io/master": "true",
505+
"node.kubernetes.io/instance-type": "k3s",
506+
})
507+
ns, err := ki.CoreV1().Nodes().List(ctx, meta.ListOptions{LabelSelector: le.String()})
508+
if err != nil {
509+
dlog.Errorf(ctx, "failed to list nodes: %v", err)
510+
return addr
511+
}
512+
nis := ns.Items
513+
for i := range nis {
514+
n := &nis[i]
515+
for _, a := range n.Status.Addresses {
516+
if a.Type == core.NodeInternalIP {
517+
addr, err = netip.ParseAddr(a.Address)
518+
if err != nil {
519+
dlog.Errorf(ctx, "failed to parse address %s: %v", a.Address, err)
520+
} else {
521+
dlog.Debugf(ctx, "Found k3s control plane %s with internal IP %s", n.Name, addr)
522+
}
523+
break
524+
}
525+
}
526+
}
527+
return addr
528+
}
529+
489530
// detectMinikube returns the container IP:port for the given hostAddrPort for a container where the
490531
// "name.minikube.sigs.k8s.io" label is equal to the given cluster name.
491532
// Returns the internal IP:port for the given hostAddrPort and the name of a network that makes the

pkg/labels/selector.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ type Operator string
1919
// NOTE! The lowercase variants in the k8s.io/apimachinery/pkg/selection are intended for
2020
// the selector string representation only. They are invalid in a YAML/JSON manifest!
2121
const (
22-
OperatorIn Operator = "In"
23-
OperatorNotIn Operator = "NotIn"
24-
OperatorExists Operator = "Exists"
22+
OperatorIn Operator = "In"
23+
OperatorNotIn Operator = "NotIn"
24+
OperatorExists Operator = "Exists"
25+
OperatorNotExists Operator = "DoesNotExist"
2526
)
2627

2728
func (op Operator) String() string {

0 commit comments

Comments
 (0)