mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-01 09:32:08 +00:00
cmd/{k8s-operator,containerboot},k8s-operator,kube: reconcile ExternalName Services for ProxyGroup (#13635)
Adds a new reconciler that reconciles ExternalName Services that define a tailnet target that should be exposed to cluster workloads on a ProxyGroup's proxies. The reconciler ensures that for each such service, the config mounted to the proxies is updated with the tailnet target definition and that and EndpointSlice and ClusterIP Service are created for the service. Adds a new reconciler that ensures that as proxy Pods become ready to route traffic to a tailnet target, the EndpointSlice for the target is updated with the Pods' endpoints. Updates tailscale/tailscale#13406 Signed-off-by: Irbe Krumina <irbe@tailscale.com>
This commit is contained in:
@@ -467,21 +467,6 @@ _Appears in:_
|
||||
| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.3/#condition-v1-meta) array_ | List of status conditions to indicate the status of the ProxyClass.<br />Known condition types are `ProxyClassReady`. | | |
|
||||
|
||||
|
||||
#### ProxyClassType
|
||||
|
||||
_Underlying type:_ _string_
|
||||
|
||||
|
||||
|
||||
_Validation:_
|
||||
- Enum: [egress]
|
||||
- Type: string
|
||||
|
||||
_Appears in:_
|
||||
- [ProxyGroupSpec](#proxygroupspec)
|
||||
|
||||
|
||||
|
||||
#### ProxyGroup
|
||||
|
||||
|
||||
@@ -537,7 +522,7 @@ _Appears in:_
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `type` _[ProxyClassType](#proxyclasstype)_ | Type of the ProxyGroup, either ingress or egress. Each set of proxies<br />managed by a single ProxyGroup definition operate as only ingress or<br />only egress proxies. | | Enum: [egress] <br />Type: string <br /> |
|
||||
| `type` _[ProxyGroupType](#proxygrouptype)_ | Type of the ProxyGroup, either ingress or egress. Each set of proxies<br />managed by a single ProxyGroup definition operate as only ingress or<br />only egress proxies. | | Enum: [egress] <br />Type: string <br /> |
|
||||
| `tags` _[Tags](#tags)_ | Tags that the Tailscale devices will be tagged with. Defaults to [tag:k8s].<br />If you specify custom tags here, make sure you also make the operator<br />an owner of these tags.<br />See https://tailscale.com/kb/1236/kubernetes-operator/#setting-up-the-kubernetes-operator.<br />Tags cannot be changed once a ProxyGroup device has been created.<br />Tag values must be in form ^tag:[a-zA-Z][a-zA-Z0-9-]*$. | | Pattern: `^tag:[a-zA-Z][a-zA-Z0-9-]*$` <br />Type: string <br /> |
|
||||
| `replicas` _integer_ | Replicas specifies how many replicas to create the StatefulSet with.<br />Defaults to 2. | | |
|
||||
| `hostnamePrefix` _[HostnamePrefix](#hostnameprefix)_ | HostnamePrefix is the hostname prefix to use for tailnet devices created<br />by the ProxyGroup. Each device will have the integer number from its<br />StatefulSet pod appended to this prefix to form the full hostname.<br />HostnamePrefix can contain lower case letters, numbers and dashes, it<br />must not start with a dash and must be between 1 and 62 characters long. | | Pattern: `^[a-z0-9][a-z0-9-]{0,61}$` <br />Type: string <br /> |
|
||||
@@ -561,6 +546,21 @@ _Appears in:_
|
||||
| `devices` _[TailnetDevice](#tailnetdevice) array_ | List of tailnet devices associated with the ProxyGroup StatefulSet. | | |
|
||||
|
||||
|
||||
#### ProxyGroupType
|
||||
|
||||
_Underlying type:_ _string_
|
||||
|
||||
|
||||
|
||||
_Validation:_
|
||||
- Enum: [egress]
|
||||
- Type: string
|
||||
|
||||
_Appears in:_
|
||||
- [ProxyGroupSpec](#proxygroupspec)
|
||||
|
||||
|
||||
|
||||
#### Recorder
|
||||
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
&DNSConfigList{},
|
||||
&Recorder{},
|
||||
&RecorderList{},
|
||||
&ProxyGroup{},
|
||||
&ProxyGroupList{},
|
||||
)
|
||||
|
||||
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
|
||||
|
||||
@@ -172,6 +172,14 @@ type ConditionType string
|
||||
const (
|
||||
ConnectorReady ConditionType = `ConnectorReady`
|
||||
ProxyClassready ConditionType = `ProxyClassReady`
|
||||
ProxyGroupReady ConditionType = `ProxyGroupReady`
|
||||
ProxyReady ConditionType = `TailscaleProxyReady` // a Tailscale-specific condition type for corev1.Service
|
||||
RecorderReady ConditionType = `RecorderReady`
|
||||
// EgressSvcValid is set to true if the user configured ExternalName Service for exposing a tailnet target on
|
||||
// ProxyGroup nodes is valid.
|
||||
EgressSvcValid ConditionType = `EgressSvcValid`
|
||||
// EgressSvcConfigured is set to true if the configuration for the egress Service (proxy ConfigMap update,
|
||||
// EndpointSlice for the Service) has been successfully applied. The Reason for this condition
|
||||
// contains the name of the ProxyGroup and the hash of the Service ports and the tailnet target.
|
||||
EgressSvcConfigured ConditionType = `EgressSvcConfigured`
|
||||
)
|
||||
|
||||
@@ -40,7 +40,7 @@ type ProxyGroupSpec struct {
|
||||
// Type of the ProxyGroup, either ingress or egress. Each set of proxies
|
||||
// managed by a single ProxyGroup definition operate as only ingress or
|
||||
// only egress proxies.
|
||||
Type ProxyClassType `json:"type"`
|
||||
Type ProxyGroupType `json:"type"`
|
||||
|
||||
// Tags that the Tailscale devices will be tagged with. Defaults to [tag:k8s].
|
||||
// If you specify custom tags here, make sure you also make the operator
|
||||
@@ -101,10 +101,10 @@ type TailnetDevice struct {
|
||||
|
||||
// +kubebuilder:validation:Type=string
|
||||
// +kubebuilder:validation:Enum=egress
|
||||
type ProxyClassType string
|
||||
type ProxyGroupType string
|
||||
|
||||
const (
|
||||
ProxyClassTypeEgress ProxyClassType = "egress"
|
||||
ProxyGroupTypeEgress ProxyGroupType = "egress"
|
||||
)
|
||||
|
||||
// +kubebuilder:validation:Type=string
|
||||
|
||||
@@ -56,6 +56,18 @@ func SetServiceCondition(svc *corev1.Service, conditionType tsapi.ConditionType,
|
||||
svc.Status.Conditions = conds
|
||||
}
|
||||
|
||||
// GetServiceCondition returns Service condition with the specified type, if it exists on the Service.
|
||||
func GetServiceCondition(svc *corev1.Service, conditionType tsapi.ConditionType) *metav1.Condition {
|
||||
idx := xslices.IndexFunc(svc.Status.Conditions, func(cond metav1.Condition) bool {
|
||||
return cond.Type == string(conditionType)
|
||||
})
|
||||
|
||||
if idx == -1 {
|
||||
return nil
|
||||
}
|
||||
return &svc.Status.Conditions[idx]
|
||||
}
|
||||
|
||||
// RemoveServiceCondition will remove condition of the given type if it exists.
|
||||
func RemoveServiceCondition(svc *corev1.Service, conditionType tsapi.ConditionType) {
|
||||
svc.Status.Conditions = slices.DeleteFunc(svc.Status.Conditions, func(cond metav1.Condition) bool {
|
||||
@@ -63,6 +75,16 @@ func RemoveServiceCondition(svc *corev1.Service, conditionType tsapi.ConditionTy
|
||||
})
|
||||
}
|
||||
|
||||
func EgressServiceIsValidAndConfigured(svc *corev1.Service) bool {
|
||||
for _, typ := range []tsapi.ConditionType{tsapi.EgressSvcValid, tsapi.EgressSvcConfigured} {
|
||||
cond := GetServiceCondition(svc, typ)
|
||||
if cond == nil || cond.Status != metav1.ConditionTrue {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// SetRecorderCondition ensures that Recorder status has a condition with the
|
||||
// given attributes. LastTransitionTime gets set every time condition's status
|
||||
// changes.
|
||||
@@ -116,6 +138,17 @@ func ProxyClassIsReady(pc *tsapi.ProxyClass) bool {
|
||||
return cond.Status == metav1.ConditionTrue && cond.ObservedGeneration == pc.Generation
|
||||
}
|
||||
|
||||
func ProxyGroupIsReady(pg *tsapi.ProxyGroup) bool {
|
||||
idx := xslices.IndexFunc(pg.Status.Conditions, func(cond metav1.Condition) bool {
|
||||
return cond.Type == string(tsapi.ProxyGroupReady)
|
||||
})
|
||||
if idx == -1 {
|
||||
return false
|
||||
}
|
||||
cond := pg.Status.Conditions[idx]
|
||||
return cond.Status == metav1.ConditionTrue && cond.ObservedGeneration == pg.Generation
|
||||
}
|
||||
|
||||
func DNSCfgIsReady(cfg *tsapi.DNSConfig) bool {
|
||||
idx := xslices.IndexFunc(cfg.Status.Conditions, func(cond metav1.Condition) bool {
|
||||
return cond.Type == string(tsapi.NameserverReady)
|
||||
|
||||
Reference in New Issue
Block a user