From 7993bc76cfc520ee09305b8441eac86cb6e89500 Mon Sep 17 00:00:00 2001 From: Dan Upton Date: Wed, 15 Oct 2025 14:42:42 +0100 Subject: [PATCH] MWI: Add kind enum to bot heartbeats --- .../teleport/machineid/v1/bot_instance.pb.go | 151 ++++++++++++++---- .../teleport/machineid/v1/bot_instance.proto | 23 +++ integrations/lib/embeddedtbot/bot.go | 1 + integrations/lib/embeddedtbot/config.go | 1 + integrations/operator/main.go | 3 +- .../terraform-mwi/provider/provider.go | 1 + .../terraform/provider/credentials.go | 1 + lib/tbot/bot/bot.go | 1 + lib/tbot/bot/config.go | 28 ++++ lib/tbot/internal/heartbeat/service.go | 5 + lib/tbot/internal/heartbeat/service_test.go | 2 + lib/tbot/tbot.go | 1 + tool/tctl/common/terraform_command.go | 1 + 13 files changed, 184 insertions(+), 35 deletions(-) diff --git a/api/gen/proto/go/teleport/machineid/v1/bot_instance.pb.go b/api/gen/proto/go/teleport/machineid/v1/bot_instance.pb.go index 044fcf2fa6a0e..1c914f1a1875d 100644 --- a/api/gen/proto/go/teleport/machineid/v1/bot_instance.pb.go +++ b/api/gen/proto/go/teleport/machineid/v1/bot_instance.pb.go @@ -41,6 +41,68 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// BotKind identifies whether the bot is the tbot binary or embedded in another +// component. +type BotKind int32 + +const ( + // The enum zero-value, it means no kind was included. + BotKind_BOT_KIND_UNSPECIFIED BotKind = 0 + // Means the bot is running the tbot binary. + BotKind_BOT_KIND_TBOT BotKind = 1 + // Means the bot is running inside one of our Terraform providers. + BotKind_BOT_KIND_TERRAFORM_PROVIDER BotKind = 2 + // Means the bot is running inside the Teleport Kubernetes operator. + BotKind_BOT_KIND_KUBERNETES_OPERATOR BotKind = 3 + // Means the bot is running inside tctl (e.g. `tctl terraform env`) + BotKind_BOT_KIND_TCTL BotKind = 4 +) + +// Enum value maps for BotKind. +var ( + BotKind_name = map[int32]string{ + 0: "BOT_KIND_UNSPECIFIED", + 1: "BOT_KIND_TBOT", + 2: "BOT_KIND_TERRAFORM_PROVIDER", + 3: "BOT_KIND_KUBERNETES_OPERATOR", + 4: "BOT_KIND_TCTL", + } + BotKind_value = map[string]int32{ + "BOT_KIND_UNSPECIFIED": 0, + "BOT_KIND_TBOT": 1, + "BOT_KIND_TERRAFORM_PROVIDER": 2, + "BOT_KIND_KUBERNETES_OPERATOR": 3, + "BOT_KIND_TCTL": 4, + } +) + +func (x BotKind) Enum() *BotKind { + p := new(BotKind) + *p = x + return p +} + +func (x BotKind) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (BotKind) Descriptor() protoreflect.EnumDescriptor { + return file_teleport_machineid_v1_bot_instance_proto_enumTypes[0].Descriptor() +} + +func (BotKind) Type() protoreflect.EnumType { + return &file_teleport_machineid_v1_bot_instance_proto_enumTypes[0] +} + +func (x BotKind) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use BotKind.Descriptor instead. +func (BotKind) EnumDescriptor() ([]byte, []int) { + return file_teleport_machineid_v1_bot_instance_proto_rawDescGZIP(), []int{0} +} + // A BotInstance type BotInstance struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -232,7 +294,10 @@ type BotInstanceStatusHeartbeat struct { ExternalUpdaterVersion string `protobuf:"bytes,11,opt,name=external_updater_version,json=externalUpdaterVersion,proto3" json:"external_updater_version,omitempty"` // Information provided by the external updater, including the update group // and updater status. - UpdaterInfo *types.UpdaterV2Info `protobuf:"bytes,12,opt,name=updater_info,json=updaterInfo,proto3" json:"updater_info,omitempty"` + UpdaterInfo *types.UpdaterV2Info `protobuf:"bytes,12,opt,name=updater_info,json=updaterInfo,proto3" json:"updater_info,omitempty"` + // Identifies whether the bot is running in the tbot binary or embedded in + // another component. + Kind BotKind `protobuf:"varint,13,opt,name=kind,proto3,enum=teleport.machineid.v1.BotKind" json:"kind,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -351,6 +416,13 @@ func (x *BotInstanceStatusHeartbeat) GetUpdaterInfo() *types.UpdaterV2Info { return nil } +func (x *BotInstanceStatusHeartbeat) GetKind() BotKind { + if x != nil { + return x.Kind + } + return BotKind_BOT_KIND_UNSPECIFIED +} + // BotInstanceStatusAuthentication contains information about a join or renewal. // Ths information is entirely sourced by the Auth Server and can be trusted. type BotInstanceStatusAuthentication struct { @@ -554,7 +626,7 @@ const file_teleport_machineid_v1_bot_instance_proto_rawDesc = "" + "\bbot_name\x18\x01 \x01(\tR\abotName\x12\x1f\n" + "\vinstance_id\x18\x02 \x01(\tR\n" + "instanceId\x120\n" + - "\x14previous_instance_id\x18\x04 \x01(\tR\x12previousInstanceIdJ\x04\b\x03\x10\x04R\x03ttl\"\xef\x03\n" + + "\x14previous_instance_id\x18\x04 \x01(\tR\x12previousInstanceIdJ\x04\b\x03\x10\x04R\x03ttl\"\xa3\x04\n" + "\x1aBotInstanceStatusHeartbeat\x12;\n" + "\vrecorded_at\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampR\n" + "recordedAt\x12\x1d\n" + @@ -571,7 +643,8 @@ const file_teleport_machineid_v1_bot_instance_proto_rawDesc = "" + "\x10external_updater\x18\n" + " \x01(\tR\x0fexternalUpdater\x128\n" + "\x18external_updater_version\x18\v \x01(\tR\x16externalUpdaterVersion\x127\n" + - "\fupdater_info\x18\f \x01(\v2\x14.types.UpdaterV2InfoR\vupdaterInfo\"\xf7\x02\n" + + "\fupdater_info\x18\f \x01(\v2\x14.types.UpdaterV2InfoR\vupdaterInfo\x122\n" + + "\x04kind\x18\r \x01(\x0e2\x1e.teleport.machineid.v1.BotKindR\x04kind\"\xf7\x02\n" + "\x1fBotInstanceStatusAuthentication\x12E\n" + "\x10authenticated_at\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampR\x0fauthenticatedAt\x12\x1f\n" + "\vjoin_method\x18\x02 \x01(\tR\n" + @@ -590,7 +663,13 @@ const file_teleport_machineid_v1_bot_instance_proto_rawDesc = "" + "\x16initial_authentication\x18\x01 \x01(\v26.teleport.machineid.v1.BotInstanceStatusAuthenticationR\x15initialAuthentication\x12m\n" + "\x16latest_authentications\x18\x02 \x03(\v26.teleport.machineid.v1.BotInstanceStatusAuthenticationR\x15latestAuthentications\x12^\n" + "\x11initial_heartbeat\x18\x03 \x01(\v21.teleport.machineid.v1.BotInstanceStatusHeartbeatR\x10initialHeartbeat\x12^\n" + - "\x11latest_heartbeats\x18\x04 \x03(\v21.teleport.machineid.v1.BotInstanceStatusHeartbeatR\x10latestHeartbeatsBVZTgithub.com/gravitational/teleport/api/gen/proto/go/teleport/machineid/v1;machineidv1b\x06proto3" + "\x11latest_heartbeats\x18\x04 \x03(\v21.teleport.machineid.v1.BotInstanceStatusHeartbeatR\x10latestHeartbeats*\x8c\x01\n" + + "\aBotKind\x12\x18\n" + + "\x14BOT_KIND_UNSPECIFIED\x10\x00\x12\x11\n" + + "\rBOT_KIND_TBOT\x10\x01\x12\x1f\n" + + "\x1bBOT_KIND_TERRAFORM_PROVIDER\x10\x02\x12 \n" + + "\x1cBOT_KIND_KUBERNETES_OPERATOR\x10\x03\x12\x11\n" + + "\rBOT_KIND_TCTL\x10\x04BVZTgithub.com/gravitational/teleport/api/gen/proto/go/teleport/machineid/v1;machineidv1b\x06proto3" var ( file_teleport_machineid_v1_bot_instance_proto_rawDescOnce sync.Once @@ -604,39 +683,42 @@ func file_teleport_machineid_v1_bot_instance_proto_rawDescGZIP() []byte { return file_teleport_machineid_v1_bot_instance_proto_rawDescData } +var file_teleport_machineid_v1_bot_instance_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_teleport_machineid_v1_bot_instance_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_teleport_machineid_v1_bot_instance_proto_goTypes = []any{ - (*BotInstance)(nil), // 0: teleport.machineid.v1.BotInstance - (*BotInstanceSpec)(nil), // 1: teleport.machineid.v1.BotInstanceSpec - (*BotInstanceStatusHeartbeat)(nil), // 2: teleport.machineid.v1.BotInstanceStatusHeartbeat - (*BotInstanceStatusAuthentication)(nil), // 3: teleport.machineid.v1.BotInstanceStatusAuthentication - (*BotInstanceStatus)(nil), // 4: teleport.machineid.v1.BotInstanceStatus - (*v1.Metadata)(nil), // 5: teleport.header.v1.Metadata - (*timestamppb.Timestamp)(nil), // 6: google.protobuf.Timestamp - (*durationpb.Duration)(nil), // 7: google.protobuf.Duration - (*types.UpdaterV2Info)(nil), // 8: types.UpdaterV2Info - (*structpb.Struct)(nil), // 9: google.protobuf.Struct - (*v11.JoinAttrs)(nil), // 10: teleport.workloadidentity.v1.JoinAttrs + (BotKind)(0), // 0: teleport.machineid.v1.BotKind + (*BotInstance)(nil), // 1: teleport.machineid.v1.BotInstance + (*BotInstanceSpec)(nil), // 2: teleport.machineid.v1.BotInstanceSpec + (*BotInstanceStatusHeartbeat)(nil), // 3: teleport.machineid.v1.BotInstanceStatusHeartbeat + (*BotInstanceStatusAuthentication)(nil), // 4: teleport.machineid.v1.BotInstanceStatusAuthentication + (*BotInstanceStatus)(nil), // 5: teleport.machineid.v1.BotInstanceStatus + (*v1.Metadata)(nil), // 6: teleport.header.v1.Metadata + (*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp + (*durationpb.Duration)(nil), // 8: google.protobuf.Duration + (*types.UpdaterV2Info)(nil), // 9: types.UpdaterV2Info + (*structpb.Struct)(nil), // 10: google.protobuf.Struct + (*v11.JoinAttrs)(nil), // 11: teleport.workloadidentity.v1.JoinAttrs } var file_teleport_machineid_v1_bot_instance_proto_depIdxs = []int32{ - 5, // 0: teleport.machineid.v1.BotInstance.metadata:type_name -> teleport.header.v1.Metadata - 1, // 1: teleport.machineid.v1.BotInstance.spec:type_name -> teleport.machineid.v1.BotInstanceSpec - 4, // 2: teleport.machineid.v1.BotInstance.status:type_name -> teleport.machineid.v1.BotInstanceStatus - 6, // 3: teleport.machineid.v1.BotInstanceStatusHeartbeat.recorded_at:type_name -> google.protobuf.Timestamp - 7, // 4: teleport.machineid.v1.BotInstanceStatusHeartbeat.uptime:type_name -> google.protobuf.Duration - 8, // 5: teleport.machineid.v1.BotInstanceStatusHeartbeat.updater_info:type_name -> types.UpdaterV2Info - 6, // 6: teleport.machineid.v1.BotInstanceStatusAuthentication.authenticated_at:type_name -> google.protobuf.Timestamp - 9, // 7: teleport.machineid.v1.BotInstanceStatusAuthentication.metadata:type_name -> google.protobuf.Struct - 10, // 8: teleport.machineid.v1.BotInstanceStatusAuthentication.join_attrs:type_name -> teleport.workloadidentity.v1.JoinAttrs - 3, // 9: teleport.machineid.v1.BotInstanceStatus.initial_authentication:type_name -> teleport.machineid.v1.BotInstanceStatusAuthentication - 3, // 10: teleport.machineid.v1.BotInstanceStatus.latest_authentications:type_name -> teleport.machineid.v1.BotInstanceStatusAuthentication - 2, // 11: teleport.machineid.v1.BotInstanceStatus.initial_heartbeat:type_name -> teleport.machineid.v1.BotInstanceStatusHeartbeat - 2, // 12: teleport.machineid.v1.BotInstanceStatus.latest_heartbeats:type_name -> teleport.machineid.v1.BotInstanceStatusHeartbeat - 13, // [13:13] is the sub-list for method output_type - 13, // [13:13] is the sub-list for method input_type - 13, // [13:13] is the sub-list for extension type_name - 13, // [13:13] is the sub-list for extension extendee - 0, // [0:13] is the sub-list for field type_name + 6, // 0: teleport.machineid.v1.BotInstance.metadata:type_name -> teleport.header.v1.Metadata + 2, // 1: teleport.machineid.v1.BotInstance.spec:type_name -> teleport.machineid.v1.BotInstanceSpec + 5, // 2: teleport.machineid.v1.BotInstance.status:type_name -> teleport.machineid.v1.BotInstanceStatus + 7, // 3: teleport.machineid.v1.BotInstanceStatusHeartbeat.recorded_at:type_name -> google.protobuf.Timestamp + 8, // 4: teleport.machineid.v1.BotInstanceStatusHeartbeat.uptime:type_name -> google.protobuf.Duration + 9, // 5: teleport.machineid.v1.BotInstanceStatusHeartbeat.updater_info:type_name -> types.UpdaterV2Info + 0, // 6: teleport.machineid.v1.BotInstanceStatusHeartbeat.kind:type_name -> teleport.machineid.v1.BotKind + 7, // 7: teleport.machineid.v1.BotInstanceStatusAuthentication.authenticated_at:type_name -> google.protobuf.Timestamp + 10, // 8: teleport.machineid.v1.BotInstanceStatusAuthentication.metadata:type_name -> google.protobuf.Struct + 11, // 9: teleport.machineid.v1.BotInstanceStatusAuthentication.join_attrs:type_name -> teleport.workloadidentity.v1.JoinAttrs + 4, // 10: teleport.machineid.v1.BotInstanceStatus.initial_authentication:type_name -> teleport.machineid.v1.BotInstanceStatusAuthentication + 4, // 11: teleport.machineid.v1.BotInstanceStatus.latest_authentications:type_name -> teleport.machineid.v1.BotInstanceStatusAuthentication + 3, // 12: teleport.machineid.v1.BotInstanceStatus.initial_heartbeat:type_name -> teleport.machineid.v1.BotInstanceStatusHeartbeat + 3, // 13: teleport.machineid.v1.BotInstanceStatus.latest_heartbeats:type_name -> teleport.machineid.v1.BotInstanceStatusHeartbeat + 14, // [14:14] is the sub-list for method output_type + 14, // [14:14] is the sub-list for method input_type + 14, // [14:14] is the sub-list for extension type_name + 14, // [14:14] is the sub-list for extension extendee + 0, // [0:14] is the sub-list for field type_name } func init() { file_teleport_machineid_v1_bot_instance_proto_init() } @@ -649,13 +731,14 @@ func file_teleport_machineid_v1_bot_instance_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_teleport_machineid_v1_bot_instance_proto_rawDesc), len(file_teleport_machineid_v1_bot_instance_proto_rawDesc)), - NumEnums: 0, + NumEnums: 1, NumMessages: 5, NumExtensions: 0, NumServices: 0, }, GoTypes: file_teleport_machineid_v1_bot_instance_proto_goTypes, DependencyIndexes: file_teleport_machineid_v1_bot_instance_proto_depIdxs, + EnumInfos: file_teleport_machineid_v1_bot_instance_proto_enumTypes, MessageInfos: file_teleport_machineid_v1_bot_instance_proto_msgTypes, }.Build() File_teleport_machineid_v1_bot_instance_proto = out.File diff --git a/api/proto/teleport/machineid/v1/bot_instance.proto b/api/proto/teleport/machineid/v1/bot_instance.proto index 54d3d3fb13e9a..1010412b1a6a8 100644 --- a/api/proto/teleport/machineid/v1/bot_instance.proto +++ b/api/proto/teleport/machineid/v1/bot_instance.proto @@ -92,11 +92,34 @@ message BotInstanceStatusHeartbeat { // and updater status. types.UpdaterV2Info updater_info = 12; + // Identifies whether the bot is running in the tbot binary or embedded in + // another component. + BotKind kind = 13; + // In future iterations, additional information can be submitted here. // For example, the configuration of `tbot` or the health of individual // outputs. } +// BotKind identifies whether the bot is the tbot binary or embedded in another +// component. +enum BotKind { + // The enum zero-value, it means no kind was included. + BOT_KIND_UNSPECIFIED = 0; + + // Means the bot is running the tbot binary. + BOT_KIND_TBOT = 1; + + // Means the bot is running inside one of our Terraform providers. + BOT_KIND_TERRAFORM_PROVIDER = 2; + + // Means the bot is running inside the Teleport Kubernetes operator. + BOT_KIND_KUBERNETES_OPERATOR = 3; + + // Means the bot is running inside tctl (e.g. `tctl terraform env`) + BOT_KIND_TCTL = 4; +} + // BotInstanceStatusAuthentication contains information about a join or renewal. // Ths information is entirely sourced by the Auth Server and can be trusted. message BotInstanceStatusAuthentication { diff --git a/integrations/lib/embeddedtbot/bot.go b/integrations/lib/embeddedtbot/bot.go index 62ecc280f5510..fdbf2fee685b0 100644 --- a/integrations/lib/embeddedtbot/bot.go +++ b/integrations/lib/embeddedtbot/bot.go @@ -57,6 +57,7 @@ func New(botConfig *BotConfig, log *slog.Logger) (*EmbeddedBot, error) { credential := &clientcredentials.UnstableConfig{} cfg := bot.Config{ + Kind: botConfig.Kind, Connection: connection.Config{ Address: botConfig.AuthServer, AddressKind: connection.AddressKindAuth, diff --git a/integrations/lib/embeddedtbot/config.go b/integrations/lib/embeddedtbot/config.go index d8c8984034344..6346b1f524070 100644 --- a/integrations/lib/embeddedtbot/config.go +++ b/integrations/lib/embeddedtbot/config.go @@ -37,6 +37,7 @@ const ( // of the `lib/tbot/config.BotConfig` struct with CLI flags and operator-specific // defaults. type BotConfig struct { + Kind bot.Kind AuthServer string Onboarding onboarding.Config CredentialLifetime bot.CredentialLifetime diff --git a/integrations/operator/main.go b/integrations/operator/main.go index 391a57ce46cb6..5ef188527a490 100644 --- a/integrations/operator/main.go +++ b/integrations/operator/main.go @@ -40,6 +40,7 @@ import ( "github.com/gravitational/teleport/integrations/lib/embeddedtbot" "github.com/gravitational/teleport/integrations/operator/controllers" "github.com/gravitational/teleport/integrations/operator/controllers/resources" + "github.com/gravitational/teleport/lib/tbot/bot" logutils "github.com/gravitational/teleport/lib/utils/log" ) @@ -69,7 +70,7 @@ func main() { config := &operatorConfig{} config.BindFlags(flag.CommandLine) - botConfig := &embeddedtbot.BotConfig{} + botConfig := &embeddedtbot.BotConfig{Kind: bot.KindKubernetesOperator} botConfig.BindFlags(flag.CommandLine) flag.Parse() diff --git a/integrations/terraform-mwi/provider/provider.go b/integrations/terraform-mwi/provider/provider.go index 7422fd17a2f11..d4dcb555f8f6d 100644 --- a/integrations/terraform-mwi/provider/provider.go +++ b/integrations/terraform-mwi/provider/provider.go @@ -114,6 +114,7 @@ func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest, botInternalStore := destination.NewMemory() newBotConfig := func() bot.Config { return bot.Config{ + Kind: bot.KindTerraformProvider, Connection: connection.Config{ Address: data.ProxyServer.ValueString(), AddressKind: connection.AddressKindProxy, diff --git a/integrations/terraform/provider/credentials.go b/integrations/terraform/provider/credentials.go index a98674c29991c..59400d1544a24 100644 --- a/integrations/terraform/provider/credentials.go +++ b/integrations/terraform/provider/credentials.go @@ -516,6 +516,7 @@ See https://goteleport.com/docs/reference/join-methods for more details.`) return nil, trace.Wrap(err, "Invalid Join Method") } botConfig := &embeddedtbot.BotConfig{ + Kind: bot.KindTerraformProvider, AuthServer: addr, Onboarding: onboarding.Config{ TokenValue: joinToken, diff --git a/lib/tbot/bot/bot.go b/lib/tbot/bot/bot.go index c61dd5c23de47..b48b7f1731ff0 100644 --- a/lib/tbot/bot/bot.go +++ b/lib/tbot/bot/bot.go @@ -351,6 +351,7 @@ func (b *Bot) buildHeartbeatService( statusRegistry *readyz.Registry, ) (*heartbeat.Service, error) { return heartbeat.NewService(heartbeat.Config{ + BotKind: machineidv1.BotKind(b.cfg.Kind), Interval: 30 * time.Minute, RetryLimit: 5, Client: machineidv1.NewBotInstanceServiceClient(identityService.GetClient().GetConnection()), diff --git a/lib/tbot/bot/config.go b/lib/tbot/bot/config.go index 6bdb625171103..2b502acfb71fd 100644 --- a/lib/tbot/bot/config.go +++ b/lib/tbot/bot/config.go @@ -24,6 +24,7 @@ import ( "github.com/gravitational/trace" "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus" + machineidv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/machineid/v1" "github.com/gravitational/teleport/lib/tbot/bot/connection" "github.com/gravitational/teleport/lib/tbot/bot/destination" "github.com/gravitational/teleport/lib/tbot/bot/onboarding" @@ -33,6 +34,10 @@ import ( // Config contains the core bot's configuration. The tbot binary's configuration // file format is handled by the lib/tbot/config package. type Config struct { + // Kind identifies whether the bot is running in the tbot binary or embedded + // in another component. + Kind Kind + // Connection controls how the bot connects to the cluster. Connection connection.Config @@ -87,3 +92,26 @@ func (c *Config) CheckAndSetDefaults() error { // dynamically registered (like the Kubernetes Secret Destination, which is only // available if you import the k8s package) without maintaining a global registry. type UnmarshalConfigContext = internal.UnmarshalConfigContext + +// Kind identifies whether the bot is running in the tbot binary or embedded +// in another component +type Kind machineidv1.BotKind + +const ( + // KindUnspecified means no bot kind was given. + KindUnspecified = Kind(machineidv1.BotKind_BOT_KIND_UNSPECIFIED) + + // KindTbot means the bot is running in the tbot binary. + KindTbot = Kind(machineidv1.BotKind_BOT_KIND_TBOT) + + // KindTerraformProvider means the bot is embedded in one of our Terraform + // providers. + KindTerraformProvider = Kind(machineidv1.BotKind_BOT_KIND_TERRAFORM_PROVIDER) + + // KindKubernetesOperator means the bot is embedded in our Kubernetes + // operator. + KindKubernetesOperator = Kind(machineidv1.BotKind_BOT_KIND_KUBERNETES_OPERATOR) + + // KindTctl means the bot is embedded in tctl. + KindTctl = Kind(machineidv1.BotKind_BOT_KIND_TCTL) +) diff --git a/lib/tbot/internal/heartbeat/service.go b/lib/tbot/internal/heartbeat/service.go index 422f07afc0d59..a868ca1e804fe 100644 --- a/lib/tbot/internal/heartbeat/service.go +++ b/lib/tbot/internal/heartbeat/service.go @@ -47,6 +47,10 @@ type Client interface { // Config for the heartbeat service. type Config struct { + // BotKind identifies whether the bot is running in the tbot binary or + // embedded in another component + BotKind machineidv1pb.BotKind + // Interval controls how frequently heartbeats are submitted. Interval time.Duration @@ -173,6 +177,7 @@ func (s *Service) heartbeat(ctx context.Context, isOneShot, isStartup bool) erro Version: teleport.Version, Architecture: runtime.GOARCH, Os: runtime.GOOS, + Kind: s.cfg.BotKind, } _, err = s.cfg.Client.SubmitHeartbeat(ctx, &machineidv1pb.SubmitHeartbeatRequest{ diff --git a/lib/tbot/internal/heartbeat/service_test.go b/lib/tbot/internal/heartbeat/service_test.go index 1374aa7d06232..9a9e772fbef26 100644 --- a/lib/tbot/internal/heartbeat/service_test.go +++ b/lib/tbot/internal/heartbeat/service_test.go @@ -76,6 +76,7 @@ func TestHeartbeatService(t *testing.T) { StartedAt: time.Date(2024, time.April, 1, 11, 0, 0, 0, time.UTC), Logger: log, JoinMethod: types.JoinMethodGitHub, + BotKind: machineidv1pb.BotKind_BOT_KIND_TBOT, }) require.NoError(t, err) @@ -99,6 +100,7 @@ func TestHeartbeatService(t *testing.T) { Architecture: runtime.GOARCH, Os: runtime.GOOS, JoinMethod: string(types.JoinMethodGitHub), + Kind: machineidv1pb.BotKind_BOT_KIND_TBOT, }, } assert.Empty(t, cmp.Diff(want, got, protocmp.Transform())) diff --git a/lib/tbot/tbot.go b/lib/tbot/tbot.go index 377fd75ad7dfe..0780570288a64 100644 --- a/lib/tbot/tbot.go +++ b/lib/tbot/tbot.go @@ -259,6 +259,7 @@ func (b *Bot) Run(ctx context.Context) (err error) { } bt, err := bot.New(bot.Config{ + Kind: bot.KindTbot, Connection: b.cfg.ConnectionConfig(), Onboarding: b.cfg.Onboarding, InternalStorage: b.cfg.Storage.Destination, diff --git a/tool/tctl/common/terraform_command.go b/tool/tctl/common/terraform_command.go index 2c54e61605eff..b2d2527b0829b 100644 --- a/tool/tctl/common/terraform_command.go +++ b/tool/tctl/common/terraform_command.go @@ -307,6 +307,7 @@ func (c *TerraformCommand) useBotToObtainIdentity(ctx context.Context, addr util RenewalInterval: bot.DefaultCredentialLifetime.RenewalInterval, } cfg := bot.Config{ + Kind: bot.KindTctl, Connection: connection.Config{ Address: addr.String(), AddressKind: connection.AddressKindAuth,