Remove dead heartbeatv1 code for v19 (#68399)

* Remove HeartbeatV1 fallback for services which have supported HeartbeatV2 since v17.

* Remove heartbeatv1 implementation for services that haven't used it since v17.

* Fix accessPoint interface; Remove dead constants; Add delete in TODO.

* Add inventory control handler to tests.

* Update lib/auth/auth.go

Co-authored-by: rosstimothy <[email protected]>

* Add old Announcer field and comment.

* Fix TestRootUTMPEntryExists.

---------

Co-authored-by: rosstimothy <[email protected]>
This commit is contained in:
Brian Joerger
2026-07-09 23:40:54 +00:00
committed by GitHub
co-authored by rosstimothy
parent 7309862f42
commit 08c06437fc
10 changed files with 110 additions and 356 deletions
+14
View File
@@ -49,6 +49,7 @@ import (
"github.com/gravitational/teleport/lib/authz"
"github.com/gravitational/teleport/lib/bpf"
"github.com/gravitational/teleport/lib/cryptosuites"
"github.com/gravitational/teleport/lib/inventory"
"github.com/gravitational/teleport/lib/reversetunnel"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/srv"
@@ -333,6 +334,18 @@ func newSrvCtx(ctx context.Context, t *testing.T) *SrvCtx {
})
require.NoError(t, err)
inventoryHandle, err := inventory.NewDownstreamHandle(s.nodeClient.InventoryControlStream,
func(_ context.Context) (*proto.UpstreamInventoryHello, error) {
return proto.UpstreamInventoryHello_builder{
ServerID: s.nodeID,
Version: teleport.Version,
Services: []string{string(types.RoleNode)},
Hostname: "test",
}.Build(), nil
})
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, inventoryHandle.Close()) })
nodeDir := t.TempDir()
srv, err := regular.New(
ctx,
@@ -363,6 +376,7 @@ func newSrvCtx(ctx context.Context, t *testing.T) *SrvCtx {
regular.SetLockWatcher(lockWatcher),
regular.SetSessionController(nodeSessionController),
regular.SetConnectedProxyGetter(reversetunnel.NewConnectedProxyGetter()),
regular.SetInventoryControlHandle(inventoryHandle),
)
require.NoError(t, err)
s.srv = srv
+7 -13
View File
@@ -56,10 +56,6 @@ import (
// Announcer specifies interface responsible for announcing presence
type Announcer interface {
// UpsertNode registers node presence, permanently if ttl is 0 or
// for the specified duration with second resolution if it's >= 1 second
UpsertNode(ctx context.Context, s types.Server) (*types.KeepAlive, error)
// UpsertProxyServerWithoutReturn registers proxy presence, permanently if
// ttl is 0 or for the specified duration with second resolution if it's
// >= 1 second. The upserted server is not returned because the HTTP
@@ -73,18 +69,9 @@ type Announcer interface {
// for the specified duration with second resolution if it's >= 1 second
UpsertAuthServer(ctx context.Context, s types.Server) error
// UpsertKubernetesServer registers a kubernetes server
UpsertKubernetesServer(context.Context, types.KubeServer) (*types.KeepAlive, error)
// NewKeepAliver returns a new instance of keep aliver
NewKeepAliver(ctx context.Context) (types.KeepAliver, error)
// UpsertApplicationServer registers an application server.
UpsertApplicationServer(context.Context, types.AppServer) (*types.KeepAlive, error)
// UpsertDatabaseServer registers a database proxy server.
UpsertDatabaseServer(context.Context, types.DatabaseServer) (*types.KeepAlive, error)
// UpsertWindowsDesktopService registers a Windows desktop service.
UpsertWindowsDesktopService(context.Context, types.WindowsDesktopService) (*types.KeepAlive, error)
@@ -116,6 +103,13 @@ type accessPoint interface {
// ConnectionDiagnosticTraceAppender adds a method to append traces into ConnectionDiagnostics.
services.ConnectionDiagnosticTraceAppender
// UpsertNode registers node presence, permanently if ttl is 0 or
// for the specified duration with second resolution if it's >= 1 second
UpsertNode(ctx context.Context, s types.Server) (*types.KeepAlive, error)
// UpsertApplicationServer registers an application server.
UpsertApplicationServer(context.Context, types.AppServer) (*types.KeepAlive, error)
}
// ReadNodeAccessPoint is a read only API interface implemented by a certificate authority (CA) to be
-1
View File
@@ -696,7 +696,6 @@ func (t *TLSServer) getKubeClusterWithServiceLabels(name string) (*types.Kuberne
func (t *TLSServer) startHeartbeat(name string) error {
heartbeat, err := srv.NewKubernetesServerHeartbeat(srv.HeartbeatV2Config[*types.KubernetesServerV3]{
InventoryHandle: t.InventoryHandle,
Announcer: t.TLSServerConfig.AuthClient,
GetResource: func(context.Context) (*types.KubernetesServerV3, error) { return t.GetServerInfo(name) },
OnHeartbeat: t.TLSServerConfig.OnHeartbeat,
})
-5
View File
@@ -407,11 +407,6 @@ func (process *TeleportProcess) runRelayService() error {
GetResource: func(context.Context) (*presencev1.RelayServer, error) {
return relayServer.Load(), nil
},
// there's no fallback announce mode, the relay service only works with
// clusters recent enough to support relay heartbeats through the ICS
Announcer: nil,
OnHeartbeat: process.OnHeartbeat(teleport.ComponentRelay),
}, sublogger("heartbeat"))
if err != nil {
+2 -113
View File
@@ -88,7 +88,7 @@ type HeartbeatMode int
// CheckAndSetDefaults checks values and sets defaults
func (h HeartbeatMode) CheckAndSetDefaults() error {
switch h {
case HeartbeatModeNode, HeartbeatModeProxy, HeartbeatModeAuth, HeartbeatModeKube, HeartbeatModeApp, HeartbeatModeDB, HeartbeatModeDatabaseService, HeartbeatModeWindowsDesktopService, HeartbeatModeWindowsDesktop:
case HeartbeatModeProxy, HeartbeatModeAuth, HeartbeatModeDatabaseService, HeartbeatModeWindowsDesktopService, HeartbeatModeWindowsDesktop:
return nil
default:
return trace.BadParameter("unrecognized mode")
@@ -98,18 +98,10 @@ func (h HeartbeatMode) CheckAndSetDefaults() error {
// String returns user-friendly representation of the mode
func (h HeartbeatMode) String() string {
switch h {
case HeartbeatModeNode:
return "Node"
case HeartbeatModeProxy:
return "Proxy"
case HeartbeatModeAuth:
return "Auth"
case HeartbeatModeKube:
return "Kube"
case HeartbeatModeApp:
return "App"
case HeartbeatModeDB:
return "Database"
case HeartbeatModeDatabaseService:
return "DatabaseService"
case HeartbeatModeWindowsDesktopService:
@@ -122,21 +114,12 @@ func (h HeartbeatMode) String() string {
}
const (
// HeartbeatModeNode sets heartbeat to node
// updates that support keep alives
HeartbeatModeNode HeartbeatMode = iota
// HeartbeatModeProxy sets heartbeat to proxy
// that does not support keep alives
HeartbeatModeProxy
HeartbeatModeProxy HeartbeatMode = iota
// HeartbeatModeAuth sets heartbeat to auth
// that does not support keep alives
HeartbeatModeAuth
// HeartbeatModeKube is a mode for kubernetes service heartbeats.
HeartbeatModeKube
// HeartbeatModeApp sets heartbeat to apps and will use keep alives.
HeartbeatModeApp
// HeartbeatModeDB sets heartbeat to db
HeartbeatModeDB
// HeartbeatModeDatabaseService sets heartbeat mode to DatabaseService.
HeartbeatModeDatabaseService
// HeartbeatModeWindowsDesktopService sets heartbeat mode to windows desktop
@@ -438,100 +421,6 @@ func (h *Heartbeat) announce() (doneSomething bool, _ error) {
h.notifySend()
h.setState(HeartbeatStateAnnounceWait)
return true, nil
case HeartbeatModeNode:
node, ok := h.current.(types.Server)
if !ok {
return false, trace.BadParameter("expected services.Server, got %#v", h.current)
}
keepAlive, err := h.Announcer.UpsertNode(h.cancelCtx, node)
if err != nil {
return false, trace.Wrap(err)
}
h.notifySend()
keepAliver, err := h.Announcer.NewKeepAliver(h.cancelCtx)
if err != nil {
h.reset(HeartbeatStateInit)
return false, trace.Wrap(err)
}
h.nextAnnounce = h.Clock.Now().UTC().Add(h.AnnouncePeriod)
h.nextKeepAlive = h.Clock.Now().UTC().Add(h.KeepAlivePeriod)
h.keepAlive = keepAlive
h.keepAliver = keepAliver
h.setState(HeartbeatStateKeepAliveWait)
return true, nil
case HeartbeatModeKube:
var (
keepAlive *types.KeepAlive
err error
)
switch current := h.current.(type) {
case types.KubeServer:
keepAlive, err = h.Announcer.UpsertKubernetesServer(h.cancelCtx, current)
if err != nil {
return false, trace.Wrap(err)
}
default:
return false, trace.BadParameter("expected types.KubeServer, got %#v", h.current)
}
h.notifySend()
keepAliver, err := h.Announcer.NewKeepAliver(h.cancelCtx)
if err != nil {
h.reset(HeartbeatStateInit)
return false, trace.Wrap(err)
}
h.nextAnnounce = h.Clock.Now().UTC().Add(h.AnnouncePeriod)
h.nextKeepAlive = h.Clock.Now().UTC().Add(h.KeepAlivePeriod)
h.keepAlive = keepAlive
h.keepAliver = keepAliver
h.setState(HeartbeatStateKeepAliveWait)
return true, nil
case HeartbeatModeApp:
var keepAlive *types.KeepAlive
var err error
switch current := h.current.(type) {
case types.AppServer:
keepAlive, err = h.Announcer.UpsertApplicationServer(h.cancelCtx, current)
default:
return false, trace.BadParameter("expected types.AppServer, got %#v", h.current)
}
if err != nil {
return false, trace.Wrap(err)
}
h.notifySend()
keepAliver, err := h.Announcer.NewKeepAliver(h.cancelCtx)
if err != nil {
h.reset(HeartbeatStateInit)
return false, trace.Wrap(err)
}
h.nextAnnounce = h.Clock.Now().UTC().Add(h.AnnouncePeriod)
h.nextKeepAlive = h.Clock.Now().UTC().Add(h.KeepAlivePeriod)
h.keepAlive = keepAlive
h.keepAliver = keepAliver
h.setState(HeartbeatStateKeepAliveWait)
return true, nil
case HeartbeatModeDB:
db, ok := h.current.(types.DatabaseServer)
if !ok {
return false, trace.BadParameter("expected services.DatabaseServer, got %#v", h.current)
}
keepAlive, err := h.Announcer.UpsertDatabaseServer(h.cancelCtx, db)
if err != nil {
return false, trace.Wrap(err)
}
h.notifySend()
keepAliver, err := h.Announcer.NewKeepAliver(h.cancelCtx)
if err != nil {
h.reset(HeartbeatStateInit)
return false, trace.Wrap(err)
}
h.nextAnnounce = h.Clock.Now().UTC().Add(h.AnnouncePeriod)
h.nextKeepAlive = h.Clock.Now().UTC().Add(h.KeepAlivePeriod)
h.keepAlive = keepAlive
h.keepAliver = keepAliver
h.setState(HeartbeatStateKeepAliveWait)
return true, nil
case HeartbeatModeWindowsDesktopService:
wd, ok := h.current.(types.WindowsDesktopService)
if !ok {
+22 -102
View File
@@ -29,10 +29,10 @@ import (
apidefaults "github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/defaults"
)
// TestHeartbeatKeepAlive tests keep alive cycle used for nodes and apps.
// TestHeartbeatKeepAlive tests keep alive cycle used for database services and
// windows desktop services.
func TestHeartbeatKeepAlive(t *testing.T) {
t.Parallel()
@@ -41,60 +41,6 @@ func TestHeartbeatKeepAlive(t *testing.T) {
mode HeartbeatMode
makeServer func() types.Resource
}{
{
name: "keep alive node",
mode: HeartbeatModeNode,
makeServer: func() types.Resource {
return &types.ServerV2{
Kind: types.KindNode,
Version: types.V2,
Metadata: types.Metadata{
Namespace: apidefaults.Namespace,
Name: "1",
},
Spec: types.ServerSpecV2{
Addr: "127.0.0.1:1234",
Hostname: "2",
},
}
},
},
{
name: "keep alive app server",
mode: HeartbeatModeApp,
makeServer: func() types.Resource {
return &types.AppServerV3{
Kind: types.KindAppServer,
Version: types.V3,
Metadata: types.Metadata{
Namespace: apidefaults.Namespace,
Name: "1",
},
Spec: types.AppServerSpecV3{
App: &types.AppV3{},
Hostname: "2",
},
}
},
},
{
name: "keep alive database server",
mode: HeartbeatModeDB,
makeServer: func() types.Resource {
return &types.DatabaseServerV3{
Kind: types.KindDatabaseServer,
Version: types.V3,
Metadata: types.Metadata{
Namespace: apidefaults.Namespace,
Name: "db-1",
},
Spec: types.DatabaseServerSpecV3{
Database: mustCreateDatabase(t, "db-1", defaults.ProtocolPostgres, "127.0.0.1:1234"),
Hostname: "2",
},
}
},
},
{
name: "keep alive database service",
mode: HeartbeatModeDatabaseService,
@@ -116,6 +62,26 @@ func TestHeartbeatKeepAlive(t *testing.T) {
}
},
},
{
name: "keep alive windows desktop service",
mode: HeartbeatModeWindowsDesktopService,
makeServer: func() types.Resource {
return &types.WindowsDesktopServiceV3{
ResourceHeader: types.ResourceHeader{
Kind: types.KindWindowsDesktopService,
Version: types.V3,
Metadata: types.Metadata{
Name: "1",
Namespace: apidefaults.Namespace,
},
},
Spec: types.WindowsDesktopServiceSpecV3{
Addr: "127.0.0.1:1234",
TeleportVersion: "1.2.3",
},
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -217,20 +183,6 @@ func TestHeartbeatKeepAlive(t *testing.T) {
}
}
func mustCreateDatabase(t *testing.T, name, protocol, uri string) *types.DatabaseV3 {
database, err := types.NewDatabaseV3(
types.Metadata{
Name: name,
},
types.DatabaseSpecV3{
Protocol: protocol,
URI: uri,
},
)
require.NoError(t, err)
return database
}
// TestHeartbeatAnnounce tests announce cycles used for proxies and auth servers
func TestHeartbeatAnnounce(t *testing.T) {
t.Parallel()
@@ -352,30 +304,6 @@ type fakeAnnouncer struct {
keepAlivesC chan<- types.KeepAlive
}
func (f *fakeAnnouncer) UpsertApplicationServer(ctx context.Context, s types.AppServer) (*types.KeepAlive, error) {
f.upsertCalls[HeartbeatModeApp]++
if f.err != nil {
return nil, f.err
}
return &types.KeepAlive{}, nil
}
func (f *fakeAnnouncer) UpsertDatabaseServer(ctx context.Context, s types.DatabaseServer) (*types.KeepAlive, error) {
f.upsertCalls[HeartbeatModeDB]++
if f.err != nil {
return nil, f.err
}
return &types.KeepAlive{}, nil
}
func (f *fakeAnnouncer) UpsertNode(ctx context.Context, s types.Server) (*types.KeepAlive, error) {
f.upsertCalls[HeartbeatModeNode]++
if f.err != nil {
return nil, f.err
}
return &types.KeepAlive{}, nil
}
func (f *fakeAnnouncer) UpsertProxyServerWithoutReturn(ctx context.Context, s types.Server) error {
f.upsertCalls[HeartbeatModeProxy]++
return f.err
@@ -386,14 +314,6 @@ func (f *fakeAnnouncer) UpsertAuthServer(ctx context.Context, s types.Server) er
return f.err
}
func (f *fakeAnnouncer) UpsertKubernetesServer(ctx context.Context, s types.KubeServer) (*types.KeepAlive, error) {
f.upsertCalls[HeartbeatModeKube]++
if f.err != nil {
return nil, f.err
}
return &types.KeepAlive{}, f.err
}
func (f *fakeAnnouncer) UpsertWindowsDesktopService(ctx context.Context, s types.WindowsDesktopService) (*types.KeepAlive, error) {
f.upsertCalls[HeartbeatModeWindowsDesktopService]++
if f.err != nil {
+13 -112
View File
@@ -56,7 +56,11 @@ type HeartbeatV2Config[T any] struct {
// Announcer is a fallback used to perform basic upsert-style heartbeats
// if the control stream is unavailable.
//
// DELETE IN: 11.0 (only exists for back-compat with v9 auth servers)
// This field is currently unused as all services migrated to heartbeatV2
// have deprecated V1 support. Future heartbeatV2 service migrations would
// need to set this field and use it to implement `SupportsFallback` and
// `FallbackAnnounce`. See git history prior to this change for exact
// code examples.
Announcer authclient.Announcer
// OnHeartbeat is a per-attempt callback (optional).
OnHeartbeat func(error)
@@ -88,7 +92,6 @@ func NewSSHServerHeartbeat(cfg HeartbeatV2Config[*types.ServerV2]) (*HeartbeatV2
inner := &sshServerHeartbeatV2{
getMetadata: metadata.Get,
announcer: cfg.Announcer,
}
inner.getServer = func(ctx context.Context) (*types.ServerV2, error) {
server, err := cfg.GetResource(ctx)
@@ -124,7 +127,6 @@ func NewAppServerHeartbeat(cfg HeartbeatV2Config[*types.AppServerV3]) (*Heartbea
inner := &appServerHeartbeatV2{
getServer: cfg.GetResource,
announcer: cfg.Announcer,
}
return newHeartbeatV2(cfg.InventoryHandle, inner, heartbeatV2Config{
@@ -144,7 +146,6 @@ func NewDatabaseServerHeartbeat(cfg HeartbeatV2Config[*types.DatabaseServerV3])
inner := &dbServerHeartbeatV2{
getServer: cfg.GetResource,
announcer: cfg.Announcer,
}
return newHeartbeatV2(cfg.InventoryHandle, inner, heartbeatV2Config{
@@ -164,7 +165,6 @@ func NewKubernetesServerHeartbeat(cfg HeartbeatV2Config[*types.KubernetesServerV
inner := &kubeServerHeartbeatV2{
getServer: cfg.GetResource,
announcer: cfg.Announcer,
}
return newHeartbeatV2(cfg.InventoryHandle, inner, heartbeatV2Config{
@@ -595,7 +595,6 @@ type metadataGetter func(ctx context.Context) (*metadata.Metadata, error)
type sshServerHeartbeatV2 struct {
getServer func(ctx context.Context) (*types.ServerV2, error)
getMetadata metadataGetter
announcer authclient.Announcer
prev *types.ServerV2
}
@@ -613,26 +612,11 @@ func (h *sshServerHeartbeatV2) Poll(ctx context.Context) (changed bool) {
}
func (h *sshServerHeartbeatV2) SupportsFallback() bool {
return h.announcer != nil
return false
}
func (h *sshServerHeartbeatV2) FallbackAnnounce(ctx context.Context) (ok bool) {
if h.announcer == nil {
return false
}
server, err := h.getServer(ctx)
if err != nil {
slog.WarnContext(ctx, "Failed to perform fallback heartbeat for ssh server", "error", err)
return false
}
if _, err := h.announcer.UpsertNode(ctx, server); err != nil {
slog.WarnContext(ctx, "Failed to perform fallback heartbeat for ssh server", "error", err)
return false
}
h.prev = server
return true
return false
}
func (h *sshServerHeartbeatV2) Announce(ctx context.Context, sender inventory.DownstreamSender) (ok bool) {
@@ -653,7 +637,6 @@ func (h *sshServerHeartbeatV2) Announce(ctx context.Context, sender inventory.Do
// appServerHeartbeatV2 is the heartbeatV2 implementation for app servers.
type appServerHeartbeatV2 struct {
getServer func(ctx context.Context) (*types.AppServerV3, error)
announcer authclient.Announcer
prev *types.AppServerV3
}
@@ -671,41 +654,14 @@ func (h *appServerHeartbeatV2) Poll(ctx context.Context) (changed bool) {
}
func (h *appServerHeartbeatV2) SupportsFallback() bool {
return h.announcer != nil
return false
}
func (h *appServerHeartbeatV2) FallbackAnnounce(ctx context.Context) (ok bool) {
if h.announcer == nil {
return false
}
server, err := h.getServer(ctx)
if err != nil {
slog.WarnContext(ctx, "Failed to perform fallback heartbeat for app server", "error", err)
return false
}
if _, err := h.announcer.UpsertApplicationServer(ctx, server); err != nil {
if !errors.Is(err, context.Canceled) && status.Code(err) != codes.Canceled {
slog.WarnContext(ctx, "Failed to perform fallback heartbeat for app server", "error", err)
}
return false
}
h.prev = server
return true
return false
}
func (h *appServerHeartbeatV2) Announce(ctx context.Context, sender inventory.DownstreamSender) (ok bool) {
// AppServer heartbeats via inventory control stream were not introduced in a major version,
// so there is a chance that the Auth server is unable to process the request via the inventory
// control stream. If the Auth server capabilities indicate as such, then use the fallback mechanism.
hello := sender.Hello()
switch {
case !hello.HasCapabilities():
return h.FallbackAnnounce(ctx)
case hello.HasCapabilities() && !hello.GetCapabilities().GetAppHeartbeats():
return h.FallbackAnnounce(ctx)
}
server, err := h.getServer(ctx)
if err != nil {
slog.WarnContext(ctx, "Failed to perform inventory heartbeat for app server", "error", err)
@@ -726,7 +682,6 @@ func (h *appServerHeartbeatV2) Announce(ctx context.Context, sender inventory.Do
// dbServerHeartbeatV2 is the heartbeatV2 implementation for db servers.
type dbServerHeartbeatV2 struct {
getServer func(ctx context.Context) (*types.DatabaseServerV3, error)
announcer authclient.Announcer
prev *types.DatabaseServerV3
}
@@ -744,40 +699,14 @@ func (h *dbServerHeartbeatV2) Poll(ctx context.Context) (changed bool) {
}
func (h *dbServerHeartbeatV2) SupportsFallback() bool {
return h.announcer != nil
return false
}
func (h *dbServerHeartbeatV2) FallbackAnnounce(ctx context.Context) (ok bool) {
if h.announcer == nil {
return false
}
server, err := h.getServer(ctx)
if err != nil {
slog.WarnContext(ctx, "Failed to perform fallback heartbeat for database server", "error", err)
return false
}
if _, err := h.announcer.UpsertDatabaseServer(ctx, server); err != nil {
if !errors.Is(err, context.Canceled) && status.Code(err) != codes.Canceled {
slog.WarnContext(ctx, "Failed to perform fallback heartbeat for database server", "error", err)
}
return false
}
h.prev = server
return true
return false
}
func (h *dbServerHeartbeatV2) Announce(ctx context.Context, sender inventory.DownstreamSender) (ok bool) {
// DatabaseServer heartbeats via inventory control stream were not introduced in a major version,
// so there is a chance that the Auth server is unable to process the request via the inventory
// control stream. If the Auth server capabilities indicate as such, then use the fallback mechanism.
hello := sender.Hello()
switch {
case !hello.HasCapabilities():
return h.FallbackAnnounce(ctx)
case hello.HasCapabilities() && !hello.GetCapabilities().GetDatabaseHeartbeats():
return h.FallbackAnnounce(ctx)
}
server, err := h.getServer(ctx)
if err != nil {
slog.WarnContext(ctx, "Failed to perform inventory heartbeat for database server", "error", err)
@@ -797,7 +726,6 @@ func (h *dbServerHeartbeatV2) Announce(ctx context.Context, sender inventory.Dow
// kubeServerHeartbeatV2 is the heartbeatV2 implementation for kubernetes servers.
type kubeServerHeartbeatV2 struct {
getServer func(ctx context.Context) (*types.KubernetesServerV3, error)
announcer authclient.Announcer
prev *types.KubernetesServerV3
}
@@ -815,41 +743,14 @@ func (h *kubeServerHeartbeatV2) Poll(ctx context.Context) (changed bool) {
}
func (h *kubeServerHeartbeatV2) SupportsFallback() bool {
return h.announcer != nil
return false
}
func (h *kubeServerHeartbeatV2) FallbackAnnounce(ctx context.Context) (ok bool) {
if h.announcer == nil {
return false
}
server, err := h.getServer(ctx)
if err != nil {
slog.WarnContext(ctx, "Failed to perform fallback heartbeat for kubernetes server", "error", err)
return false
}
if _, err := h.announcer.UpsertKubernetesServer(ctx, apiutils.CloneProtoMsg(server)); err != nil {
if !errors.Is(err, context.Canceled) && status.Code(err) != codes.Canceled {
slog.WarnContext(ctx, "Failed to perform fallback heartbeat for kubernetes server", "error", err)
}
return false
}
h.prev = server
return true
return false
}
func (h *kubeServerHeartbeatV2) Announce(ctx context.Context, sender inventory.DownstreamSender) (ok bool) {
// KubernetesServer heartbeats via inventory control stream were not introduced in a major version,
// so there is a chance that the Auth server is unable to process the request via the inventory
// control stream. If the Auth server capabilities indicate as such, then use the fallback mechanism.
hello := sender.Hello()
switch {
case !hello.HasCapabilities():
return h.FallbackAnnounce(ctx)
case hello.HasCapabilities() && !hello.GetCapabilities().GetKubernetesHeartbeats():
return h.FallbackAnnounce(ctx)
}
server, err := h.getServer(ctx)
if err != nil {
slog.WarnContext(ctx, "Failed to perform inventory heartbeat for kubernetes server", "error", err)
+5 -9
View File
@@ -1042,15 +1042,11 @@ func New(
}
}
var heartbeatMode srv.HeartbeatMode
if s.proxyMode {
heartbeatMode = srv.HeartbeatModeProxy
} else {
heartbeatMode = srv.HeartbeatModeNode
}
var heartbeat srv.HeartbeatI
if heartbeatMode == srv.HeartbeatModeNode && s.inventoryHandle != nil {
if !s.proxyMode {
if s.inventoryHandle == nil {
return nil, trace.BadParameter("inventoryHandle must not be nil")
}
s.logger.DebugContext(ctx, "starting control-stream based heartbeat")
heartbeat, err = srv.NewSSHServerHeartbeat(srv.HeartbeatV2Config[*types.ServerV2]{
InventoryHandle: s.inventoryHandle,
@@ -1060,7 +1056,7 @@ func New(
} else {
s.logger.DebugContext(ctx, "starting legacy heartbeat")
heartbeat, err = srv.NewHeartbeat(srv.HeartbeatConfig{
Mode: heartbeatMode,
Mode: srv.HeartbeatModeProxy,
Context: ctx,
Component: component,
Announcer: s.authService,
+24 -1
View File
@@ -73,6 +73,7 @@ import (
"github.com/gravitational/teleport/lib/cryptosuites"
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/events/eventstest"
"github.com/gravitational/teleport/lib/inventory"
"github.com/gravitational/teleport/lib/limiter"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/observability/tracing"
@@ -196,6 +197,22 @@ func (f *sshTestFixture) newSSHClient(ctx context.Context, t testing.TB, user *u
return client
}
func newTestInventoryHandle(t testing.TB, clt *authclient.Client, hostID string, role types.SystemRole) inventory.DownstreamHandle {
t.Helper()
handle, err := inventory.NewDownstreamHandle(clt.InventoryControlStream,
func(_ context.Context) (*proto.UpstreamInventoryHello, error) {
return proto.UpstreamInventoryHello_builder{
ServerID: hostID,
Version: teleport.Version,
Services: types.SystemRoles{role}.StringSlice(),
Hostname: "test",
}.Build(), nil
})
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, handle.Close()) })
return handle
}
func setChildLogConfigForTest() ServerOption {
return func(s *Server) error {
s.childLogConfig = &srv.ChildLogConfig{
@@ -266,6 +283,7 @@ func newCustomFixture(t testing.TB, mutateCfg func(*authtest.ServerConfig), sshO
SetSessionController(sessionController),
SetStoragePresenceService(testServer.AuthServer.AuthServer.PresenceInternal),
SetConnectedProxyGetter(reversetunnel.NewConnectedProxyGetter()),
SetInventoryControlHandle(newTestInventoryHandle(t, nodeClient, nodeID, types.RoleNode)),
setChildLogConfigForTest(),
}
@@ -2423,7 +2441,7 @@ func TestLimiter(t *testing.T) {
)
require.NoError(t, err)
nodeClient, _ := newNodeClient(t, f.testSrv)
nodeClient, nodeID := newNodeClient(t, f.testSrv)
lockWatcher := newLockWatcher(ctx, t, nodeClient)
@@ -2458,6 +2476,7 @@ func TestLimiter(t *testing.T) {
SetLockWatcher(lockWatcher),
SetSessionController(sessionController),
SetConnectedProxyGetter(reversetunnel.NewConnectedProxyGetter()),
SetInventoryControlHandle(newTestInventoryHandle(t, nodeClient, nodeID, types.RoleNode)),
setChildLogConfigForTest(),
)
require.NoError(t, err)
@@ -3338,6 +3357,7 @@ func TestEventMetadata(t *testing.T) {
SetX11ForwardingConfig(&x11.ServerConfig{}),
SetSessionController(sessionController),
SetConnectedProxyGetter(reversetunnel.NewConnectedProxyGetter()),
SetInventoryControlHandle(newTestInventoryHandle(t, nodeClient, nodeID, types.RoleNode)),
setChildLogConfigForTest(),
}
@@ -4034,6 +4054,9 @@ func TestGetServerInfoHeartbeat(t *testing.T) {
SetSessionController(sessionController),
SetConnectedProxyGetter(reversetunnel.NewConnectedProxyGetter()),
}
if role == types.RoleNode {
opts = append(opts, SetInventoryControlHandle(newTestInventoryHandle(t, client, serverID, role)))
}
opts = append(opts, extraOpts...)
s, err := New(
+23
View File
@@ -224,6 +224,22 @@ func noHistoryShell(t *testing.T) string {
return shell
}
func newTestInventoryHandle(t *testing.T, clt *authclient.Client, hostID string, role types.SystemRole) inventory.DownstreamHandle {
t.Helper()
handle, err := inventory.NewDownstreamHandle(clt.InventoryControlStream,
func(_ context.Context) (*authproto.UpstreamInventoryHello, error) {
return authproto.UpstreamInventoryHello_builder{
ServerID: hostID,
Version: teleport.Version,
Services: types.SystemRoles{role}.StringSlice(),
Hostname: "test",
}.Build(), nil
})
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, handle.Close()) })
return handle
}
func newWebSuite(t *testing.T) *WebSuite {
return newWebSuiteWithConfig(t, webSuiteConfig{})
}
@@ -403,6 +419,7 @@ func newWebSuiteWithConfig(t *testing.T, cfg webSuiteConfig) *WebSuite {
// create SSH service:
nodeDataDir := t.TempDir()
inventoryHandle := newTestInventoryHandle(t, nodeClient, nodeID, types.RoleNode)
node, err := regular.New(
ctx,
utils.NetAddr{AddrNetwork: "tcp", Addr: "127.0.0.1:0"},
@@ -423,6 +440,7 @@ func newWebSuiteWithConfig(t *testing.T, cfg webSuiteConfig) *WebSuite {
regular.SetLockWatcher(nodeLockWatcher),
regular.SetSessionController(nodeSessionController),
regular.SetConnectedProxyGetter(reversetunnel.NewConnectedProxyGetter()),
regular.SetInventoryControlHandle(inventoryHandle),
)
require.NoError(t, err)
s.node = node
@@ -680,6 +698,9 @@ func newWebSuiteWithConfig(t *testing.T, cfg webSuiteConfig) *WebSuite {
if err := s.node.Close(); err != nil {
errors = append(errors, err)
}
// Close the inventory handle so its control stream doesn't block
// s.server.Shutdown's graceful stop below.
inventoryHandle.Close()
s.webServer.Close()
if err := s.proxy.Close(); err != nil {
errors = append(errors, err)
@@ -763,6 +784,7 @@ func (s *WebSuite) addNode(t *testing.T, uuid string, hostname string, address s
regular.SetLockWatcher(nodeLockWatcher),
regular.SetSessionController(nodeSessionController),
regular.SetConnectedProxyGetter(reversetunnel.NewConnectedProxyGetter()),
regular.SetInventoryControlHandle(newTestInventoryHandle(t, nodeClient, uuid, types.RoleNode)),
)
require.NoError(t, err)
require.NoError(t, node.Start())
@@ -9133,6 +9155,7 @@ func newWebPack(t *testing.T, numProxies int, opts ...webPackOptions) *webPack {
regular.SetLockWatcher(nodeLockWatcher),
regular.SetSessionController(nodeSessionController),
regular.SetConnectedProxyGetter(reversetunnel.NewConnectedProxyGetter()),
regular.SetInventoryControlHandle(newTestInventoryHandle(t, nodeClient, nodeID, types.RoleNode)),
)
require.NoError(t, err)