mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-01 17:49:02 +00:00
cmd/k8s-operator,k8s-operator,go.{mod,sum}: publish proxy status condition for annotated services (#12463)
Adds a new TailscaleProxyReady condition type for use in corev1.Service conditions. Also switch our CRDs to use metav1.Condition instead of ConnectorCondition. The Go structs are seralized identically, but it updates some descriptions and validation rules. Update k8s controller-tools and controller-runtime deps to fix the documentation generation for metav1.Condition so that it excludes comments and TODOs. Stop expecting the fake client to populate TypeMeta in tests. See kubernetes-sigs/controller-runtime#2633 for details of the change. Finally, make some minor improvements to validation for service hostnames. Fixes #12216 Co-authored-by: Irbe Krumina <irbe@tailscale.com> Signed-off-by: Tom Proctor <tomhjp@users.noreply.github.com>
This commit is contained in:
1583
k8s-operator/api.md
1583
k8s-operator/api.md
File diff suppressed because it is too large
Load Diff
@@ -148,7 +148,7 @@ type ConnectorStatus struct {
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
// +optional
|
||||
Conditions []ConnectorCondition `json:"conditions"`
|
||||
Conditions []metav1.Condition `json:"conditions"`
|
||||
// SubnetRoutes are the routes currently exposed to tailnet via this
|
||||
// Connector instance.
|
||||
// +optional
|
||||
@@ -167,42 +167,10 @@ type ConnectorStatus struct {
|
||||
Hostname string `json:"hostname,omitempty"`
|
||||
}
|
||||
|
||||
// ConnectorCondition contains condition information for a Connector.
|
||||
type ConnectorCondition struct {
|
||||
// Type of the condition, known values are (`SubnetRouterReady`).
|
||||
Type ConnectorConditionType `json:"type"`
|
||||
|
||||
// Status of the condition, one of ('True', 'False', 'Unknown').
|
||||
Status metav1.ConditionStatus `json:"status"`
|
||||
|
||||
// LastTransitionTime is the timestamp corresponding to the last status
|
||||
// change of this condition.
|
||||
// +optional
|
||||
LastTransitionTime *metav1.Time `json:"lastTransitionTime,omitempty"`
|
||||
|
||||
// Reason is a brief machine readable explanation for the condition's last
|
||||
// transition.
|
||||
// +optional
|
||||
Reason string `json:"reason,omitempty"`
|
||||
|
||||
// Message is a human readable description of the details of the last
|
||||
// transition, complementing reason.
|
||||
// +optional
|
||||
Message string `json:"message,omitempty"`
|
||||
|
||||
// If set, this represents the .metadata.generation that the condition was
|
||||
// set based upon.
|
||||
// For instance, if .metadata.generation is currently 12, but the
|
||||
// .status.condition[x].observedGeneration is 9, the condition is out of date
|
||||
// with respect to the current state of the Connector.
|
||||
// +optional
|
||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
||||
}
|
||||
|
||||
// ConnectorConditionType represents a Connector condition type.
|
||||
type ConnectorConditionType string
|
||||
type ConditionType string
|
||||
|
||||
const (
|
||||
ConnectorReady ConnectorConditionType = `ConnectorReady`
|
||||
ProxyClassready ConnectorConditionType = `ProxyClassReady`
|
||||
ConnectorReady ConditionType = `ConnectorReady`
|
||||
ProxyClassready ConditionType = `ProxyClassReady`
|
||||
ProxyReady ConditionType = `TailscaleProxyReady` // a Tailscale-specific condition type for corev1.Service
|
||||
)
|
||||
|
||||
@@ -232,5 +232,5 @@ type ProxyClassStatus struct {
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
// +optional
|
||||
Conditions []ConnectorCondition `json:"conditions,omitempty"`
|
||||
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ type DNSConfigStatus struct {
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
// +optional
|
||||
Conditions []ConnectorCondition `json:"conditions"`
|
||||
Conditions []metav1.Condition `json:"conditions"`
|
||||
// Nameserver describes the status of nameserver cluster resources.
|
||||
// +optional
|
||||
Nameserver *NameserverStatus `json:"nameserver"`
|
||||
@@ -115,4 +115,4 @@ type NameserverStatus struct {
|
||||
|
||||
// NameserverReady is set to True if the nameserver has been successfully
|
||||
// deployed to cluster.
|
||||
const NameserverReady ConnectorConditionType = `NameserverReady`
|
||||
const NameserverReady ConditionType = `NameserverReady`
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
@@ -39,25 +40,6 @@ func (in *Connector) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ConnectorCondition) DeepCopyInto(out *ConnectorCondition) {
|
||||
*out = *in
|
||||
if in.LastTransitionTime != nil {
|
||||
in, out := &in.LastTransitionTime, &out.LastTransitionTime
|
||||
*out = (*in).DeepCopy()
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConnectorCondition.
|
||||
func (in *ConnectorCondition) DeepCopy() *ConnectorCondition {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ConnectorCondition)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ConnectorList) DeepCopyInto(out *ConnectorList) {
|
||||
*out = *in
|
||||
@@ -120,7 +102,7 @@ func (in *ConnectorStatus) DeepCopyInto(out *ConnectorStatus) {
|
||||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]ConnectorCondition, len(*in))
|
||||
*out = make([]v1.Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
@@ -153,7 +135,7 @@ func (in *Container) DeepCopyInto(out *Container) {
|
||||
in.Resources.DeepCopyInto(&out.Resources)
|
||||
if in.SecurityContext != nil {
|
||||
in, out := &in.SecurityContext, &out.SecurityContext
|
||||
*out = new(v1.SecurityContext)
|
||||
*out = new(corev1.SecurityContext)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
@@ -252,7 +234,7 @@ func (in *DNSConfigStatus) DeepCopyInto(out *DNSConfigStatus) {
|
||||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]ConnectorCondition, len(*in))
|
||||
*out = make([]v1.Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
@@ -373,7 +355,7 @@ func (in *Pod) DeepCopyInto(out *Pod) {
|
||||
}
|
||||
if in.Affinity != nil {
|
||||
in, out := &in.Affinity, &out.Affinity
|
||||
*out = new(v1.Affinity)
|
||||
*out = new(corev1.Affinity)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.TailscaleContainer != nil {
|
||||
@@ -388,12 +370,12 @@ func (in *Pod) DeepCopyInto(out *Pod) {
|
||||
}
|
||||
if in.SecurityContext != nil {
|
||||
in, out := &in.SecurityContext, &out.SecurityContext
|
||||
*out = new(v1.PodSecurityContext)
|
||||
*out = new(corev1.PodSecurityContext)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ImagePullSecrets != nil {
|
||||
in, out := &in.ImagePullSecrets, &out.ImagePullSecrets
|
||||
*out = make([]v1.LocalObjectReference, len(*in))
|
||||
*out = make([]corev1.LocalObjectReference, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.NodeSelector != nil {
|
||||
@@ -405,7 +387,7 @@ func (in *Pod) DeepCopyInto(out *Pod) {
|
||||
}
|
||||
if in.Tolerations != nil {
|
||||
in, out := &in.Tolerations, &out.Tolerations
|
||||
*out = make([]v1.Toleration, len(*in))
|
||||
*out = make([]corev1.Toleration, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
@@ -516,7 +498,7 @@ func (in *ProxyClassStatus) DeepCopyInto(out *ProxyClassStatus) {
|
||||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]ConnectorCondition, len(*in))
|
||||
*out = make([]v1.Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"go.uber.org/zap"
|
||||
xslices "golang.org/x/exp/slices"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
tsapi "tailscale.com/k8s-operator/apis/v1alpha1"
|
||||
"tailscale.com/tstime"
|
||||
@@ -19,22 +20,22 @@ import (
|
||||
// SetConnectorCondition ensures that Connector status has a condition with the
|
||||
// given attributes. LastTransitionTime gets set every time condition's status
|
||||
// changes.
|
||||
func SetConnectorCondition(cn *tsapi.Connector, conditionType tsapi.ConnectorConditionType, status metav1.ConditionStatus, reason, message string, gen int64, clock tstime.Clock, logger *zap.SugaredLogger) {
|
||||
func SetConnectorCondition(cn *tsapi.Connector, conditionType tsapi.ConditionType, status metav1.ConditionStatus, reason, message string, gen int64, clock tstime.Clock, logger *zap.SugaredLogger) {
|
||||
conds := updateCondition(cn.Status.Conditions, conditionType, status, reason, message, gen, clock, logger)
|
||||
cn.Status.Conditions = conds
|
||||
}
|
||||
|
||||
// RemoveConnectorCondition will remove condition of the given type if it exists.
|
||||
func RemoveConnectorCondition(conn *tsapi.Connector, conditionType tsapi.ConnectorConditionType) {
|
||||
conn.Status.Conditions = slices.DeleteFunc(conn.Status.Conditions, func(cond tsapi.ConnectorCondition) bool {
|
||||
return cond.Type == conditionType
|
||||
func RemoveConnectorCondition(conn *tsapi.Connector, conditionType tsapi.ConditionType) {
|
||||
conn.Status.Conditions = slices.DeleteFunc(conn.Status.Conditions, func(cond metav1.Condition) bool {
|
||||
return cond.Type == string(conditionType)
|
||||
})
|
||||
}
|
||||
|
||||
// SetProxyClassCondition ensures that ProxyClass status has a condition with the
|
||||
// given attributes. LastTransitionTime gets set every time condition's status
|
||||
// changes.
|
||||
func SetProxyClassCondition(pc *tsapi.ProxyClass, conditionType tsapi.ConnectorConditionType, status metav1.ConditionStatus, reason, message string, gen int64, clock tstime.Clock, logger *zap.SugaredLogger) {
|
||||
func SetProxyClassCondition(pc *tsapi.ProxyClass, conditionType tsapi.ConditionType, status metav1.ConditionStatus, reason, message string, gen int64, clock tstime.Clock, logger *zap.SugaredLogger) {
|
||||
conds := updateCondition(pc.Status.Conditions, conditionType, status, reason, message, gen, clock, logger)
|
||||
pc.Status.Conditions = conds
|
||||
}
|
||||
@@ -42,14 +43,29 @@ func SetProxyClassCondition(pc *tsapi.ProxyClass, conditionType tsapi.ConnectorC
|
||||
// SetDNSConfigCondition ensures that DNSConfig status has a condition with the
|
||||
// given attributes. LastTransitionTime gets set every time condition's status
|
||||
// changes
|
||||
func SetDNSConfigCondition(dnsCfg *tsapi.DNSConfig, conditionType tsapi.ConnectorConditionType, status metav1.ConditionStatus, reason, message string, gen int64, clock tstime.Clock, logger *zap.SugaredLogger) {
|
||||
func SetDNSConfigCondition(dnsCfg *tsapi.DNSConfig, conditionType tsapi.ConditionType, status metav1.ConditionStatus, reason, message string, gen int64, clock tstime.Clock, logger *zap.SugaredLogger) {
|
||||
conds := updateCondition(dnsCfg.Status.Conditions, conditionType, status, reason, message, gen, clock, logger)
|
||||
dnsCfg.Status.Conditions = conds
|
||||
}
|
||||
|
||||
func updateCondition(conds []tsapi.ConnectorCondition, conditionType tsapi.ConnectorConditionType, status metav1.ConditionStatus, reason, message string, gen int64, clock tstime.Clock, logger *zap.SugaredLogger) []tsapi.ConnectorCondition {
|
||||
newCondition := tsapi.ConnectorCondition{
|
||||
Type: conditionType,
|
||||
// SetServiceCondition ensures that Service status has a condition with the
|
||||
// given attributes. LastTransitionTime gets set every time condition's status
|
||||
// changes.
|
||||
func SetServiceCondition(svc *corev1.Service, conditionType tsapi.ConditionType, status metav1.ConditionStatus, reason, message string, clock tstime.Clock, logger *zap.SugaredLogger) {
|
||||
conds := updateCondition(svc.Status.Conditions, conditionType, status, reason, message, 0, clock, logger)
|
||||
svc.Status.Conditions = conds
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return cond.Type == string(conditionType)
|
||||
})
|
||||
}
|
||||
|
||||
func updateCondition(conds []metav1.Condition, conditionType tsapi.ConditionType, status metav1.ConditionStatus, reason, message string, gen int64, clock tstime.Clock, logger *zap.SugaredLogger) []metav1.Condition {
|
||||
newCondition := metav1.Condition{
|
||||
Type: string(conditionType),
|
||||
Status: status,
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
@@ -57,10 +73,10 @@ func updateCondition(conds []tsapi.ConnectorCondition, conditionType tsapi.Conne
|
||||
}
|
||||
|
||||
nowTime := metav1.NewTime(clock.Now().Truncate(time.Second))
|
||||
newCondition.LastTransitionTime = &nowTime
|
||||
newCondition.LastTransitionTime = nowTime
|
||||
|
||||
idx := xslices.IndexFunc(conds, func(cond tsapi.ConnectorCondition) bool {
|
||||
return cond.Type == conditionType
|
||||
idx := xslices.IndexFunc(conds, func(cond metav1.Condition) bool {
|
||||
return cond.Type == string(conditionType)
|
||||
})
|
||||
|
||||
if idx == -1 {
|
||||
@@ -82,8 +98,8 @@ func updateCondition(conds []tsapi.ConnectorCondition, conditionType tsapi.Conne
|
||||
}
|
||||
|
||||
func ProxyClassIsReady(pc *tsapi.ProxyClass) bool {
|
||||
idx := xslices.IndexFunc(pc.Status.Conditions, func(cond tsapi.ConnectorCondition) bool {
|
||||
return cond.Type == tsapi.ProxyClassready
|
||||
idx := xslices.IndexFunc(pc.Status.Conditions, func(cond metav1.Condition) bool {
|
||||
return cond.Type == string(tsapi.ProxyClassready)
|
||||
})
|
||||
if idx == -1 {
|
||||
return false
|
||||
@@ -93,8 +109,8 @@ func ProxyClassIsReady(pc *tsapi.ProxyClass) bool {
|
||||
}
|
||||
|
||||
func DNSCfgIsReady(cfg *tsapi.DNSConfig) bool {
|
||||
idx := xslices.IndexFunc(cfg.Status.Conditions, func(cond tsapi.ConnectorCondition) bool {
|
||||
return cond.Type == tsapi.NameserverReady
|
||||
idx := xslices.IndexFunc(cfg.Status.Conditions, func(cond metav1.Condition) bool {
|
||||
return cond.Type == string(tsapi.NameserverReady)
|
||||
})
|
||||
if idx == -1 {
|
||||
return false
|
||||
|
||||
@@ -28,14 +28,14 @@ func TestSetConnectorCondition(t *testing.T) {
|
||||
SetConnectorCondition(&cn, tsapi.ConnectorReady, metav1.ConditionTrue, "someReason", "someMsg", 1, clock, zl.Sugar())
|
||||
assert.Equal(t, cn, tsapi.Connector{
|
||||
Status: tsapi.ConnectorStatus{
|
||||
Conditions: []tsapi.ConnectorCondition{
|
||||
Conditions: []metav1.Condition{
|
||||
{
|
||||
Type: tsapi.ConnectorReady,
|
||||
Type: string(tsapi.ConnectorReady),
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "someReason",
|
||||
Message: "someMsg",
|
||||
ObservedGeneration: 1,
|
||||
LastTransitionTime: &fakeNow,
|
||||
LastTransitionTime: fakeNow,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -43,28 +43,28 @@ func TestSetConnectorCondition(t *testing.T) {
|
||||
|
||||
// Modify status of an existing condition
|
||||
cn.Status = tsapi.ConnectorStatus{
|
||||
Conditions: []tsapi.ConnectorCondition{
|
||||
Conditions: []metav1.Condition{
|
||||
{
|
||||
Type: tsapi.ConnectorReady,
|
||||
Type: string(tsapi.ConnectorReady),
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "someReason",
|
||||
Message: "someMsg",
|
||||
ObservedGeneration: 1,
|
||||
LastTransitionTime: &fakePast,
|
||||
LastTransitionTime: fakePast,
|
||||
},
|
||||
},
|
||||
}
|
||||
SetConnectorCondition(&cn, tsapi.ConnectorReady, metav1.ConditionTrue, "anotherReason", "anotherMsg", 2, clock, zl.Sugar())
|
||||
assert.Equal(t, cn, tsapi.Connector{
|
||||
Status: tsapi.ConnectorStatus{
|
||||
Conditions: []tsapi.ConnectorCondition{
|
||||
Conditions: []metav1.Condition{
|
||||
{
|
||||
Type: tsapi.ConnectorReady,
|
||||
Type: string(tsapi.ConnectorReady),
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "anotherReason",
|
||||
Message: "anotherMsg",
|
||||
ObservedGeneration: 2,
|
||||
LastTransitionTime: &fakeNow,
|
||||
LastTransitionTime: fakeNow,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -72,28 +72,28 @@ func TestSetConnectorCondition(t *testing.T) {
|
||||
|
||||
// Don't modify last transition time if status hasn't changed
|
||||
cn.Status = tsapi.ConnectorStatus{
|
||||
Conditions: []tsapi.ConnectorCondition{
|
||||
Conditions: []metav1.Condition{
|
||||
{
|
||||
Type: tsapi.ConnectorReady,
|
||||
Type: string(tsapi.ConnectorReady),
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "someReason",
|
||||
Message: "someMsg",
|
||||
ObservedGeneration: 1,
|
||||
LastTransitionTime: &fakePast,
|
||||
LastTransitionTime: fakePast,
|
||||
},
|
||||
},
|
||||
}
|
||||
SetConnectorCondition(&cn, tsapi.ConnectorReady, metav1.ConditionTrue, "anotherReason", "anotherMsg", 2, clock, zl.Sugar())
|
||||
assert.Equal(t, cn, tsapi.Connector{
|
||||
Status: tsapi.ConnectorStatus{
|
||||
Conditions: []tsapi.ConnectorCondition{
|
||||
Conditions: []metav1.Condition{
|
||||
{
|
||||
Type: tsapi.ConnectorReady,
|
||||
Type: string(tsapi.ConnectorReady),
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "anotherReason",
|
||||
Message: "anotherMsg",
|
||||
ObservedGeneration: 2,
|
||||
LastTransitionTime: &fakePast,
|
||||
LastTransitionTime: fakePast,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user