diff --git a/cmd/k8s-operator/api-server-proxy-pg.go b/cmd/k8s-operator/api-server-proxy-pg.go index ac2f73577..37260c7a0 100644 --- a/cmd/k8s-operator/api-server-proxy-pg.go +++ b/cmd/k8s-operator/api-server-proxy-pg.go @@ -436,14 +436,16 @@ func exclusiveOwnerAnnotations(pg *tsapi.ProxyGroup, operatorID string, svc *tai } if svc == nil { c := ownerAnnotationValue{OwnerRefs: []OwnerRef{ref}} - json, err := json.Marshal(c) + data, err := json.Marshal(c) if err != nil { - return nil, fmt.Errorf("[unexpected] unable to marshal Tailscale Service's owner annotation contents: %w, please report this", err) + return nil, fmt.Errorf("failed to marshal Tailscale Service's owner annotation contents: %w", err) } + return map[string]string{ - ownerAnnotation: string(json), + ownerAnnotation: string(data), }, nil } + o, err := parseOwnerAnnotation(svc) if err != nil { return nil, err @@ -451,15 +453,19 @@ func exclusiveOwnerAnnotations(pg *tsapi.ProxyGroup, operatorID string, svc *tai if o == nil || len(o.OwnerRefs) == 0 { return nil, fmt.Errorf("Tailscale Service %s exists, but does not contain owner annotation with owner references; not proceeding as this is likely a resource created by something other than the Tailscale Kubernetes operator", svc.Name) } + if len(o.OwnerRefs) > 1 || o.OwnerRefs[0].OperatorID != operatorID { return nil, fmt.Errorf("Tailscale Service %s is already owned by other operator(s) and cannot be shared across multiple clusters; configure a difference Service name to continue", svc.Name) } + if o.OwnerRefs[0].Resource == nil { return nil, fmt.Errorf("Tailscale Service %s exists, but does not reference an owning resource; not proceeding as this is likely a Service already owned by an Ingress", svc.Name) } + if o.OwnerRefs[0].Resource.Kind != "ProxyGroup" || o.OwnerRefs[0].Resource.UID != string(pg.UID) { return nil, fmt.Errorf("Tailscale Service %s is already owned by another resource: %#v; configure a difference Service name to continue", svc.Name, o.OwnerRefs[0].Resource) } + if o.OwnerRefs[0].Resource.Name != pg.Name { // ProxyGroup name can be updated in place. o.OwnerRefs[0].Resource.Name = pg.Name diff --git a/cmd/k8s-operator/dnsrecords.go b/cmd/k8s-operator/dnsrecords.go index e75bcd4c2..f926dd684 100644 --- a/cmd/k8s-operator/dnsrecords.go +++ b/cmd/k8s-operator/dnsrecords.go @@ -22,6 +22,7 @@ "k8s.io/utils/net" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + operatorutils "tailscale.com/k8s-operator" tsapi "tailscale.com/k8s-operator/apis/v1alpha1" "tailscale.com/util/mak" @@ -281,19 +282,26 @@ func (dnsRR *dnsRecordsReconciler) fqdnForDNSRecord(ctx context.Context, proxySv if err := dnsRR.Get(ctx, parentName, ing); err != nil { return "", err } + if len(ing.Status.LoadBalancer.Ingress) == 0 { return "", nil } + return ing.Status.LoadBalancer.Ingress[0].Hostname, nil } + if isManagedByType(proxySvc, serviceTypeSvc) { - svc := new(corev1.Service) - if err := dnsRR.Get(ctx, parentName, svc); apierrors.IsNotFound(err) { - logger.Infof("[unexpected] parent Service for egress proxy %s not found", proxySvc.Name) + var svc corev1.Service + + err := dnsRR.Get(ctx, parentName, &svc) + switch { + case apierrors.IsNotFound(err): + logger.Warnf("parent Service for egress proxy %q not found", proxySvc.Name) return "", nil - } else if err != nil { + case err != nil: return "", err } + return svc.Annotations[AnnotationTailnetTargetFQDN], nil } return "", nil @@ -303,28 +311,31 @@ func (dnsRR *dnsRecordsReconciler) fqdnForDNSRecord(ctx context.Context, proxySv // ConfigMap. At this point the in-cluster ts.net nameserver is expected to be // successfully created together with the ConfigMap. func (dnsRR *dnsRecordsReconciler) updateDNSConfig(ctx context.Context, update func(*operatorutils.Records)) error { - cm := &corev1.ConfigMap{} - err := dnsRR.Get(ctx, types.NamespacedName{Name: operatorutils.DNSRecordsCMName, Namespace: dnsRR.tsNamespace}, cm) - if apierrors.IsNotFound(err) { - dnsRR.logger.Info("[unexpected] dnsrecords ConfigMap not found in cluster. Not updating DNS records. Please open an issue and attach operator logs.") + var cm corev1.ConfigMap + err := dnsRR.Get(ctx, types.NamespacedName{Name: operatorutils.DNSRecordsCMName, Namespace: dnsRR.tsNamespace}, &cm) + switch { + case apierrors.IsNotFound(err): + dnsRR.logger.Warn("dnsrecords ConfigMap not found in cluster. Not updating DNS records. Please open an issue and attach operator logs.") return nil + case err != nil: + return fmt.Errorf("failed to retrieve dnsrecords ConfigMap: %w", err) } - if err != nil { - return fmt.Errorf("error retrieving dnsrecords ConfigMap: %w", err) - } + dnsRecords := operatorutils.Records{Version: operatorutils.Alpha1Version, IP4: map[string][]string{}} if cm.Data != nil && cm.Data[operatorutils.DNSRecordsCMKey] != "" { - if err := json.Unmarshal([]byte(cm.Data[operatorutils.DNSRecordsCMKey]), &dnsRecords); err != nil { + if err = json.Unmarshal([]byte(cm.Data[operatorutils.DNSRecordsCMKey]), &dnsRecords); err != nil { return err } } + update(&dnsRecords) dnsRecordsBs, err := json.Marshal(dnsRecords) if err != nil { return fmt.Errorf("error marshalling DNS records: %w", err) } + mak.Set(&cm.Data, operatorutils.DNSRecordsCMKey, string(dnsRecordsBs)) - return dnsRR.Update(ctx, cm) + return dnsRR.Update(ctx, &cm) } // isSvcForFQDNEgressProxy returns true if the Service is a headless Service diff --git a/cmd/k8s-operator/egress-eps.go b/cmd/k8s-operator/egress-eps.go index 9f8510165..b47009e36 100644 --- a/cmd/k8s-operator/egress-eps.go +++ b/cmd/k8s-operator/egress-eps.go @@ -91,9 +91,10 @@ func (er *egressEpsReconciler) Reconcile(ctx context.Context, req reconcile.Requ lg.Debugf("No egress config found, likely because ProxyGroup has not been created") return res, nil } + cfg, ok := cfgs[tailnetSvc] if !ok { - lg.Infof("[unexpected] configuration for tailnet service %s not found", tailnetSvc) + lg.Warnf("configuration for tailnet service %q not found", tailnetSvc) return res, nil } @@ -130,11 +131,12 @@ func (er *egressEpsReconciler) Reconcile(ctx context.Context, req reconcile.Requ // run a cleanup for deleted Pods etc. eps.Endpoints = newEndpoints if !reflect.DeepEqual(eps, oldEps) { - lg.Infof("Updating EndpointSlice to ensure traffic is routed to ready proxy Pods") - if err := er.Update(ctx, eps); err != nil { + lg.Info("Updating EndpointSlice to ensure traffic is routed to ready proxy Pods") + if err = er.Update(ctx, eps); err != nil { return res, fmt.Errorf("error updating EndpointSlice: %w", err) } } + return res, nil } @@ -156,59 +158,69 @@ func podIPv4(pod *corev1.Pod) (string, error) { // status written there to the desired service configuration. func (er *egressEpsReconciler) podIsReadyToRouteTraffic(ctx context.Context, pod corev1.Pod, cfg *egressservices.Config, tailnetSvcName string, lg *zap.SugaredLogger) (bool, error) { lg = lg.With("proxy_pod", pod.Name) - lg.Debugf("checking whether proxy is ready to route to egress service") + lg.Debug("checking whether proxy is ready to route to egress service") if !pod.DeletionTimestamp.IsZero() { - lg.Debugf("proxy Pod is being deleted, ignore") + lg.Debug("proxy Pod is being deleted, ignore") return false, nil } + podIP, err := podIPv4(&pod) - if err != nil { + switch { + case err != nil: return false, fmt.Errorf("error determining Pod IP address: %v", err) - } - if podIP == "" { - lg.Infof("[unexpected] Pod does not have an IPv4 address, and IPv6 is not currently supported") + case podIP == "": + lg.Warn("Pod does not have an IPv4 address, and IPv6 is not currently supported") return false, nil } + stateS := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: pod.Name, Namespace: pod.Namespace, }, } + err = er.Get(ctx, client.ObjectKeyFromObject(stateS), stateS) - if apierrors.IsNotFound(err) { - lg.Debugf("proxy does not have a state Secret, waiting...") + switch { + case apierrors.IsNotFound(err): + lg.Debug("proxy does not yet have a state Secret, waiting...") return false, nil + case err != nil: + return false, fmt.Errorf("error retrieving state Secret: %w", err) } - if err != nil { - return false, fmt.Errorf("error getting state Secret: %w", err) - } + svcStatusBS := stateS.Data[egressservices.KeyEgressServices] if len(svcStatusBS) == 0 { - lg.Debugf("proxy's state Secret does not contain egress services status, waiting...") + lg.Debug("proxy's state Secret does not contain egress services status, waiting...") return false, nil } + svcStatus := &egressservices.Status{} - if err := json.Unmarshal(svcStatusBS, svcStatus); err != nil { + if err = json.Unmarshal(svcStatusBS, svcStatus); err != nil { return false, fmt.Errorf("error unmarshalling egress service status: %w", err) } + if !strings.EqualFold(podIP, svcStatus.PodIPv4) { - lg.Infof("proxy's egress service status is for Pod IP %s, current proxy's Pod IP %s, waiting for the proxy to reconfigure...", svcStatus.PodIPv4, podIP) + lg.Infof("proxy's egress service status is for Pod IP %q, current proxy's Pod IP %q, waiting for the proxy to reconfigure...", svcStatus.PodIPv4, podIP) return false, nil } - st, ok := (*svcStatus).Services[tailnetSvcName] + + st, ok := svcStatus.Services[tailnetSvcName] if !ok { lg.Infof("proxy's state Secret does not have egress service status, waiting...") return false, nil } + if !reflect.DeepEqual(cfg.TailnetTarget, st.TailnetTarget) { - lg.Infof("proxy has configured egress service for tailnet target %v, current target is %v, waiting for proxy to reconfigure...", st.TailnetTarget, cfg.TailnetTarget) + lg.Infof("proxy has configured egress service for tailnet target %q, current target is %q, waiting for proxy to reconfigure...", st.TailnetTarget, cfg.TailnetTarget) return false, nil } + if !reflect.DeepEqual(cfg.Ports, st.Ports) { lg.Debugf("proxy has configured egress service for ports %#+v, wants ports %#+v, waiting for proxy to reconfigure", st.Ports, cfg.Ports) return false, nil } - lg.Debugf("proxy is ready to route traffic to egress service") + + lg.Debug("proxy is ready to route traffic to egress service") return true, nil } diff --git a/cmd/k8s-operator/egress-pod-readiness.go b/cmd/k8s-operator/egress-pod-readiness.go index a8f306353..17ba0c5ec 100644 --- a/cmd/k8s-operator/egress-pod-readiness.go +++ b/cmd/k8s-operator/egress-pod-readiness.go @@ -23,6 +23,7 @@ "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + tsapi "tailscale.com/k8s-operator/apis/v1alpha1" "tailscale.com/kube/kubetypes" "tailscale.com/tstime" @@ -87,8 +88,9 @@ func (er *egressPodsReconciler) Reconcile(ctx context.Context, req reconcile.Req lg.Debugf("Pod is being deleted, do nothing") return res, nil } + if pod.Labels[LabelParentType] != proxyTypeProxyGroup { - lg.Infof("[unexpected] reconciler called for a Pod that is not a ProxyGroup Pod") + lg.Warn("reconciler called for a Pod that is not a ProxyGroup Pod") return res, nil } @@ -106,10 +108,12 @@ func (er *egressPodsReconciler) Reconcile(ctx context.Context, req reconcile.Req if err := er.Get(ctx, types.NamespacedName{Name: proxyGroupName}, pg); err != nil { return res, fmt.Errorf("error getting ProxyGroup %q: %w", proxyGroupName, err) } + if pg.Spec.Type != typeEgress { - lg.Infof("[unexpected] reconciler called for %q ProxyGroup Pod", pg.Spec.Type) + lg.Warnf("reconciler called for %q ProxyGroup Pod", pg.Spec.Type) return res, nil } + // Get all ClusterIP Services for all egress targets exposed to cluster via this ProxyGroup. lbls := map[string]string{ kubetypes.LabelManaged: "true", diff --git a/cmd/k8s-operator/egress-services-readiness.go b/cmd/k8s-operator/egress-services-readiness.go index 965dc08f8..54504af98 100644 --- a/cmd/k8s-operator/egress-services-readiness.go +++ b/cmd/k8s-operator/egress-services-readiness.go @@ -20,6 +20,7 @@ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + tsoperator "tailscale.com/k8s-operator" tsapi "tailscale.com/k8s-operator/apis/v1alpha1" "tailscale.com/tstime" @@ -127,11 +128,13 @@ func (esrr *egressSvcsReadinessReconciler) Reconcile(ctx context.Context, req re msg = err.Error() return res, err } + if pod == nil { - lg.Warnf("[unexpected] ProxyGroup is ready, but replica %d was not found", i) + lg.Warnf("ProxyGroup is ready, but replica %d was not found", i) reason, msg = reasonClusterResourcesNotReady, reasonClusterResourcesNotReady return res, nil } + lg.Debugf("looking at Pod with IPs %v", pod.Status.PodIPs) ready := false for _, ep := range eps.Endpoints { @@ -166,9 +169,10 @@ func (esrr *egressSvcsReadinessReconciler) Reconcile(ctx context.Context, req re func endpointReadyForPod(ep *discoveryv1.Endpoint, pod *corev1.Pod, lg *zap.SugaredLogger) bool { podIP, err := podIPv4(pod) if err != nil { - lg.Warnf("[unexpected] error retrieving Pod's IPv4 address: %v", err) + lg.Warnf("error retrieving Pod's IPv4 address: %v", err) return false } + // Currently we only ever set a single address on and Endpoint and nothing else is meant to modify this. if len(ep.Addresses) != 1 { return false diff --git a/cmd/k8s-operator/ingress-for-pg.go b/cmd/k8s-operator/ingress-for-pg.go index d6872f680..6cacec61e 100644 --- a/cmd/k8s-operator/ingress-for-pg.go +++ b/cmd/k8s-operator/ingress-for-pg.go @@ -173,14 +173,14 @@ func (r *HAIngressReconciler) maybeProvision(ctx context.Context, hostname strin logger.Infof("error validating tailscale IngressClass: %v.", err) return false, nil } - // Get and validate ProxyGroup readiness + + // We only act on services that are annotated as using a proxy group. pgName := ing.Annotations[AnnotationProxyGroup] if pgName == "" { - logger.Infof("[unexpected] no ProxyGroup annotation, skipping Tailscale Service provisioning") return false, nil } - logger = logger.With("ProxyGroup", pgName) + logger = logger.With("ProxyGroup", pgName) if !tsoperator.ProxyGroupAvailable(pg) { logger.Infof("ProxyGroup is not (yet) ready") return false, nil @@ -685,9 +685,10 @@ func (r *HAIngressReconciler) validateIngress(ctx context.Context, ing *networki // It is invalid to have multiple Ingress resources for the same Tailscale Service in one cluster. ingList := &networkingv1.IngressList{} if err := r.List(ctx, ingList); err != nil { - errs = append(errs, fmt.Errorf("[unexpected] error listing Ingresses: %w", err)) + errs = append(errs, fmt.Errorf("failed to list ingresses: %w", err)) return errors.Join(errs...) } + for _, i := range ingList.Items { if r.shouldExpose(&i) && hostnameForIngress(&i) == hostname && i.UID != ing.UID { errs = append(errs, fmt.Errorf("found duplicate Ingress %q for hostname %q - multiple Ingresses for the same hostname in the same cluster are not allowed", client.ObjectKeyFromObject(&i), hostname)) @@ -876,14 +877,16 @@ func ownerAnnotations(operatorID string, svc *tailscale.VIPService) (map[string] } if svc == nil { c := ownerAnnotationValue{OwnerRefs: []OwnerRef{ref}} - json, err := json.Marshal(c) + data, err := json.Marshal(c) if err != nil { - return nil, fmt.Errorf("[unexpected] unable to marshal Tailscale Service's owner annotation contents: %w, please report this", err) + return nil, fmt.Errorf("failed to marshal Tailscale Service's owner annotation contents: %w", err) } + return map[string]string{ - ownerAnnotation: string(json), + ownerAnnotation: string(data), }, nil } + o, err := parseOwnerAnnotation(svc) if err != nil { return nil, err diff --git a/cmd/k8s-operator/operator.go b/cmd/k8s-operator/operator.go index 3fc636367..af44dd4f7 100644 --- a/cmd/k8s-operator/operator.go +++ b/cmd/k8s-operator/operator.go @@ -1477,9 +1477,10 @@ func HAIngressesFromSecret(cl client.Client, logger *zap.SugaredLogger) handler. return func(ctx context.Context, o client.Object) []reconcile.Request { secret, ok := o.(*corev1.Secret) if !ok { - logger.Infof("[unexpected] Secret handler triggered for an object that is not a Secret") + logger.Warn("Secret handler triggered for an object that is not a Secret") return nil } + if isTLSSecret(secret) { return []reconcile.Request{ { @@ -1516,15 +1517,16 @@ func HAIngressesFromSecret(cl client.Client, logger *zap.SugaredLogger) handler. } } -// HAServiceFromSecret returns a handler that returns reconcile requests for +// HAServicesFromSecret returns a handler that returns reconcile requests for // all HA Services that should be reconciled in response to a Secret event. func HAServicesFromSecret(cl client.Client, logger *zap.SugaredLogger) handler.MapFunc { return func(ctx context.Context, o client.Object) []reconcile.Request { secret, ok := o.(*corev1.Secret) if !ok { - logger.Infof("[unexpected] Secret handler triggered for an object that is not a Secret") + logger.Warn("Secret handler triggered for an object that is not a Secret") return nil } + if !isPGStateSecret(secret) { return nil } @@ -1556,9 +1558,10 @@ func kubeAPIServerPGsFromSecret(cl client.Client, logger *zap.SugaredLogger) han return func(ctx context.Context, o client.Object) []reconcile.Request { secret, ok := o.(*corev1.Secret) if !ok { - logger.Infof("[unexpected] Secret handler triggered for an object that is not a Secret") + logger.Warn("Secret handler triggered for an object that is not a Secret") return nil } + if secret.ObjectMeta.Labels[kubetypes.LabelManaged] != "true" || secret.ObjectMeta.Labels[LabelParentType] != "proxygroup" { return nil @@ -1594,9 +1597,10 @@ func egressSvcsFromEgressProxyGroup(cl client.Client, logger *zap.SugaredLogger) return func(ctx context.Context, o client.Object) []reconcile.Request { pg, ok := o.(*tsapi.ProxyGroup) if !ok { - logger.Infof("[unexpected] ProxyGroup handler triggered for an object that is not a ProxyGroup") + logger.Warn("ProxyGroup handler triggered for an object that is not a ProxyGroup") return nil } + if pg.Spec.Type != tsapi.ProxyGroupTypeEgress { return nil } @@ -1624,9 +1628,10 @@ func ingressesFromIngressProxyGroup(cl client.Client, logger *zap.SugaredLogger) return func(ctx context.Context, o client.Object) []reconcile.Request { pg, ok := o.(*tsapi.ProxyGroup) if !ok { - logger.Infof("[unexpected] ProxyGroup handler triggered for an object that is not a ProxyGroup") + logger.Warn("ProxyGroup handler triggered for an object that is not a ProxyGroup") return nil } + if pg.Spec.Type != tsapi.ProxyGroupTypeIngress { return nil } @@ -1654,9 +1659,10 @@ func epsFromExternalNameService(cl client.Client, logger *zap.SugaredLogger, ns return func(ctx context.Context, o client.Object) []reconcile.Request { svc, ok := o.(*corev1.Service) if !ok { - logger.Infof("[unexpected] Service handler triggered for an object that is not a Service") + logger.Warn("Service handler triggered for an object that is not a Service") return nil } + if !isEgressSvcForProxyGroup(svc) { return nil } @@ -1683,9 +1689,10 @@ func podsFromEgressEps(cl client.Client, logger *zap.SugaredLogger, ns string) h return func(ctx context.Context, o client.Object) []reconcile.Request { eps, ok := o.(*discoveryv1.EndpointSlice) if !ok { - logger.Infof("[unexpected] EndpointSlice handler triggered for an object that is not a EndpointSlice") + logger.Warn("EndpointSlice handler triggered for an object that is not a EndpointSlice") return nil } + if eps.Labels[labelProxyGroup] == "" { return nil } @@ -1722,18 +1729,21 @@ func proxyClassesWithServiceMonitor(cl client.Client, logger *zap.SugaredLogger) return func(ctx context.Context, o client.Object) []reconcile.Request { crd, ok := o.(*apiextensionsv1.CustomResourceDefinition) if !ok { - logger.Debugf("[unexpected] ServiceMonitor CRD handler received an object that is not a CustomResourceDefinition") + logger.Warn("ServiceMonitor CRD handler received an object that is not a CustomResourceDefinition") return nil } + if crd.Name != serviceMonitorCRD { - logger.Debugf("[unexpected] ServiceMonitor CRD handler received an unexpected CRD %q", crd.Name) + logger.Warnf("ServiceMonitor CRD handler received an unexpected CRD %q", crd.Name) return nil } + pcl := &tsapi.ProxyClassList{} if err := cl.List(ctx, pcl); err != nil { - logger.Debugf("[unexpected] error listing ProxyClasses: %v", err) + logger.Errorf("failed to list ProxyClass resources: %v", err) return nil } + reqs := make([]reconcile.Request, 0) for _, pc := range pcl.Items { if pc.Spec.Metrics != nil && pc.Spec.Metrics.ServiceMonitor != nil && pc.Spec.Metrics.ServiceMonitor.Enable { @@ -1742,6 +1752,7 @@ func proxyClassesWithServiceMonitor(cl client.Client, logger *zap.SugaredLogger) }) } } + return reqs } } @@ -1751,9 +1762,10 @@ func crdTransformer(log *zap.SugaredLogger) toolscache.TransformFunc { return func(o any) (any, error) { crd, ok := o.(*apiextensionsv1.CustomResourceDefinition) if !ok { - log.Infof("[unexpected] CRD transformer called for a non-CRD type") + log.Warn("CRD transformer called for a non-CRD type") return crd, nil } + crd.Spec = apiextensionsv1.CustomResourceDefinitionSpec{} return crd, nil } diff --git a/cmd/k8s-operator/proxyclass.go b/cmd/k8s-operator/proxyclass.go index c0ea46116..1484cf448 100644 --- a/cmd/k8s-operator/proxyclass.go +++ b/cmd/k8s-operator/proxyclass.go @@ -26,6 +26,7 @@ "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + tsoperator "tailscale.com/k8s-operator" tsapi "tailscale.com/k8s-operator/apis/v1alpha1" "tailscale.com/tstime" @@ -170,10 +171,11 @@ func (pcr *ProxyClassReconciler) validate(ctx context.Context, pc *tsapi.ProxyCl } } } + if pc.Spec.Metrics != nil && pc.Spec.Metrics.ServiceMonitor != nil && pc.Spec.Metrics.ServiceMonitor.Enable { found, err := hasServiceMonitorCRD(ctx, pcr.Client) if err != nil { - pcr.logger.Infof("[unexpected]: error retrieving %q CRD: %v", serviceMonitorCRD, err) + pcr.logger.Errorf("error retrieving %q CRD: %v", serviceMonitorCRD, err) // best effort validation - don't error out here } else if !found { msg := fmt.Sprintf("ProxyClass defines that a ServiceMonitor custom resource should be created, but %q CRD was not found", serviceMonitorCRD) diff --git a/cmd/k8s-operator/proxygroup_specs.go b/cmd/k8s-operator/proxygroup_specs.go index 60b4bddd5..cc54656bb 100644 --- a/cmd/k8s-operator/proxygroup_specs.go +++ b/cmd/k8s-operator/proxygroup_specs.go @@ -19,6 +19,7 @@ "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" "sigs.k8s.io/yaml" + tsapi "tailscale.com/k8s-operator/apis/v1alpha1" "tailscale.com/kube/egressservices" "tailscale.com/kube/ingressservices" @@ -74,10 +75,10 @@ func pgStatefulSet(pg *tsapi.ProxyGroup, namespace, image, tsFirewallMode string } // Validate some base assumptions. if len(ss.Spec.Template.Spec.InitContainers) != 1 { - return nil, fmt.Errorf("[unexpected] base proxy config had %d init containers instead of 1", len(ss.Spec.Template.Spec.InitContainers)) + return nil, fmt.Errorf("base proxy config had %d init containers instead of 1", len(ss.Spec.Template.Spec.InitContainers)) } if len(ss.Spec.Template.Spec.Containers) != 1 { - return nil, fmt.Errorf("[unexpected] base proxy config had %d containers instead of 1", len(ss.Spec.Template.Spec.Containers)) + return nil, fmt.Errorf("base proxy config had %d containers instead of 1", len(ss.Spec.Template.Spec.Containers)) } // StatefulSet config. diff --git a/cmd/k8s-operator/sts.go b/cmd/k8s-operator/sts.go index 66c2e917f..daad35aad 100644 --- a/cmd/k8s-operator/sts.go +++ b/cmd/k8s-operator/sts.go @@ -783,7 +783,7 @@ func (r *tailscaleSTSReconciler) reconcileSTS(ctx context.Context, logger *zap.S // No need to error out if now or in future we end up in a // situation where app info cannot be determined for one of the // many proxy configurations that the operator can produce. - logger.Error("[unexpected] unable to determine proxy type") + logger.Error("unable to determine proxy type") } else { container.Env = append(container.Env, corev1.EnvVar{ Name: "TS_INTERNAL_APP", @@ -1321,7 +1321,7 @@ func proxyCapVer(sec *corev1.Secret, podUID string, log *zap.SugaredLogger) tail } capVer, err := strconv.Atoi(string(sec.Data[kubetypes.KeyCapVer])) if err != nil { - log.Infof("[unexpected]: unexpected capability version in proxy's state Secret, expected an integer, got %q", string(sec.Data[kubetypes.KeyCapVer])) + log.Warnf("unexpected capability version in proxy's state Secret, expected an integer, got %q", string(sec.Data[kubetypes.KeyCapVer])) return tailcfg.CapabilityVersion(-1) } if !strings.EqualFold(podUID, string(sec.Data[kubetypes.KeyPodUID])) { diff --git a/cmd/k8s-operator/svc-for-pg.go b/cmd/k8s-operator/svc-for-pg.go index 29d1a1ebd..08d573f83 100644 --- a/cmd/k8s-operator/svc-for-pg.go +++ b/cmd/k8s-operator/svc-for-pg.go @@ -99,7 +99,6 @@ func (r *HAServiceReconciler) Reconcile(ctx context.Context, req reconcile.Reque pgName := svc.Annotations[AnnotationProxyGroup] if pgName == "" { - logger.Infof("[unexpected] no ProxyGroup annotation, skipping Tailscale Service provisioning") return res, nil } @@ -674,7 +673,7 @@ func (r *HAServiceReconciler) maybeUpdateAdvertiseServicesConfig(ctx context.Con case shouldBeAdvertised: replicaName, ok := strings.CutSuffix(secret.Name, "-config") if !ok { - logger.Infof("[unexpected] unable to determine replica name from config Secret name %q, unable to determine if backend routing has been configured", secret.Name) + logger.Warnf("unable to determine replica name from config Secret name %q, unable to determine if backend routing has been configured", secret.Name) return nil } ready, err := r.backendRoutesSetup(ctx, serviceName.String(), replicaName, cfg, logger) @@ -825,7 +824,7 @@ func (r *HAServiceReconciler) validateService(ctx context.Context, svc *corev1.S } svcList := &corev1.ServiceList{} if err := r.List(ctx, svcList); err != nil { - errs = append(errs, fmt.Errorf("[unexpected] error listing Services: %w", err)) + errs = append(errs, fmt.Errorf("error listing Services: %w", err)) return errors.Join(errs...) } svcName := nameForService(svc) diff --git a/cmd/k8s-operator/svc.go b/cmd/k8s-operator/svc.go index 6f12148c8..198a0c943 100644 --- a/cmd/k8s-operator/svc.go +++ b/cmd/k8s-operator/svc.go @@ -468,7 +468,7 @@ func retrieveClusterDomain(namespace string, logger *zap.SugaredLogger) string { if err != nil { // Vast majority of clusters use the cluster.local domain, so it // is probably better to fall back to that than error out. - logger.Infof("[unexpected] error parsing /etc/resolv.conf to determine cluster domain, defaulting to 'cluster.local'.") + logger.Warn("error parsing /etc/resolv.conf to determine cluster domain, defaulting to 'cluster.local'.") return defaultClusterDomain } return clusterDomainFromResolverConf(conf, namespace, logger) @@ -480,17 +480,17 @@ func retrieveClusterDomain(namespace string, logger *zap.SugaredLogger) string { // If the domains don't match the expected structure or an error is encountered, it defaults to 'cluster.local' domain. func clusterDomainFromResolverConf(conf *resolvconffile.Config, namespace string, logger *zap.SugaredLogger) string { if len(conf.SearchDomains) < 3 { - logger.Infof("[unexpected] resolver config contains only %d search domains, at least three expected.\nDefaulting cluster domain to 'cluster.local'.") + logger.Warnf(" resolver config contains only %d search domains, at least three expected.\nDefaulting cluster domain to 'cluster.local'.", len(conf.SearchDomains)) return defaultClusterDomain } first := conf.SearchDomains[0] if !strings.HasPrefix(string(first), namespace+".svc") { - logger.Infof("[unexpected] first search domain in resolver config is %s; expected %s.\nDefaulting cluster domain to 'cluster.local'.", first, namespace+".svc.") + logger.Warnf("first search domain in resolver config is %s; expected %s.\nDefaulting cluster domain to 'cluster.local'.", first, namespace+".svc.") return defaultClusterDomain } second := conf.SearchDomains[1] if !strings.HasPrefix(string(second), "svc") { - logger.Infof("[unexpected] second search domain in resolver config is %s; expected 'svc.'.\nDefaulting cluster domain to 'cluster.local'.", second) + logger.Warnf("second search domain in resolver config is %s; expected 'svc.'.\nDefaulting cluster domain to 'cluster.local'.", second) return defaultClusterDomain } // Trim the trailing dot for backwards compatibility purposes as the @@ -499,7 +499,7 @@ func clusterDomainFromResolverConf(conf *resolvconffile.Config, namespace string probablyClusterDomain := strings.TrimPrefix(second.WithoutTrailingDot(), "svc.") third := conf.SearchDomains[2] if !strings.EqualFold(third.WithoutTrailingDot(), probablyClusterDomain) { - logger.Infof("[unexpected] expected resolver config to contain serch domains .svc., svc., ; got %s %s %s\n. Defaulting cluster domain to 'cluster.local'.", first, second, third) + logger.Warnf("expected resolver config to contain serch domains .svc., svc., ; got %s %s %s\n. Defaulting cluster domain to 'cluster.local'.", first, second, third) return defaultClusterDomain } logger.Infof("Cluster domain %q extracted from resolver config", probablyClusterDomain)