mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-27 02:37:38 +00:00
cmd/k8s-operator,k8s-operator: allow proxies accept advertized routes.
Add a new .spec.tailscale.acceptRoutes field to ProxyClass, that can be optionally set to true for the proxies to accept routes advertized by other nodes on tailnet (equivalent of setting --accept-routes to true). Updates tailscale/tailscale#12322,tailscale/tailscale#10684 Signed-off-by: Irbe Krumina <irbe@tailscale.com>
This commit is contained in:
parent
36e8e8cd64
commit
66dc71e420
@ -184,7 +184,7 @@ func (a *ConnectorReconciler) maybeProvisionConnector(ctx context.Context, logge
|
|||||||
Connector: &connector{
|
Connector: &connector{
|
||||||
isExitNode: cn.Spec.ExitNode,
|
isExitNode: cn.Spec.ExitNode,
|
||||||
},
|
},
|
||||||
ProxyClass: proxyClass,
|
ProxyClassName: proxyClass,
|
||||||
}
|
}
|
||||||
|
|
||||||
if cn.Spec.SubnetRouter != nil && len(cn.Spec.SubnetRouter.AdvertiseRoutes) > 0 {
|
if cn.Spec.SubnetRouter != nil && len(cn.Spec.SubnetRouter.AdvertiseRoutes) > 0 {
|
||||||
|
@ -74,7 +74,7 @@ func TestConnector(t *testing.T) {
|
|||||||
isExitNode: true,
|
isExitNode: true,
|
||||||
subnetRoutes: "10.40.0.0/14",
|
subnetRoutes: "10.40.0.0/14",
|
||||||
}
|
}
|
||||||
expectEqual(t, fc, expectedSecret(t, opts), nil)
|
expectEqual(t, fc, expectedSecret(t, fc, opts), nil)
|
||||||
expectEqual(t, fc, expectedSTS(t, fc, opts), removeHashAnnotation)
|
expectEqual(t, fc, expectedSTS(t, fc, opts), removeHashAnnotation)
|
||||||
|
|
||||||
// Add another route to be advertised.
|
// Add another route to be advertised.
|
||||||
@ -152,7 +152,7 @@ func TestConnector(t *testing.T) {
|
|||||||
subnetRoutes: "10.40.0.0/14",
|
subnetRoutes: "10.40.0.0/14",
|
||||||
hostname: "test-connector",
|
hostname: "test-connector",
|
||||||
}
|
}
|
||||||
expectEqual(t, fc, expectedSecret(t, opts), nil)
|
expectEqual(t, fc, expectedSecret(t, fc, opts), nil)
|
||||||
expectEqual(t, fc, expectedSTS(t, fc, opts), removeHashAnnotation)
|
expectEqual(t, fc, expectedSTS(t, fc, opts), removeHashAnnotation)
|
||||||
|
|
||||||
// Add an exit node.
|
// Add an exit node.
|
||||||
@ -237,7 +237,7 @@ func TestConnectorWithProxyClass(t *testing.T) {
|
|||||||
isExitNode: true,
|
isExitNode: true,
|
||||||
subnetRoutes: "10.40.0.0/14",
|
subnetRoutes: "10.40.0.0/14",
|
||||||
}
|
}
|
||||||
expectEqual(t, fc, expectedSecret(t, opts), nil)
|
expectEqual(t, fc, expectedSecret(t, fc, opts), nil)
|
||||||
expectEqual(t, fc, expectedSTS(t, fc, opts), removeHashAnnotation)
|
expectEqual(t, fc, expectedSTS(t, fc, opts), removeHashAnnotation)
|
||||||
|
|
||||||
// 2. Update Connector to specify a ProxyClass. ProxyClass is not yet
|
// 2. Update Connector to specify a ProxyClass. ProxyClass is not yet
|
||||||
|
@ -1011,6 +1011,13 @@ spec:
|
|||||||
value:
|
value:
|
||||||
description: Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.
|
description: Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.
|
||||||
type: string
|
type: string
|
||||||
|
tailscale:
|
||||||
|
description: TailscaleConfig contains options to configure the tailscale-specific parameters of proxies.
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
acceptRoutes:
|
||||||
|
description: AcceptRoutes can be set to true to make the proxy instance accept routes advertized by other nodes on the tailnet, such as subnet routes. This is equivalent of passing --accept-routes flag to a tailscale Linux client. https://tailscale.com/kb/1019/subnets#use-your-subnet-routes-from-other-machines Defaults to false.
|
||||||
|
type: boolean
|
||||||
status:
|
status:
|
||||||
description: Status of the ProxyClass. This is set and managed automatically. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
description: Status of the ProxyClass. This is set and managed automatically. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||||
type: object
|
type: object
|
||||||
|
@ -1263,6 +1263,13 @@ spec:
|
|||||||
type: array
|
type: array
|
||||||
type: object
|
type: object
|
||||||
type: object
|
type: object
|
||||||
|
tailscale:
|
||||||
|
description: TailscaleConfig contains options to configure the tailscale-specific parameters of proxies.
|
||||||
|
properties:
|
||||||
|
acceptRoutes:
|
||||||
|
description: AcceptRoutes can be set to true to make the proxy instance accept routes advertized by other nodes on the tailnet, such as subnet routes. This is equivalent of passing --accept-routes flag to a tailscale Linux client. https://tailscale.com/kb/1019/subnets#use-your-subnet-routes-from-other-machines Defaults to false.
|
||||||
|
type: boolean
|
||||||
|
type: object
|
||||||
type: object
|
type: object
|
||||||
status:
|
status:
|
||||||
description: Status of the ProxyClass. This is set and managed automatically. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
description: Status of the ProxyClass. This is set and managed automatically. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||||
|
@ -264,7 +264,7 @@ func (a *IngressReconciler) maybeProvision(ctx context.Context, logger *zap.Suga
|
|||||||
ServeConfig: sc,
|
ServeConfig: sc,
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
ChildResourceLabels: crl,
|
ChildResourceLabels: crl,
|
||||||
ProxyClass: proxyClass,
|
ProxyClassName: proxyClass,
|
||||||
}
|
}
|
||||||
|
|
||||||
if val := ing.GetAnnotations()[AnnotationExperimentalForwardClusterTrafficViaL7IngresProxy]; val == "true" {
|
if val := ing.GetAnnotations()[AnnotationExperimentalForwardClusterTrafficViaL7IngresProxy]; val == "true" {
|
||||||
|
@ -100,7 +100,7 @@ func TestTailscaleIngress(t *testing.T) {
|
|||||||
}
|
}
|
||||||
opts.serveConfig = serveConfig
|
opts.serveConfig = serveConfig
|
||||||
|
|
||||||
expectEqual(t, fc, expectedSecret(t, opts), nil)
|
expectEqual(t, fc, expectedSecret(t, fc, opts), nil)
|
||||||
expectEqual(t, fc, expectedHeadlessService(shortName, "ingress"), nil)
|
expectEqual(t, fc, expectedHeadlessService(shortName, "ingress"), nil)
|
||||||
expectEqual(t, fc, expectedSTSUserspace(t, fc, opts), removeHashAnnotation)
|
expectEqual(t, fc, expectedSTSUserspace(t, fc, opts), removeHashAnnotation)
|
||||||
|
|
||||||
@ -231,7 +231,7 @@ func TestTailscaleIngressWithProxyClass(t *testing.T) {
|
|||||||
}
|
}
|
||||||
opts.serveConfig = serveConfig
|
opts.serveConfig = serveConfig
|
||||||
|
|
||||||
expectEqual(t, fc, expectedSecret(t, opts), nil)
|
expectEqual(t, fc, expectedSecret(t, fc, opts), nil)
|
||||||
expectEqual(t, fc, expectedHeadlessService(shortName, "ingress"), nil)
|
expectEqual(t, fc, expectedHeadlessService(shortName, "ingress"), nil)
|
||||||
expectEqual(t, fc, expectedSTSUserspace(t, fc, opts), removeHashAnnotation)
|
expectEqual(t, fc, expectedSTSUserspace(t, fc, opts), removeHashAnnotation)
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ func TestLoadBalancerClass(t *testing.T) {
|
|||||||
clusterTargetIP: "10.20.30.40",
|
clusterTargetIP: "10.20.30.40",
|
||||||
}
|
}
|
||||||
|
|
||||||
expectEqual(t, fc, expectedSecret(t, opts), nil)
|
expectEqual(t, fc, expectedSecret(t, fc, opts), nil)
|
||||||
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
||||||
expectEqual(t, fc, expectedSTS(t, fc, opts), removeHashAnnotation)
|
expectEqual(t, fc, expectedSTS(t, fc, opts), removeHashAnnotation)
|
||||||
|
|
||||||
@ -216,7 +216,7 @@ func TestTailnetTargetFQDNAnnotation(t *testing.T) {
|
|||||||
hostname: "default-test",
|
hostname: "default-test",
|
||||||
}
|
}
|
||||||
|
|
||||||
expectEqual(t, fc, expectedSecret(t, o), nil)
|
expectEqual(t, fc, expectedSecret(t, fc, o), nil)
|
||||||
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
||||||
expectEqual(t, fc, expectedSTS(t, fc, o), removeHashAnnotation)
|
expectEqual(t, fc, expectedSTS(t, fc, o), removeHashAnnotation)
|
||||||
want := &corev1.Service{
|
want := &corev1.Service{
|
||||||
@ -240,7 +240,7 @@ func TestTailnetTargetFQDNAnnotation(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
expectEqual(t, fc, want, nil)
|
expectEqual(t, fc, want, nil)
|
||||||
expectEqual(t, fc, expectedSecret(t, o), nil)
|
expectEqual(t, fc, expectedSecret(t, fc, o), nil)
|
||||||
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
||||||
expectEqual(t, fc, expectedSTS(t, fc, o), removeHashAnnotation)
|
expectEqual(t, fc, expectedSTS(t, fc, o), removeHashAnnotation)
|
||||||
|
|
||||||
@ -326,7 +326,7 @@ func TestTailnetTargetIPAnnotation(t *testing.T) {
|
|||||||
hostname: "default-test",
|
hostname: "default-test",
|
||||||
}
|
}
|
||||||
|
|
||||||
expectEqual(t, fc, expectedSecret(t, o), nil)
|
expectEqual(t, fc, expectedSecret(t, fc, o), nil)
|
||||||
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
||||||
expectEqual(t, fc, expectedSTS(t, fc, o), removeHashAnnotation)
|
expectEqual(t, fc, expectedSTS(t, fc, o), removeHashAnnotation)
|
||||||
want := &corev1.Service{
|
want := &corev1.Service{
|
||||||
@ -350,7 +350,7 @@ func TestTailnetTargetIPAnnotation(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
expectEqual(t, fc, want, nil)
|
expectEqual(t, fc, want, nil)
|
||||||
expectEqual(t, fc, expectedSecret(t, o), nil)
|
expectEqual(t, fc, expectedSecret(t, fc, o), nil)
|
||||||
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
||||||
expectEqual(t, fc, expectedSTS(t, fc, o), removeHashAnnotation)
|
expectEqual(t, fc, expectedSTS(t, fc, o), removeHashAnnotation)
|
||||||
|
|
||||||
@ -433,7 +433,7 @@ func TestAnnotations(t *testing.T) {
|
|||||||
clusterTargetIP: "10.20.30.40",
|
clusterTargetIP: "10.20.30.40",
|
||||||
}
|
}
|
||||||
|
|
||||||
expectEqual(t, fc, expectedSecret(t, o), nil)
|
expectEqual(t, fc, expectedSecret(t, fc, o), nil)
|
||||||
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
||||||
expectEqual(t, fc, expectedSTS(t, fc, o), removeHashAnnotation)
|
expectEqual(t, fc, expectedSTS(t, fc, o), removeHashAnnotation)
|
||||||
want := &corev1.Service{
|
want := &corev1.Service{
|
||||||
@ -541,7 +541,7 @@ func TestAnnotationIntoLB(t *testing.T) {
|
|||||||
clusterTargetIP: "10.20.30.40",
|
clusterTargetIP: "10.20.30.40",
|
||||||
}
|
}
|
||||||
|
|
||||||
expectEqual(t, fc, expectedSecret(t, o), nil)
|
expectEqual(t, fc, expectedSecret(t, fc, o), nil)
|
||||||
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
||||||
expectEqual(t, fc, expectedSTS(t, fc, o), removeHashAnnotation)
|
expectEqual(t, fc, expectedSTS(t, fc, o), removeHashAnnotation)
|
||||||
|
|
||||||
@ -672,7 +672,7 @@ func TestLBIntoAnnotation(t *testing.T) {
|
|||||||
clusterTargetIP: "10.20.30.40",
|
clusterTargetIP: "10.20.30.40",
|
||||||
}
|
}
|
||||||
|
|
||||||
expectEqual(t, fc, expectedSecret(t, o), nil)
|
expectEqual(t, fc, expectedSecret(t, fc, o), nil)
|
||||||
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
||||||
expectEqual(t, fc, expectedSTS(t, fc, o), removeHashAnnotation)
|
expectEqual(t, fc, expectedSTS(t, fc, o), removeHashAnnotation)
|
||||||
|
|
||||||
@ -813,7 +813,7 @@ func TestCustomHostname(t *testing.T) {
|
|||||||
clusterTargetIP: "10.20.30.40",
|
clusterTargetIP: "10.20.30.40",
|
||||||
}
|
}
|
||||||
|
|
||||||
expectEqual(t, fc, expectedSecret(t, o), nil)
|
expectEqual(t, fc, expectedSecret(t, fc, o), nil)
|
||||||
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
||||||
expectEqual(t, fc, expectedSTS(t, fc, o), removeHashAnnotation)
|
expectEqual(t, fc, expectedSTS(t, fc, o), removeHashAnnotation)
|
||||||
want := &corev1.Service{
|
want := &corev1.Service{
|
||||||
@ -935,10 +935,14 @@ func TestProxyClassForService(t *testing.T) {
|
|||||||
// Setup
|
// Setup
|
||||||
pc := &tsapi.ProxyClass{
|
pc := &tsapi.ProxyClass{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "custom-metadata"},
|
ObjectMeta: metav1.ObjectMeta{Name: "custom-metadata"},
|
||||||
Spec: tsapi.ProxyClassSpec{StatefulSet: &tsapi.StatefulSet{
|
Spec: tsapi.ProxyClassSpec{
|
||||||
Labels: map[string]string{"foo": "bar"},
|
TailscaleConfig: &tsapi.TailscaleConfig{
|
||||||
Annotations: map[string]string{"bar.io/foo": "some-val"},
|
AcceptRoutes: true,
|
||||||
Pod: &tsapi.Pod{Annotations: map[string]string{"foo.io/bar": "some-val"}}}},
|
},
|
||||||
|
StatefulSet: &tsapi.StatefulSet{
|
||||||
|
Labels: map[string]string{"foo": "bar"},
|
||||||
|
Annotations: map[string]string{"bar.io/foo": "some-val"},
|
||||||
|
Pod: &tsapi.Pod{Annotations: map[string]string{"foo.io/bar": "some-val"}}}},
|
||||||
}
|
}
|
||||||
fc := fake.NewClientBuilder().
|
fc := fake.NewClientBuilder().
|
||||||
WithScheme(tsapi.GlobalScheme).
|
WithScheme(tsapi.GlobalScheme).
|
||||||
@ -989,7 +993,7 @@ func TestProxyClassForService(t *testing.T) {
|
|||||||
hostname: "default-test",
|
hostname: "default-test",
|
||||||
clusterTargetIP: "10.20.30.40",
|
clusterTargetIP: "10.20.30.40",
|
||||||
}
|
}
|
||||||
expectEqual(t, fc, expectedSecret(t, opts), nil)
|
expectEqual(t, fc, expectedSecret(t, fc, opts), nil)
|
||||||
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
||||||
expectEqual(t, fc, expectedSTS(t, fc, opts), removeHashAnnotation)
|
expectEqual(t, fc, expectedSTS(t, fc, opts), removeHashAnnotation)
|
||||||
|
|
||||||
@ -1001,6 +1005,7 @@ func TestProxyClassForService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
expectReconciled(t, sr, "default", "test")
|
expectReconciled(t, sr, "default", "test")
|
||||||
expectEqual(t, fc, expectedSTS(t, fc, opts), removeHashAnnotation)
|
expectEqual(t, fc, expectedSTS(t, fc, opts), removeHashAnnotation)
|
||||||
|
expectEqual(t, fc, expectedSecret(t, fc, opts), nil)
|
||||||
|
|
||||||
// 3. ProxyClass is set to Ready, the Service gets reconciled by the
|
// 3. ProxyClass is set to Ready, the Service gets reconciled by the
|
||||||
// services-reconciler and the customization from the ProxyClass is
|
// services-reconciler and the customization from the ProxyClass is
|
||||||
@ -1016,6 +1021,7 @@ func TestProxyClassForService(t *testing.T) {
|
|||||||
opts.proxyClass = pc.Name
|
opts.proxyClass = pc.Name
|
||||||
expectReconciled(t, sr, "default", "test")
|
expectReconciled(t, sr, "default", "test")
|
||||||
expectEqual(t, fc, expectedSTS(t, fc, opts), removeHashAnnotation)
|
expectEqual(t, fc, expectedSTS(t, fc, opts), removeHashAnnotation)
|
||||||
|
expectEqual(t, fc, expectedSecret(t, fc, opts), removeAuthKeyIfExistsModifier(t))
|
||||||
|
|
||||||
// 4. tailscale.com/proxy-class label is removed from the Service, the
|
// 4. tailscale.com/proxy-class label is removed from the Service, the
|
||||||
// configuration from the ProxyClass is removed from the cluster
|
// configuration from the ProxyClass is removed from the cluster
|
||||||
@ -1477,7 +1483,7 @@ func Test_externalNameService(t *testing.T) {
|
|||||||
clusterTargetDNS: "foo.com",
|
clusterTargetDNS: "foo.com",
|
||||||
}
|
}
|
||||||
|
|
||||||
expectEqual(t, fc, expectedSecret(t, opts), nil)
|
expectEqual(t, fc, expectedSecret(t, fc, opts), nil)
|
||||||
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
expectEqual(t, fc, expectedHeadlessService(shortName, "svc"), nil)
|
||||||
expectEqual(t, fc, expectedSTS(t, fc, opts), removeHashAnnotation)
|
expectEqual(t, fc, expectedSTS(t, fc, opts), removeHashAnnotation)
|
||||||
|
|
||||||
|
@ -125,7 +125,9 @@ type tailscaleSTSConfig struct {
|
|||||||
// what this StatefulSet should be created for.
|
// what this StatefulSet should be created for.
|
||||||
Connector *connector
|
Connector *connector
|
||||||
|
|
||||||
ProxyClass string
|
ProxyClassName string // name of ProxyClass if one needs to be applied to the proxy
|
||||||
|
|
||||||
|
ProxyClass *tsapi.ProxyClass // ProxyClass that needs to be applied to the proxy (if there is one)
|
||||||
}
|
}
|
||||||
|
|
||||||
type connector struct {
|
type connector struct {
|
||||||
@ -171,6 +173,18 @@ func (a *tailscaleSTSReconciler) Provision(ctx context.Context, logger *zap.Suga
|
|||||||
return nil, fmt.Errorf("failed to reconcile headless service: %w", err)
|
return nil, fmt.Errorf("failed to reconcile headless service: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proxyClass := new(tsapi.ProxyClass)
|
||||||
|
if sts.ProxyClassName != "" {
|
||||||
|
if err := a.Get(ctx, types.NamespacedName{Name: sts.ProxyClassName}, proxyClass); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get ProxyClass: %w", err)
|
||||||
|
}
|
||||||
|
if !tsoperator.ProxyClassIsReady(proxyClass) {
|
||||||
|
logger.Infof("ProxyClass %s specified for the proxy, but it is not (yet) in a ready state, waiting..")
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sts.ProxyClass = proxyClass
|
||||||
|
|
||||||
secretName, tsConfigHash, configs, err := a.createOrGetSecret(ctx, logger, sts, hsvc)
|
secretName, tsConfigHash, configs, err := a.createOrGetSecret(ctx, logger, sts, hsvc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create or get API key secret: %w", err)
|
return nil, fmt.Errorf("failed to create or get API key secret: %w", err)
|
||||||
@ -465,16 +479,6 @@ func (a *tailscaleSTSReconciler) reconcileSTS(ctx context.Context, logger *zap.S
|
|||||||
}
|
}
|
||||||
pod := &ss.Spec.Template
|
pod := &ss.Spec.Template
|
||||||
container := &pod.Spec.Containers[0]
|
container := &pod.Spec.Containers[0]
|
||||||
proxyClass := new(tsapi.ProxyClass)
|
|
||||||
if sts.ProxyClass != "" {
|
|
||||||
if err := a.Get(ctx, types.NamespacedName{Name: sts.ProxyClass}, proxyClass); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get ProxyClass: %w", err)
|
|
||||||
}
|
|
||||||
if !tsoperator.ProxyClassIsReady(proxyClass) {
|
|
||||||
logger.Infof("ProxyClass %s specified for the proxy, but it is not (yet) in a ready state, waiting..")
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
container.Image = a.proxyImage
|
container.Image = a.proxyImage
|
||||||
ss.ObjectMeta = metav1.ObjectMeta{
|
ss.ObjectMeta = metav1.ObjectMeta{
|
||||||
Name: headlessSvc.Name,
|
Name: headlessSvc.Name,
|
||||||
@ -589,9 +593,9 @@ func (a *tailscaleSTSReconciler) reconcileSTS(ctx context.Context, logger *zap.S
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
logger.Debugf("reconciling statefulset %s/%s", ss.GetNamespace(), ss.GetName())
|
logger.Debugf("reconciling statefulset %s/%s", ss.GetNamespace(), ss.GetName())
|
||||||
if sts.ProxyClass != "" {
|
if sts.ProxyClassName != "" {
|
||||||
logger.Debugf("configuring proxy resources with ProxyClass %s", sts.ProxyClass)
|
logger.Debugf("configuring proxy resources with ProxyClass %s", sts.ProxyClassName)
|
||||||
ss = applyProxyClassToStatefulSet(proxyClass, ss, sts, logger)
|
ss = applyProxyClassToStatefulSet(sts.ProxyClass, ss, sts, logger)
|
||||||
}
|
}
|
||||||
updateSS := func(s *appsv1.StatefulSet) {
|
updateSS := func(s *appsv1.StatefulSet) {
|
||||||
s.Spec = ss.Spec
|
s.Spec = ss.Spec
|
||||||
@ -765,6 +769,10 @@ func tailscaledConfig(stsC *tailscaleSTSConfig, newAuthkey string, oldSecret *co
|
|||||||
}
|
}
|
||||||
conf.AdvertiseRoutes = routes
|
conf.AdvertiseRoutes = routes
|
||||||
}
|
}
|
||||||
|
if shouldAcceptRoutes(stsC.ProxyClass) {
|
||||||
|
conf.AcceptRoutes = "true"
|
||||||
|
}
|
||||||
|
|
||||||
if newAuthkey != "" {
|
if newAuthkey != "" {
|
||||||
conf.AuthKey = &newAuthkey
|
conf.AuthKey = &newAuthkey
|
||||||
} else if oldSecret != nil {
|
} else if oldSecret != nil {
|
||||||
@ -803,6 +811,10 @@ func tailscaledConfig(stsC *tailscaleSTSConfig, newAuthkey string, oldSecret *co
|
|||||||
return capVerConfigs, nil
|
return capVerConfigs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func shouldAcceptRoutes(pc *tsapi.ProxyClass) bool {
|
||||||
|
return pc != nil && pc.Spec.TailscaleConfig != nil && pc.Spec.TailscaleConfig.AcceptRoutes
|
||||||
|
}
|
||||||
|
|
||||||
// ptrObject is a type constraint for pointer types that implement
|
// ptrObject is a type constraint for pointer types that implement
|
||||||
// client.Object.
|
// client.Object.
|
||||||
type ptrObject[T any] interface {
|
type ptrObject[T any] interface {
|
||||||
|
@ -204,7 +204,7 @@ func (a *ServiceReconciler) maybeProvision(ctx context.Context, logger *zap.Suga
|
|||||||
Hostname: hostname,
|
Hostname: hostname,
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
ChildResourceLabels: crl,
|
ChildResourceLabels: crl,
|
||||||
ProxyClass: proxyClass,
|
ProxyClassName: proxyClass,
|
||||||
}
|
}
|
||||||
|
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
|
@ -328,7 +328,7 @@ func expectedHeadlessService(name string, parentType string) *corev1.Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func expectedSecret(t *testing.T, opts configOpts) *corev1.Secret {
|
func expectedSecret(t *testing.T, cl client.Client, opts configOpts) *corev1.Secret {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
s := &corev1.Secret{
|
s := &corev1.Secret{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
@ -355,6 +355,16 @@ func expectedSecret(t *testing.T, opts configOpts) *corev1.Secret {
|
|||||||
AuthKey: ptr.To("secret-authkey"),
|
AuthKey: ptr.To("secret-authkey"),
|
||||||
AcceptRoutes: "false",
|
AcceptRoutes: "false",
|
||||||
}
|
}
|
||||||
|
if opts.proxyClass != "" {
|
||||||
|
t.Logf("applying configuration from ProxyClass %s", opts.proxyClass)
|
||||||
|
proxyClass := new(tsapi.ProxyClass)
|
||||||
|
if err := cl.Get(context.Background(), types.NamespacedName{Name: opts.proxyClass}, proxyClass); err != nil {
|
||||||
|
t.Fatalf("error getting ProxyClass: %v", err)
|
||||||
|
}
|
||||||
|
if proxyClass.Spec.TailscaleConfig != nil && proxyClass.Spec.TailscaleConfig.AcceptRoutes {
|
||||||
|
conf.AcceptRoutes = "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
var routes []netip.Prefix
|
var routes []netip.Prefix
|
||||||
if opts.subnetRoutes != "" || opts.isExitNode {
|
if opts.subnetRoutes != "" || opts.isExitNode {
|
||||||
r := opts.subnetRoutes
|
r := opts.subnetRoutes
|
||||||
@ -474,6 +484,7 @@ func expectEqual[T any, O ptrObject[T]](t *testing.T, client client.Client, want
|
|||||||
got.SetResourceVersion("")
|
got.SetResourceVersion("")
|
||||||
want.SetResourceVersion("")
|
want.SetResourceVersion("")
|
||||||
if modifier != nil {
|
if modifier != nil {
|
||||||
|
modifier(want)
|
||||||
modifier(got)
|
modifier(got)
|
||||||
}
|
}
|
||||||
if diff := cmp.Diff(got, want); diff != "" {
|
if diff := cmp.Diff(got, want); diff != "" {
|
||||||
@ -608,3 +619,33 @@ func (c *fakeTSClient) Deleted() []string {
|
|||||||
func removeHashAnnotation(sts *appsv1.StatefulSet) {
|
func removeHashAnnotation(sts *appsv1.StatefulSet) {
|
||||||
delete(sts.Spec.Template.Annotations, podAnnotationLastSetConfigFileHash)
|
delete(sts.Spec.Template.Annotations, podAnnotationLastSetConfigFileHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func removeAuthKeyIfExistsModifier(t *testing.T) func(s *corev1.Secret) {
|
||||||
|
return func(secret *corev1.Secret) {
|
||||||
|
t.Helper()
|
||||||
|
if len(secret.StringData["tailscaled"]) != 0 {
|
||||||
|
conf := &ipn.ConfigVAlpha{}
|
||||||
|
if err := json.Unmarshal([]byte(secret.StringData["tailscaled"]), conf); err != nil {
|
||||||
|
t.Fatalf("error unmarshalling 'tailscaled' contents: %v", err)
|
||||||
|
}
|
||||||
|
conf.AuthKey = nil
|
||||||
|
b, err := json.Marshal(conf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error marshalling updated 'tailscaled' config: %v", err)
|
||||||
|
}
|
||||||
|
mak.Set(&secret.StringData, "tailscaled", string(b))
|
||||||
|
}
|
||||||
|
if len(secret.StringData["cap-95.hujson"]) != 0 {
|
||||||
|
conf := &ipn.ConfigVAlpha{}
|
||||||
|
if err := json.Unmarshal([]byte(secret.StringData["cap-95.hujson"]), conf); err != nil {
|
||||||
|
t.Fatalf("error umarshalling 'cap-95.hujson' contents: %v", err)
|
||||||
|
}
|
||||||
|
conf.AuthKey = nil
|
||||||
|
b, err := json.Marshal(conf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error marshalling 'cap-95.huson' contents: %v", err)
|
||||||
|
}
|
||||||
|
mak.Set(&secret.StringData, "cap-95.hujson", string(b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -613,6 +613,13 @@ Specification of the desired state of the ProxyClass resource. https://git.k8s.i
|
|||||||
Configuration parameters for the proxy's StatefulSet. Tailscale Kubernetes operator deploys a StatefulSet for each of the user configured proxies (Tailscale Ingress, Tailscale Service, Connector).<br/>
|
Configuration parameters for the proxy's StatefulSet. Tailscale Kubernetes operator deploys a StatefulSet for each of the user configured proxies (Tailscale Ingress, Tailscale Service, Connector).<br/>
|
||||||
</td>
|
</td>
|
||||||
<td>false</td>
|
<td>false</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td><b><a href="#proxyclassspectailscale">tailscale</a></b></td>
|
||||||
|
<td>object</td>
|
||||||
|
<td>
|
||||||
|
TailscaleConfig contains options to configure the tailscale-specific parameters of proxies.<br/>
|
||||||
|
</td>
|
||||||
|
<td>false</td>
|
||||||
</tr></tbody>
|
</tr></tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
@ -3302,6 +3309,33 @@ The pod this Toleration is attached to tolerates any taint that matches the trip
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
### ProxyClass.spec.tailscale
|
||||||
|
<sup><sup>[↩ Parent](#proxyclassspec)</sup></sup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TailscaleConfig contains options to configure the tailscale-specific parameters of proxies.
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Required</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody><tr>
|
||||||
|
<td><b>acceptRoutes</b></td>
|
||||||
|
<td>boolean</td>
|
||||||
|
<td>
|
||||||
|
AcceptRoutes can be set to true to make the proxy instance accept routes advertized by other nodes on the tailnet, such as subnet routes. This is equivalent of passing --accept-routes flag to a tailscale Linux client. https://tailscale.com/kb/1019/subnets#use-your-subnet-routes-from-other-machines Defaults to false.<br/>
|
||||||
|
</td>
|
||||||
|
<td>false</td>
|
||||||
|
</tr></tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
### ProxyClass.status
|
### ProxyClass.status
|
||||||
<sup><sup>[↩ Parent](#proxyclass)</sup></sup>
|
<sup><sup>[↩ Parent](#proxyclass)</sup></sup>
|
||||||
|
|
||||||
|
@ -62,6 +62,20 @@ type ProxyClassSpec struct {
|
|||||||
// recommend that you use those for debugging purposes.
|
// recommend that you use those for debugging purposes.
|
||||||
// +optional
|
// +optional
|
||||||
Metrics *Metrics `json:"metrics,omitempty"`
|
Metrics *Metrics `json:"metrics,omitempty"`
|
||||||
|
// TailscaleConfig contains options to configure the tailscale-specific
|
||||||
|
// parameters of proxies.
|
||||||
|
// +optional
|
||||||
|
TailscaleConfig *TailscaleConfig `json:"tailscale,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TailscaleConfig struct {
|
||||||
|
// AcceptRoutes can be set to true to make the proxy instance accept
|
||||||
|
// routes advertized by other nodes on the tailnet, such as subnet
|
||||||
|
// routes.
|
||||||
|
// This is equivalent of passing --accept-routes flag to a tailscale Linux client.
|
||||||
|
// https://tailscale.com/kb/1019/subnets#use-your-subnet-routes-from-other-machines
|
||||||
|
// Defaults to false.
|
||||||
|
AcceptRoutes bool `json:"acceptRoutes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type StatefulSet struct {
|
type StatefulSet struct {
|
||||||
|
@ -489,6 +489,11 @@ func (in *ProxyClassSpec) DeepCopyInto(out *ProxyClassSpec) {
|
|||||||
*out = new(Metrics)
|
*out = new(Metrics)
|
||||||
**out = **in
|
**out = **in
|
||||||
}
|
}
|
||||||
|
if in.TailscaleConfig != nil {
|
||||||
|
in, out := &in.TailscaleConfig, &out.TailscaleConfig
|
||||||
|
*out = new(TailscaleConfig)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyClassSpec.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyClassSpec.
|
||||||
@ -614,3 +619,18 @@ func (in Tags) DeepCopy() Tags {
|
|||||||
in.DeepCopyInto(out)
|
in.DeepCopyInto(out)
|
||||||
return *out
|
return *out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *TailscaleConfig) DeepCopyInto(out *TailscaleConfig) {
|
||||||
|
*out = *in
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TailscaleConfig.
|
||||||
|
func (in *TailscaleConfig) DeepCopy() *TailscaleConfig {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(TailscaleConfig)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user