mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-22 08:51:41 +00:00

Ensure no services are advertised as part of shutting down tailscaled. Prefs are only edited if services are currently advertised, and they're edited we wait for control's ~15s (+ buffer) delay to failover. Note that editing prefs will trigger a synchronous write to the state Secret, so it may fail to persist state if the ProxyGroup is getting scaled down and therefore has its RBAC deleted at the same time, but that failure doesn't stop prefs being updated within the local backend, doesn't affect connectivity to control, and the state Secret is about to get deleted anyway, so the only negative side effect is a harmless error log during shutdown. Control still learns that the node is no longer advertising the service and triggers the failover. Note that the first version of this used a PreStop lifecycle hook, but that only supports GET methods and we need the shutdown to trigger side effects (updating prefs) so it didn't seem appropriate to expose that functionality on a GET endpoint that's accessible on the k8s network. Updates tailscale/corp#24795 Change-Id: I0a9a4fe7a5395ca76135ceead05cbc3ee32b3d3c Signed-off-by: Tom Proctor <tomhjp@users.noreply.github.com>
58 lines
1.3 KiB
Go
58 lines
1.3 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
//go:build linux
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"sync"
|
|
|
|
"tailscale.com/kube/kubetypes"
|
|
)
|
|
|
|
// healthz is a simple health check server, if enabled it returns 200 OK if
|
|
// this tailscale node currently has at least one tailnet IP address else
|
|
// returns 503.
|
|
type healthz struct {
|
|
sync.Mutex
|
|
hasAddrs bool
|
|
podIPv4 string
|
|
}
|
|
|
|
func (h *healthz) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
h.Lock()
|
|
defer h.Unlock()
|
|
|
|
if h.hasAddrs {
|
|
w.Header().Add(kubetypes.PodIPv4Header, h.podIPv4)
|
|
if _, err := w.Write([]byte("ok")); err != nil {
|
|
http.Error(w, fmt.Sprintf("error writing status: %v", err), http.StatusInternalServerError)
|
|
}
|
|
} else {
|
|
http.Error(w, "node currently has no tailscale IPs", http.StatusServiceUnavailable)
|
|
}
|
|
}
|
|
|
|
func (h *healthz) update(healthy bool) {
|
|
h.Lock()
|
|
defer h.Unlock()
|
|
|
|
if h.hasAddrs != healthy {
|
|
log.Println("Setting healthy", healthy)
|
|
}
|
|
h.hasAddrs = healthy
|
|
}
|
|
|
|
// registerHealthHandlers registers a simple health handler at /healthz.
|
|
// A containerized tailscale instance is considered healthy if
|
|
// it has at least one tailnet IP address.
|
|
func registerHealthHandlers(mux *http.ServeMux, podIPv4 string) *healthz {
|
|
h := &healthz{podIPv4: podIPv4}
|
|
mux.Handle("GET /healthz", h)
|
|
return h
|
|
}
|