diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 73310d107c67..66db5255f8df 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -529,6 +529,7 @@ for a few releases. Please use other tools provided by Elastic to fetch data fro - Update ECS to 1.12.0. {pull}27770[27770] - Fields mapped as `match_only_text` will automatically fallback to a `text` mapping when using Elasticsearch versions that do not support `match_only_text`. {pull}27770[27770] - Update cloud.google.com/go library. {pull}28229[28229] +- Add additional metadata to the root HTTP endpoint. {pull}28265[28265] - Upgrade k8s.io/client-go library. {pull}28228[28228] *Auditbeat* diff --git a/libbeat/cmd/instance/beat.go b/libbeat/cmd/instance/beat.go index d3f7b449ddcb..f12aa1dcc124 100644 --- a/libbeat/cmd/instance/beat.go +++ b/libbeat/cmd/instance/beat.go @@ -29,6 +29,7 @@ import ( "math/big" "math/rand" "os" + "os/user" "runtime" "runtime/debug" "strings" @@ -162,7 +163,7 @@ func Run(settings Settings, bt beat.Creator) error { name := settings.Name idxPrefix := settings.IndexPrefix - version := settings.Version + agentVersion := settings.Version elasticLicensed := settings.ElasticLicensed return handleError(func() error { @@ -172,7 +173,7 @@ func Run(settings Settings, bt beat.Creator) error { "panic", r, zap.Stack("stack")) } }() - b, err := NewBeat(name, idxPrefix, version, elasticLicensed) + b, err := NewBeat(name, idxPrefix, agentVersion, elasticLicensed) if err != nil { return err } @@ -184,6 +185,20 @@ func Run(settings Settings, bt beat.Creator) error { monitoring.NewString(registry, "name").Set(b.Info.Name) monitoring.NewString(registry, "hostname").Set(b.Info.Hostname) + // Add more beat metadata + monitoring.NewString(registry, "binary_arch").Set(runtime.GOARCH) + monitoring.NewString(registry, "build_commit").Set(version.Commit()) + monitoring.NewTimestamp(registry, "build_time").Set(version.BuildTime()) + monitoring.NewBool(registry, "elastic_licensed").Set(b.Info.ElasticLicensed) + + u, err := user.Current() + if err != nil { + return err + } + monitoring.NewString(registry, "username").Set(u.Username) + monitoring.NewString(registry, "uid").Set(u.Uid) + monitoring.NewString(registry, "gid").Set(u.Gid) + // Add additional info to state registry. This is also reported to monitoring stateRegistry := monitoring.GetNamespace("state").GetRegistry() serviceRegistry := stateRegistry.NewRegistry("service") @@ -414,6 +429,7 @@ func (b *Beat) launch(settings Settings, bt beat.Creator) error { // Set Beat ID in registry vars, in case it was loaded from meta file infoRegistry := monitoring.GetNamespace("info").GetRegistry() monitoring.NewString(infoRegistry, "uuid").Set(b.Info.ID.String()) + monitoring.NewString(infoRegistry, "ephemeral_id").Set(b.Info.EphemeralID.String()) serviceRegistry := monitoring.GetNamespace("state").GetRegistry().GetRegistry("service") monitoring.NewString(serviceRegistry, "id").Set(b.Info.ID.String()) diff --git a/x-pack/elastic-agent/CHANGELOG.next.asciidoc b/x-pack/elastic-agent/CHANGELOG.next.asciidoc index 95f7b90e89b2..6a53c568bd53 100644 --- a/x-pack/elastic-agent/CHANGELOG.next.asciidoc +++ b/x-pack/elastic-agent/CHANGELOG.next.asciidoc @@ -145,4 +145,5 @@ - Agent now adapts the beats queue size based on output settings. {issue}26638[26638] {pull}27429[27429] - Support ephemeral containers in Kubernetes dynamic provider. {issue}27020[#27020] {pull}27707[27707] - Add complete k8s metadata through composable provider. {pull}27691[27691] +- Add diagnostics command to gather beat metadata. {pull}28265[28265] - Add `KIBANA_FLEET_SERVICE_TOKEN` to Elastic Agent container. {pull}28096[28096] diff --git a/x-pack/elastic-agent/control.proto b/x-pack/elastic-agent/control.proto index 0c5645faab9e..26b6552c395c 100644 --- a/x-pack/elastic-agent/control.proto +++ b/x-pack/elastic-agent/control.proto @@ -94,6 +94,25 @@ message ApplicationStatus { string payload = 5; } +// Current metadata for a running process. +message ProcMeta { + string process = 1; + string name = 2; + string hostname = 3; + string id = 4; + string ephemeral_id = 5; + string version = 6; + string build_commit = 7; + string build_time = 8; + string username = 9; + string user_id = 10; + string user_gid = 11; + string architecture = 12; + string route_key = 13; + bool elastic_licensed = 14; + string error = 15; +} + // Status is the current status of Elastic Agent. message StatusResponse { // Overall status of Elastic Agent. @@ -104,6 +123,11 @@ message StatusResponse { repeated ApplicationStatus applications = 3; } +// ProcMetaResponse is the current running version infomation for all processes. +message ProcMetaResponse { + repeated ProcMeta procs = 1; +} + service ElasticAgentControl { // Fetches the currently running version of the Elastic Agent. rpc Version(Empty) returns (VersionResponse); @@ -116,4 +140,7 @@ service ElasticAgentControl { // Upgrade starts the upgrade process of Elastic Agent. rpc Upgrade(UpgradeRequest) returns (UpgradeResponse); + + // Gather all running process metadata. + rpc ProcMeta(Empty) returns (ProcMetaResponse); } diff --git a/x-pack/elastic-agent/pkg/agent/application/pipeline/stream/operator_stream.go b/x-pack/elastic-agent/pkg/agent/application/pipeline/stream/operator_stream.go index 57b16bbabc4e..f56b1cfb3e9a 100644 --- a/x-pack/elastic-agent/pkg/agent/application/pipeline/stream/operator_stream.go +++ b/x-pack/elastic-agent/pkg/agent/application/pipeline/stream/operator_stream.go @@ -7,6 +7,7 @@ package stream import ( "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/pipeline" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/configrequest" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/program" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/state" ) @@ -20,6 +21,10 @@ type stater interface { State() map[string]state.State } +type specer interface { + Specs() map[string]program.Spec +} + func (b *operatorStream) Close() error { return b.configHandler.Close() } @@ -32,6 +37,13 @@ func (b *operatorStream) State() map[string]state.State { return nil } +func (b *operatorStream) Specs() map[string]program.Spec { + if s, ok := b.configHandler.(specer); ok { + return s.Specs() + } + return nil +} + func (b *operatorStream) Execute(cfg configrequest.Request) error { return b.configHandler.HandleConfig(cfg) } diff --git a/x-pack/elastic-agent/pkg/agent/cmd/common.go b/x-pack/elastic-agent/pkg/agent/cmd/common.go index e98c384cfafe..14f61b9afd63 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/common.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/common.go @@ -65,6 +65,7 @@ func NewCommandWithArgs(args []string, streams *cli.IOStreams) *cobra.Command { cmd.AddCommand(newWatchCommandWithArgs(args, streams)) cmd.AddCommand(newContainerCommand(args, streams)) cmd.AddCommand(newStatusCommand(args, streams)) + cmd.AddCommand(newDiagnosticsCommand(args, streams)) // windows special hidden sub-command (only added on windows) reexec := newReExecWindowsCommand(args, streams) diff --git a/x-pack/elastic-agent/pkg/agent/cmd/diagnostics.go b/x-pack/elastic-agent/pkg/agent/cmd/diagnostics.go new file mode 100644 index 000000000000..b4431d335e20 --- /dev/null +++ b/x-pack/elastic-agent/pkg/agent/cmd/diagnostics.go @@ -0,0 +1,134 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package cmd + +import ( + "context" + "fmt" + "io" + "os" + "text/tabwriter" + "time" + + "github.com/spf13/cobra" + + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/control/client" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/cli" +) + +var diagOutputs = map[string]outputter{ + "human": humanDiagnosticsOutput, + "json": jsonOutput, + "yaml": yamlOutput, +} + +func newDiagnosticsCommand(_ []string, streams *cli.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Use: "diagnostics", + Short: "Gather diagnostics information from the elastic-agent and running processes.", + Long: "Gather diagnostics information from the elastic-agent and running processes.", + Run: func(c *cobra.Command, args []string) { + if err := diagnosticCmd(streams, c, args); err != nil { + fmt.Fprintf(streams.Err, "Error: %v\n%s\n", err, troubleshootMessage()) + os.Exit(1) + } + }, + } + + cmd.Flags().String("output", "human", "Output the diagnostics information in either human, json, or yaml (default: human)") + + return cmd +} + +// DiagnosticsInfo a struct to track all inforation related to diagnostics for the agent. +type DiagnosticsInfo struct { + ProcMeta []client.ProcMeta + AgentVersion client.Version +} + +func diagnosticCmd(streams *cli.IOStreams, cmd *cobra.Command, args []string) error { + err := tryContainerLoadPaths() + if err != nil { + return err + } + + output, _ := cmd.Flags().GetString("output") + outputFunc, ok := diagOutputs[output] + if !ok { + return fmt.Errorf("unsupported output: %s", output) + } + + ctx := handleSignal(context.Background()) + innerCtx, cancel := context.WithTimeout(ctx, 30*time.Second) + defer cancel() + + diag, err := getDiagnostics(innerCtx) + if err == context.DeadlineExceeded { + return errors.New("timed out after 30 seconds trying to connect to Elastic Agent daemon") + } else if err == context.Canceled { + return nil + } else if err != nil { + return fmt.Errorf("failed to communicate with Elastic Agent daemon: %s", err) + } + + return outputFunc(streams.Out, diag) +} + +func getDiagnostics(ctx context.Context) (DiagnosticsInfo, error) { + daemon := client.New() + diag := DiagnosticsInfo{} + err := daemon.Connect(ctx) + if err != nil { + return DiagnosticsInfo{}, err + } + defer daemon.Disconnect() + + bv, err := daemon.ProcMeta(ctx) + if err != nil { + return DiagnosticsInfo{}, err + } + diag.ProcMeta = bv + + version, err := daemon.Version(ctx) + if err != nil { + return DiagnosticsInfo{}, err + } + diag.AgentVersion = version + + return diag, nil +} + +func humanDiagnosticsOutput(w io.Writer, obj interface{}) error { + diag, ok := obj.(DiagnosticsInfo) + if !ok { + return fmt.Errorf("unable to cast %T as DiagnosticsInfo", obj) + } + return outputDiagnostics(w, diag) +} + +func outputDiagnostics(w io.Writer, d DiagnosticsInfo) error { + tw := tabwriter.NewWriter(w, 4, 1, 2, ' ', 0) + fmt.Fprintf(tw, "elastic-agent\tversion: %s\n", d.AgentVersion.Version) + fmt.Fprintf(tw, "\tbuild_commit: %s\tbuild_time: %s\tsnapshot_build: %v\n", d.AgentVersion.Commit, d.AgentVersion.BuildTime, d.AgentVersion.Snapshot) + if len(d.ProcMeta) == 0 { + fmt.Fprintf(tw, "Applications: (none)\n") + } else { + fmt.Fprintf(tw, "Applications:\n") + for _, app := range d.ProcMeta { + fmt.Fprintf(tw, " *\tname: %s\troute_key: %s\n", app.Name, app.RouteKey) + if app.Error != "" { + fmt.Fprintf(tw, "\terror: %s\n", app.Error) + } else { + fmt.Fprintf(tw, "\tprocess: %s\tid: %s\tephemeral_id: %s\telastic_license: %v\n", app.Process, app.ID, app.EphemeralID, app.ElasticLicensed) + fmt.Fprintf(tw, "\tversion: %s\tcommit: %s\tbuild_time: %s\tbinary_arch: %v\n", app.Version, app.BuildCommit, app.BuildTime, app.BinaryArchitecture) + fmt.Fprintf(tw, "\thostname: %s\tusername: %s\tuser_id: %s\tuser_gid: %s\n", app.Hostname, app.Username, app.UserID, app.UserGID) + } + + } + } + tw.Flush() + return nil +} diff --git a/x-pack/elastic-agent/pkg/agent/cmd/diagnostics_test.go b/x-pack/elastic-agent/pkg/agent/cmd/diagnostics_test.go new file mode 100644 index 000000000000..1d985fd05780 --- /dev/null +++ b/x-pack/elastic-agent/pkg/agent/cmd/diagnostics_test.go @@ -0,0 +1,74 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package cmd + +import ( + "os" + "time" + + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/control/client" +) + +var testDiagnostics = DiagnosticsInfo{ + AgentVersion: client.Version{ + Version: "test-version", + Commit: "test-commit", + BuildTime: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC), + Snapshot: false, + }, + ProcMeta: []client.ProcMeta{client.ProcMeta{ + Process: "filebeat", + Name: "filebeat", + Hostname: "test-host", + ID: "filebeat-id", + EphemeralID: "filebeat-ephemeral-id", + Version: "filebeat-version", + BuildCommit: "filebeat-commit", + BuildTime: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC), + Username: "test-user", + UserID: "1000", + UserGID: "1000", + BinaryArchitecture: "test-architecture", + RouteKey: "test", + ElasticLicensed: true, + }, client.ProcMeta{ + Process: "filebeat", + Name: "filebeat_monitoring", + Hostname: "test-host", + ID: "filebeat_monitoring-id", + EphemeralID: "filebeat_monitoring-ephemeral-id", + Version: "filebeat_monitoring-version", + BuildCommit: "filebeat_monitoring-commit", + BuildTime: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC), + Username: "test-user", + UserID: "1000", + UserGID: "1000", + BinaryArchitecture: "test-architecture", + RouteKey: "test", + ElasticLicensed: true, + }, client.ProcMeta{ + Name: "metricbeat", + RouteKey: "test", + Error: "failed to get metricbeat data", + }}, +} + +func Example_humanDiagnosticsOutput() { + humanDiagnosticsOutput(os.Stdout, testDiagnostics) + // Output: + // elastic-agent version: test-version + // build_commit: test-commit build_time: 2021-01-01 00:00:00 +0000 UTC snapshot_build: false + // Applications: + // * name: filebeat route_key: test + // process: filebeat id: filebeat-id ephemeral_id: filebeat-ephemeral-id elastic_license: true + // version: filebeat-version commit: filebeat-commit build_time: 2021-01-01 00:00:00 +0000 UTC binary_arch: test-architecture + // hostname: test-host username: test-user user_id: 1000 user_gid: 1000 + // * name: filebeat_monitoring route_key: test + // process: filebeat id: filebeat_monitoring-id ephemeral_id: filebeat_monitoring-ephemeral-id elastic_license: true + // version: filebeat_monitoring-version commit: filebeat_monitoring-commit build_time: 2021-01-01 00:00:00 +0000 UTC binary_arch: test-architecture + // hostname: test-host username: test-user user_id: 1000 user_gid: 1000 + // * name: metricbeat route_key: test + // error: failed to get metricbeat data +} diff --git a/x-pack/elastic-agent/pkg/agent/cmd/run.go b/x-pack/elastic-agent/pkg/agent/cmd/run.go index d598460b22b8..56a41e068957 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/run.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/run.go @@ -147,6 +147,8 @@ func run(streams *cli.IOStreams, override cfgOverrider) error { return err } + control.SetRouteFn(app.Routes) + serverStopFn, err := setupMetrics(agentInfo, logger, cfg.Settings.DownloadConfig.OS(), cfg.Settings.MonitoringConfig, app) if err != nil { return err diff --git a/x-pack/elastic-agent/pkg/agent/cmd/status.go b/x-pack/elastic-agent/pkg/agent/cmd/status.go index d78e64d1e451..507138cb56f7 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/status.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/status.go @@ -22,10 +22,10 @@ import ( "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/cli" ) -type outputter func(io.Writer, *client.AgentStatus) error +type outputter func(io.Writer, interface{}) error -var outputs = map[string]outputter{ - "human": humanOutput, +var statusOutputs = map[string]outputter{ + "human": humanStatusOutput, "json": jsonOutput, "yaml": yamlOutput, } @@ -55,7 +55,7 @@ func statusCmd(streams *cli.IOStreams, cmd *cobra.Command, args []string) error } output, _ := cmd.Flags().GetString("output") - outputFunc, ok := outputs[output] + outputFunc, ok := statusOutputs[output] if !ok { return fmt.Errorf("unsupported output: %s", output) } @@ -86,7 +86,15 @@ func statusCmd(streams *cli.IOStreams, cmd *cobra.Command, args []string) error return nil } -func humanOutput(w io.Writer, status *client.AgentStatus) error { +func humanStatusOutput(w io.Writer, obj interface{}) error { + status, ok := obj.(*client.AgentStatus) + if !ok { + return fmt.Errorf("unable to cast %T as *client.AgentStatus", obj) + } + return outputStatus(w, status) +} + +func outputStatus(w io.Writer, status *client.AgentStatus) error { fmt.Fprintf(w, "Status: %s\n", status.Status) if status.Message == "" { fmt.Fprint(w, "Message: (no message)\n") @@ -111,8 +119,8 @@ func humanOutput(w io.Writer, status *client.AgentStatus) error { return nil } -func jsonOutput(w io.Writer, status *client.AgentStatus) error { - bytes, err := json.MarshalIndent(status, "", " ") +func jsonOutput(w io.Writer, out interface{}) error { + bytes, err := json.MarshalIndent(out, "", " ") if err != nil { return err } @@ -120,8 +128,8 @@ func jsonOutput(w io.Writer, status *client.AgentStatus) error { return nil } -func yamlOutput(w io.Writer, status *client.AgentStatus) error { - bytes, err := yaml.Marshal(status) +func yamlOutput(w io.Writer, out interface{}) error { + bytes, err := yaml.Marshal(out) if err != nil { return err } diff --git a/x-pack/elastic-agent/pkg/agent/cmd/status_test.go b/x-pack/elastic-agent/pkg/agent/cmd/status_test.go index 6c4aebed58bd..914091566ce5 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/status_test.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/status_test.go @@ -41,8 +41,8 @@ var testStatus = &client.AgentStatus{ }, } -func ExamplehumanOutput() { - humanOutput(os.Stdout, testStatus) +func ExamplehumanStatusOutput() { + humanStatusOutput(os.Stdout, testStatus) // Output: // Status: HEALTHY // Message: (no message) diff --git a/x-pack/elastic-agent/pkg/agent/control/client/client.go b/x-pack/elastic-agent/pkg/agent/control/client/client.go index 3709a834a779..f7332b9896e7 100644 --- a/x-pack/elastic-agent/pkg/agent/control/client/client.go +++ b/x-pack/elastic-agent/pkg/agent/control/client/client.go @@ -53,6 +53,25 @@ type ApplicationStatus struct { Payload map[string]interface{} } +// ProcMeta is the running version and ID information for a running process. +type ProcMeta struct { + Process string + Name string + Hostname string + ID string + EphemeralID string + Version string + BuildCommit string + BuildTime time.Time + Username string + UserID string + UserGID string + BinaryArchitecture string + RouteKey string + ElasticLicensed bool + Error string +} + // AgentStatus is the current status of the Elastic Agent. type AgentStatus struct { Status Status @@ -74,6 +93,8 @@ type Client interface { Restart(ctx context.Context) error // Upgrade triggers upgrade of the current running daemon. Upgrade(ctx context.Context, version string, sourceURI string) (string, error) + // ProcMeta gathers running process meta-data. + ProcMeta(ctx context.Context) ([]ProcMeta, error) } // client manages the state and communication to the Elastic Agent. @@ -184,3 +205,45 @@ func (c *client) Upgrade(ctx context.Context, version string, sourceURI string) } return res.Version, nil } + +// ProcMeta gathers running beat metadata. +func (c *client) ProcMeta(ctx context.Context) ([]ProcMeta, error) { + resp, err := c.client.ProcMeta(ctx, &proto.Empty{}) + if err != nil { + return nil, err + } + procMeta := []ProcMeta{} + + for _, proc := range resp.Procs { + meta := ProcMeta{ + Process: proc.Process, + Name: proc.Name, + Hostname: proc.Hostname, + ID: proc.Id, + EphemeralID: proc.EphemeralId, + Version: proc.Version, + BuildCommit: proc.BuildCommit, + Username: proc.Username, + UserID: proc.UserId, + UserGID: proc.UserGid, + BinaryArchitecture: proc.Architecture, + RouteKey: proc.RouteKey, + ElasticLicensed: proc.ElasticLicensed, + Error: proc.Error, + } + if proc.BuildTime != "" { + ts, err := time.Parse(time.RFC3339, proc.BuildTime) + if err != nil { + if meta.Error != "" { + meta.Error += ", " + err.Error() + } else { + meta.Error = err.Error() + } + } else { + meta.BuildTime = ts + } + } + procMeta = append(procMeta, meta) + } + return procMeta, nil +} diff --git a/x-pack/elastic-agent/pkg/agent/control/proto/control.pb.go b/x-pack/elastic-agent/pkg/agent/control/proto/control.pb.go index a0e2e710f0c5..2cdec52cf65c 100644 --- a/x-pack/elastic-agent/pkg/agent/control/proto/control.pb.go +++ b/x-pack/elastic-agent/pkg/agent/control/proto/control.pb.go @@ -4,8 +4,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.22.0 -// protoc v3.11.4 +// protoc-gen-go v1.27.1 +// protoc v3.18.0 // source: control.proto package proto @@ -15,7 +15,6 @@ import ( reflect "reflect" sync "sync" - proto "github.com/golang/protobuf/proto" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" @@ -30,10 +29,6 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - // Status codes for the current state. type Status int32 @@ -537,6 +532,166 @@ func (x *ApplicationStatus) GetPayload() string { return "" } +// Current metadata for a running process. +type ProcMeta struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Process string `protobuf:"bytes,1,opt,name=process,proto3" json:"process,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Hostname string `protobuf:"bytes,3,opt,name=hostname,proto3" json:"hostname,omitempty"` + Id string `protobuf:"bytes,4,opt,name=id,proto3" json:"id,omitempty"` + EphemeralId string `protobuf:"bytes,5,opt,name=ephemeral_id,json=ephemeralId,proto3" json:"ephemeral_id,omitempty"` + Version string `protobuf:"bytes,6,opt,name=version,proto3" json:"version,omitempty"` + BuildCommit string `protobuf:"bytes,7,opt,name=build_commit,json=buildCommit,proto3" json:"build_commit,omitempty"` + BuildTime string `protobuf:"bytes,8,opt,name=build_time,json=buildTime,proto3" json:"build_time,omitempty"` + Username string `protobuf:"bytes,9,opt,name=username,proto3" json:"username,omitempty"` + UserId string `protobuf:"bytes,10,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + UserGid string `protobuf:"bytes,11,opt,name=user_gid,json=userGid,proto3" json:"user_gid,omitempty"` + Architecture string `protobuf:"bytes,12,opt,name=architecture,proto3" json:"architecture,omitempty"` + RouteKey string `protobuf:"bytes,13,opt,name=route_key,json=routeKey,proto3" json:"route_key,omitempty"` + ElasticLicensed bool `protobuf:"varint,14,opt,name=elastic_licensed,json=elasticLicensed,proto3" json:"elastic_licensed,omitempty"` + Error string `protobuf:"bytes,15,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *ProcMeta) Reset() { + *x = ProcMeta{} + if protoimpl.UnsafeEnabled { + mi := &file_control_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProcMeta) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProcMeta) ProtoMessage() {} + +func (x *ProcMeta) ProtoReflect() protoreflect.Message { + mi := &file_control_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProcMeta.ProtoReflect.Descriptor instead. +func (*ProcMeta) Descriptor() ([]byte, []int) { + return file_control_proto_rawDescGZIP(), []int{6} +} + +func (x *ProcMeta) GetProcess() string { + if x != nil { + return x.Process + } + return "" +} + +func (x *ProcMeta) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ProcMeta) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +func (x *ProcMeta) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *ProcMeta) GetEphemeralId() string { + if x != nil { + return x.EphemeralId + } + return "" +} + +func (x *ProcMeta) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *ProcMeta) GetBuildCommit() string { + if x != nil { + return x.BuildCommit + } + return "" +} + +func (x *ProcMeta) GetBuildTime() string { + if x != nil { + return x.BuildTime + } + return "" +} + +func (x *ProcMeta) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *ProcMeta) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +func (x *ProcMeta) GetUserGid() string { + if x != nil { + return x.UserGid + } + return "" +} + +func (x *ProcMeta) GetArchitecture() string { + if x != nil { + return x.Architecture + } + return "" +} + +func (x *ProcMeta) GetRouteKey() string { + if x != nil { + return x.RouteKey + } + return "" +} + +func (x *ProcMeta) GetElasticLicensed() bool { + if x != nil { + return x.ElasticLicensed + } + return false +} + +func (x *ProcMeta) GetError() string { + if x != nil { + return x.Error + } + return "" +} + // Status is the current status of Elastic Agent. type StatusResponse struct { state protoimpl.MessageState @@ -554,7 +709,7 @@ type StatusResponse struct { func (x *StatusResponse) Reset() { *x = StatusResponse{} if protoimpl.UnsafeEnabled { - mi := &file_control_proto_msgTypes[6] + mi := &file_control_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -567,7 +722,7 @@ func (x *StatusResponse) String() string { func (*StatusResponse) ProtoMessage() {} func (x *StatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_control_proto_msgTypes[6] + mi := &file_control_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -580,7 +735,7 @@ func (x *StatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StatusResponse.ProtoReflect.Descriptor instead. func (*StatusResponse) Descriptor() ([]byte, []int) { - return file_control_proto_rawDescGZIP(), []int{6} + return file_control_proto_rawDescGZIP(), []int{7} } func (x *StatusResponse) GetStatus() Status { @@ -604,6 +759,54 @@ func (x *StatusResponse) GetApplications() []*ApplicationStatus { return nil } +// ProcMetaResponse is the current running version infomation for all processes. +type ProcMetaResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Procs []*ProcMeta `protobuf:"bytes,1,rep,name=procs,proto3" json:"procs,omitempty"` +} + +func (x *ProcMetaResponse) Reset() { + *x = ProcMetaResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_control_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProcMetaResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProcMetaResponse) ProtoMessage() {} + +func (x *ProcMetaResponse) ProtoReflect() protoreflect.Message { + mi := &file_control_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProcMetaResponse.ProtoReflect.Descriptor instead. +func (*ProcMetaResponse) Descriptor() ([]byte, []int) { + return file_control_proto_rawDescGZIP(), []int{8} +} + +func (x *ProcMetaResponse) GetProcs() []*ProcMeta { + if x != nil { + return x.Procs + } + return nil +} + var File_control_proto protoreflect.FileDescriptor var file_control_proto_rawDesc = []byte{ @@ -643,43 +846,77 @@ var file_control_proto_rawDesc = []byte{ 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, - 0x6f, 0x61, 0x64, 0x22, 0x8f, 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3c, 0x0a, 0x0c, 0x61, 0x70, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0c, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2a, 0x79, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x0c, 0x0a, 0x08, 0x53, 0x54, 0x41, 0x52, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x00, 0x12, 0x0f, 0x0a, - 0x0b, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x55, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0b, - 0x0a, 0x07, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x59, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x44, - 0x45, 0x47, 0x52, 0x41, 0x44, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, - 0x4c, 0x45, 0x44, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x49, 0x4e, - 0x47, 0x10, 0x05, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x50, 0x47, 0x52, 0x41, 0x44, 0x49, 0x4e, 0x47, - 0x10, 0x06, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x4f, 0x4c, 0x4c, 0x42, 0x41, 0x43, 0x4b, 0x10, 0x07, - 0x2a, 0x28, 0x0a, 0x0c, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x00, 0x12, 0x0b, 0x0a, - 0x07, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x01, 0x32, 0xe0, 0x01, 0x0a, 0x13, 0x45, - 0x6c, 0x61, 0x73, 0x74, 0x69, 0x63, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x12, 0x2f, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0c, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0c, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x0c, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x12, 0x15, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x70, - 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x22, 0x5a, - 0x1d, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0xf8, 0x01, - 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x61, 0x64, 0x22, 0xb5, 0x03, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x63, 0x4d, 0x65, 0x74, 0x61, + 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x70, + 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x18, 0x0a, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x75, + 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x62, 0x75, 0x69, 0x6c, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, + 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, + 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x19, + 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x67, 0x69, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x47, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x72, 0x63, + 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1b, 0x0a, + 0x09, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x6c, + 0x61, 0x73, 0x74, 0x69, 0x63, 0x5f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x64, 0x18, 0x0e, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x65, 0x6c, 0x61, 0x73, 0x74, 0x69, 0x63, 0x4c, 0x69, 0x63, + 0x65, 0x6e, 0x73, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x0f, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x8f, 0x01, 0x0a, 0x0e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, + 0x3c, 0x0a, 0x0c, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x70, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x0c, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x39, 0x0a, + 0x10, 0x50, 0x72, 0x6f, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x25, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x4d, 0x65, 0x74, + 0x61, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x63, 0x73, 0x2a, 0x79, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x54, 0x41, 0x52, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x00, + 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x55, 0x52, 0x49, 0x4e, 0x47, 0x10, + 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x59, 0x10, 0x02, 0x12, 0x0c, + 0x0a, 0x08, 0x44, 0x45, 0x47, 0x52, 0x41, 0x44, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, + 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x54, 0x4f, 0x50, + 0x50, 0x49, 0x4e, 0x47, 0x10, 0x05, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x50, 0x47, 0x52, 0x41, 0x44, + 0x49, 0x4e, 0x47, 0x10, 0x06, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x4f, 0x4c, 0x4c, 0x42, 0x41, 0x43, + 0x4b, 0x10, 0x07, 0x2a, 0x28, 0x0a, 0x0c, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x00, + 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x01, 0x32, 0x93, 0x02, + 0x0a, 0x13, 0x45, 0x6c, 0x61, 0x73, 0x74, 0x69, 0x63, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x2f, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, + 0x65, 0x12, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x31, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x0c, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x17, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x42, 0x22, 0x5a, 0x1d, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x67, 0x65, 0x6e, 0x74, + 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0xf8, 0x01, 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -695,7 +932,7 @@ func file_control_proto_rawDescGZIP() []byte { } var file_control_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_control_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_control_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_control_proto_goTypes = []interface{}{ (Status)(0), // 0: proto.Status (ActionStatus)(0), // 1: proto.ActionStatus @@ -705,27 +942,32 @@ var file_control_proto_goTypes = []interface{}{ (*UpgradeRequest)(nil), // 5: proto.UpgradeRequest (*UpgradeResponse)(nil), // 6: proto.UpgradeResponse (*ApplicationStatus)(nil), // 7: proto.ApplicationStatus - (*StatusResponse)(nil), // 8: proto.StatusResponse + (*ProcMeta)(nil), // 8: proto.ProcMeta + (*StatusResponse)(nil), // 9: proto.StatusResponse + (*ProcMetaResponse)(nil), // 10: proto.ProcMetaResponse } var file_control_proto_depIdxs = []int32{ - 1, // 0: proto.RestartResponse.status:type_name -> proto.ActionStatus - 1, // 1: proto.UpgradeResponse.status:type_name -> proto.ActionStatus - 0, // 2: proto.ApplicationStatus.status:type_name -> proto.Status - 0, // 3: proto.StatusResponse.status:type_name -> proto.Status - 7, // 4: proto.StatusResponse.applications:type_name -> proto.ApplicationStatus - 2, // 5: proto.ElasticAgentControl.Version:input_type -> proto.Empty - 2, // 6: proto.ElasticAgentControl.Status:input_type -> proto.Empty - 2, // 7: proto.ElasticAgentControl.Restart:input_type -> proto.Empty - 5, // 8: proto.ElasticAgentControl.Upgrade:input_type -> proto.UpgradeRequest - 3, // 9: proto.ElasticAgentControl.Version:output_type -> proto.VersionResponse - 8, // 10: proto.ElasticAgentControl.Status:output_type -> proto.StatusResponse - 4, // 11: proto.ElasticAgentControl.Restart:output_type -> proto.RestartResponse - 6, // 12: proto.ElasticAgentControl.Upgrade:output_type -> proto.UpgradeResponse - 9, // [9:13] is the sub-list for method output_type - 5, // [5:9] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 1, // 0: proto.RestartResponse.status:type_name -> proto.ActionStatus + 1, // 1: proto.UpgradeResponse.status:type_name -> proto.ActionStatus + 0, // 2: proto.ApplicationStatus.status:type_name -> proto.Status + 0, // 3: proto.StatusResponse.status:type_name -> proto.Status + 7, // 4: proto.StatusResponse.applications:type_name -> proto.ApplicationStatus + 8, // 5: proto.ProcMetaResponse.procs:type_name -> proto.ProcMeta + 2, // 6: proto.ElasticAgentControl.Version:input_type -> proto.Empty + 2, // 7: proto.ElasticAgentControl.Status:input_type -> proto.Empty + 2, // 8: proto.ElasticAgentControl.Restart:input_type -> proto.Empty + 5, // 9: proto.ElasticAgentControl.Upgrade:input_type -> proto.UpgradeRequest + 2, // 10: proto.ElasticAgentControl.ProcMeta:input_type -> proto.Empty + 3, // 11: proto.ElasticAgentControl.Version:output_type -> proto.VersionResponse + 9, // 12: proto.ElasticAgentControl.Status:output_type -> proto.StatusResponse + 4, // 13: proto.ElasticAgentControl.Restart:output_type -> proto.RestartResponse + 6, // 14: proto.ElasticAgentControl.Upgrade:output_type -> proto.UpgradeResponse + 10, // 15: proto.ElasticAgentControl.ProcMeta:output_type -> proto.ProcMetaResponse + 11, // [11:16] is the sub-list for method output_type + 6, // [6:11] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_control_proto_init() } @@ -807,6 +1049,18 @@ func file_control_proto_init() { } } file_control_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProcMeta); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_control_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StatusResponse); i { case 0: return &v.state @@ -818,6 +1072,18 @@ func file_control_proto_init() { return nil } } + file_control_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProcMetaResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -825,7 +1091,7 @@ func file_control_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_control_proto_rawDesc, NumEnums: 2, - NumMessages: 7, + NumMessages: 9, NumExtensions: 0, NumServices: 1, }, @@ -860,6 +1126,8 @@ type ElasticAgentControlClient interface { Restart(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*RestartResponse, error) // Upgrade starts the upgrade process of Elastic Agent. Upgrade(ctx context.Context, in *UpgradeRequest, opts ...grpc.CallOption) (*UpgradeResponse, error) + // Gather all running process metadata. + ProcMeta(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ProcMetaResponse, error) } type elasticAgentControlClient struct { @@ -906,6 +1174,15 @@ func (c *elasticAgentControlClient) Upgrade(ctx context.Context, in *UpgradeRequ return out, nil } +func (c *elasticAgentControlClient) ProcMeta(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ProcMetaResponse, error) { + out := new(ProcMetaResponse) + err := c.cc.Invoke(ctx, "/proto.ElasticAgentControl/ProcMeta", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // ElasticAgentControlServer is the server API for ElasticAgentControl service. type ElasticAgentControlServer interface { // Fetches the currently running version of the Elastic Agent. @@ -916,6 +1193,8 @@ type ElasticAgentControlServer interface { Restart(context.Context, *Empty) (*RestartResponse, error) // Upgrade starts the upgrade process of Elastic Agent. Upgrade(context.Context, *UpgradeRequest) (*UpgradeResponse, error) + // Gather all running process metadata. + ProcMeta(context.Context, *Empty) (*ProcMetaResponse, error) } // UnimplementedElasticAgentControlServer can be embedded to have forward compatible implementations. @@ -934,6 +1213,9 @@ func (*UnimplementedElasticAgentControlServer) Restart(context.Context, *Empty) func (*UnimplementedElasticAgentControlServer) Upgrade(context.Context, *UpgradeRequest) (*UpgradeResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Upgrade not implemented") } +func (*UnimplementedElasticAgentControlServer) ProcMeta(context.Context, *Empty) (*ProcMetaResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ProcMeta not implemented") +} func RegisterElasticAgentControlServer(s *grpc.Server, srv ElasticAgentControlServer) { s.RegisterService(&_ElasticAgentControl_serviceDesc, srv) @@ -1011,6 +1293,24 @@ func _ElasticAgentControl_Upgrade_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _ElasticAgentControl_ProcMeta_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ElasticAgentControlServer).ProcMeta(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/proto.ElasticAgentControl/ProcMeta", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ElasticAgentControlServer).ProcMeta(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + var _ElasticAgentControl_serviceDesc = grpc.ServiceDesc{ ServiceName: "proto.ElasticAgentControl", HandlerType: (*ElasticAgentControlServer)(nil), @@ -1031,6 +1331,10 @@ var _ElasticAgentControl_serviceDesc = grpc.ServiceDesc{ MethodName: "Upgrade", Handler: _ElasticAgentControl_Upgrade_Handler, }, + { + MethodName: "ProcMeta", + Handler: _ElasticAgentControl_ProcMeta_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "control.proto", diff --git a/x-pack/elastic-agent/pkg/agent/control/server/server.go b/x-pack/elastic-agent/pkg/agent/control/server/server.go index 56f43a245fd0..072b212a7718 100644 --- a/x-pack/elastic-agent/pkg/agent/control/server/server.go +++ b/x-pack/elastic-agent/pkg/agent/control/server/server.go @@ -8,6 +8,9 @@ import ( "context" "encoding/json" "net" + "net/http" + "runtime" + "strings" "sync" "time" @@ -17,10 +20,15 @@ import ( "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/upgrade" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/control" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/control/proto" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/program" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" + monitoring "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/monitoring/beats" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/socket" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/status" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/fleetapi" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/release" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/sorted" ) // Server is the daemon side of the control protocol. @@ -29,11 +37,16 @@ type Server struct { rex reexec.ExecManager statusCtrl status.Controller up *upgrade.Upgrader + routeFn func() *sorted.Set listener net.Listener server *grpc.Server lock sync.RWMutex } +type specer interface { + Specs() map[string]program.Spec +} + // New creates a new control protocol server. func New(log *logger.Logger, rex reexec.ExecManager, statusCtrl status.Controller, up *upgrade.Upgrader) *Server { return &Server{ @@ -51,6 +64,13 @@ func (s *Server) SetUpgrader(up *upgrade.Upgrader) { s.up = up } +// SetRouteFn changes the route retrieval function. +func (s *Server) SetRouteFn(routesFetchFn func() *sorted.Set) { + s.lock.Lock() + defer s.lock.Unlock() + s.routeFn = routesFetchFn +} + // Start starts the GRPC endpoint and accepts new connections. func (s *Server) Start() error { if s.server != nil { @@ -147,6 +167,113 @@ func (s *Server) Upgrade(ctx context.Context, request *proto.UpgradeRequest) (*p }, nil } +// BeatInfo is the metadata response a beat will provide when the root ("/") is queried. +type BeatInfo struct { + Beat string `json:"beat"` + Name string `json:"name"` + Hostname string `json:"hostname"` + ID string `json:"uuid"` + EphemeralID string `json:"ephemeral_id"` + Version string `json:"version"` + Commit string `json:"build_commit"` + Time string `json:"build_time"` + Username string `json:"username"` + UserID string `json:"uid"` + GroupID string `json:"gid"` + BinaryArch string `json:"binary_arch"` + ElasticLicensed bool `json:"elastic_licensed"` +} + +// ProcMeta returns version and beat inforation for all running processes. +func (s *Server) ProcMeta(ctx context.Context, _ *proto.Empty) (*proto.ProcMetaResponse, error) { + if s.routeFn == nil { + return nil, errors.New("route function is nil") + } + + resp := &proto.ProcMetaResponse{ + Procs: []*proto.ProcMeta{}, + } + + routes := s.routeFn() + for _, rk := range routes.Keys() { + programs, ok := routes.Get(rk) + if !ok { + s.logger.With("route_key", rk).Warn("Unable to retrieve route.") + continue + } + + sp, ok := programs.(specer) + if !ok { + s.logger.With("route_key", rk, "route", programs).Warn("Unable to cast route as specer.") + continue + } + specs := sp.Specs() + + for n, spec := range specs { + procMeta := &proto.ProcMeta{ + Name: n, + RouteKey: rk, + } + + client := http.Client{ + Timeout: time.Second * 5, + } + endpoint := monitoring.MonitoringEndpoint(spec, runtime.GOOS, rk) + if strings.HasPrefix(endpoint, "unix://") { + client.Transport = &http.Transport{ + Proxy: nil, + DialContext: socket.DialContext(strings.TrimPrefix(endpoint, "unix://")), + } + endpoint = "unix" + } else if strings.HasPrefix(endpoint, "npipe://") { + client.Transport = &http.Transport{ + Proxy: nil, + DialContext: socket.DialContext(strings.TrimPrefix(endpoint, "npipe:///")), + } + endpoint = "npipe" + } + + res, err := client.Get("http://" + endpoint + "/") + if err != nil { + procMeta.Error = err.Error() + resp.Procs = append(resp.Procs, procMeta) + continue + } + if res.StatusCode != 200 { + procMeta.Error = "response status is: " + res.Status + resp.Procs = append(resp.Procs, procMeta) + continue + } + + bi := &BeatInfo{} + dec := json.NewDecoder(res.Body) + if err := dec.Decode(bi); err != nil { + res.Body.Close() + procMeta.Error = err.Error() + resp.Procs = append(resp.Procs, procMeta) + continue + } + res.Body.Close() + + procMeta.Process = bi.Beat + procMeta.Hostname = bi.Hostname + procMeta.Id = bi.ID + procMeta.EphemeralId = bi.EphemeralID + procMeta.Version = bi.Version + procMeta.BuildCommit = bi.Commit + procMeta.BuildTime = bi.Time + procMeta.Username = bi.Username + procMeta.UserId = bi.UserID + procMeta.UserGid = bi.GroupID + procMeta.Architecture = bi.BinaryArch + procMeta.ElasticLicensed = bi.ElasticLicensed + + resp.Procs = append(resp.Procs, procMeta) + } + } + return resp, nil +} + type upgradeRequest struct { *proto.UpgradeRequest } diff --git a/x-pack/elastic-agent/pkg/agent/operation/operator.go b/x-pack/elastic-agent/pkg/agent/operation/operator.go index 2cd68512606a..8e83efbd94ba 100644 --- a/x-pack/elastic-agent/pkg/agent/operation/operator.go +++ b/x-pack/elastic-agent/pkg/agent/operation/operator.go @@ -135,6 +135,21 @@ func (o *Operator) State() map[string]state.State { return result } +// Specs returns all program specifications +func (o *Operator) Specs() map[string]program.Spec { + r := make(map[string]program.Spec) + + o.appsLock.Lock() + defer o.appsLock.Unlock() + + for _, app := range o.apps { + // use app.Name() instead of the (map) key so we can easy find the "_monitoring" processes + r[app.Name()] = app.Spec() + } + + return r +} + // Close stops all programs handled by operator and clears state func (o *Operator) Close() error { o.monitor.Close() diff --git a/x-pack/elastic-agent/pkg/core/socket/dial.go b/x-pack/elastic-agent/pkg/core/socket/dial.go new file mode 100644 index 000000000000..e2c777d6573c --- /dev/null +++ b/x-pack/elastic-agent/pkg/core/socket/dial.go @@ -0,0 +1,23 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +//go:build !windows +// +build !windows + +package socket + +import ( + "context" + "net" +) + +// DialContext returns a function that can be used to dial a local unix-domain socket. +func DialContext(socket string) func(context.Context, string, string) (net.Conn, error) { + return func(ctx context.Context, _, _ string) (net.Conn, error) { + var d net.Dialer + d.LocalAddr = nil + addr := net.UnixAddr{Name: socket, Net: "unix"} + return d.DialContext(ctx, "unix", addr.String()) + } +} diff --git a/x-pack/elastic-agent/pkg/core/socket/dial_windows.go b/x-pack/elastic-agent/pkg/core/socket/dial_windows.go new file mode 100644 index 000000000000..9805a5f50b6c --- /dev/null +++ b/x-pack/elastic-agent/pkg/core/socket/dial_windows.go @@ -0,0 +1,22 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +//go:build windows +// +build windows + +package socket + +import ( + "context" + "net" + + winio "github.com/Microsoft/go-winio" +) + +// DialContext returns a function that can be used to dial a local Windows npipe. +func DialContext(socket string) func(context.Context, string, string) (net.Conn, error) { + return func(ctx context.Context, _, _ string) (net.Conn, error) { + return winio.DialPipeContext(ctx, `\\.\pipe\`+socket) + } +} diff --git a/x-pack/elastic-agent/pkg/core/socket/doc.go b/x-pack/elastic-agent/pkg/core/socket/doc.go new file mode 100644 index 000000000000..6ef7db7f066c --- /dev/null +++ b/x-pack/elastic-agent/pkg/core/socket/doc.go @@ -0,0 +1,6 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +// Package socket provides a DialContext function to access unix domain functions or windows npipes. +package socket