Skip to content

Commit

Permalink
[Elastic-Agent] Enable introspecting configuration (#18124)
Browse files Browse the repository at this point in the history
* introspection

* changelog

* mage fmt

* introspect -> inspect

* follow -c argument

* conflicts
  • Loading branch information
michalpristas authored May 4, 2020
1 parent 6ea21a9 commit 1c2e4b9
Show file tree
Hide file tree
Showing 6 changed files with 418 additions and 1 deletion.
1 change: 1 addition & 0 deletions x-pack/elastic-agent/CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,5 @@
- Use data subfolder as default for process logs {pull}17960[17960]
- Do not require unnecessary configuration {pull}18003[18003]
- Enable debug log level for Metricbeat and Filebeat when run under the Elastic Agent. {pull}17935[17935]
- Enable introspecting configuration {pull}18124[18124]
- Follow home path for all config files {pull}18161[18161]
6 changes: 5 additions & 1 deletion x-pack/elastic-agent/pkg/agent/application/emitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ type configModifiers struct {
Decorators []decoratorFunc
}

func emitter(log *logger.Logger, router *router, modifiers *configModifiers, reloadables ...reloadable) emitterFunc {
type programsDispatcher interface {
Dispatch(id string, grpProg map[routingKey][]program.Program) error
}

func emitter(log *logger.Logger, router programsDispatcher, modifiers *configModifiers, reloadables ...reloadable) emitterFunc {
return func(c *config.Config) error {
if err := InjectAgentConfig(c); err != nil {
return err
Expand Down
131 changes: 131 additions & 0 deletions x-pack/elastic-agent/pkg/agent/application/introspect_config_cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// 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 application

import (
"fmt"

yaml "gopkg.in/yaml.v2"

"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/storage"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/config"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/fleetapi"
)

// IntrospectConfigCmd is an introspect subcommand that shows configurations of the agent.
type IntrospectConfigCmd struct {
cfgPath string
}

// NewIntrospectConfigCmd creates a new introspect command.
func NewIntrospectConfigCmd(configPath string,
) (*IntrospectConfigCmd, error) {
return &IntrospectConfigCmd{
cfgPath: configPath,
}, nil
}

// Execute introspects agent configuration.
func (c *IntrospectConfigCmd) Execute() error {
return c.introspectConfig()
}

func (c *IntrospectConfigCmd) introspectConfig() error {
cfg, err := loadConfig(c.cfgPath)
if err != nil {
return err
}

isLocal, err := isLocalMode(cfg)
if err != nil {
return err
}

if isLocal {
return printConfig(cfg)
}

fleetConfig, err := loadFleetConfig(cfg)
if err != nil {
return err
} else if fleetConfig == nil {
return errors.New("no fleet config retrieved yet")
}

return printMapStringConfig(fleetConfig)
}

func loadConfig(configPath string) (*config.Config, error) {
rawConfig, err := config.LoadYAML(configPath)
if err != nil {
return nil, err
}

if err := InjectAgentConfig(rawConfig); err != nil {
return nil, err
}

return rawConfig, nil
}

func loadFleetConfig(cfg *config.Config) (map[string]interface{}, error) {
log, err := newErrorLogger()
if err != nil {
return nil, err
}

as, err := newActionStore(log, storage.NewDiskStore(info.AgentActionStoreFile()))
if err != nil {
return nil, err
}

for _, c := range as.Actions() {
cfgChange, ok := c.(*fleetapi.ActionConfigChange)
if !ok {
continue
}

fmt.Println("Action ID:", cfgChange.ID())
return cfgChange.Config, nil
}
return nil, nil
}

func isLocalMode(rawConfig *config.Config) (bool, error) {
c := localDefaultConfig()
if err := rawConfig.Unpack(&c); err != nil {
return false, errors.New(err, "initiating application")
}

managementConfig := struct {
Mode string `config:"mode" yaml:"mode"`
}{}

if err := c.Management.Unpack(&managementConfig); err != nil {
return false, errors.New(err, "initiating application")
}
return managementConfig.Mode == "local", nil
}

func printMapStringConfig(mapStr map[string]interface{}) error {
data, err := yaml.Marshal(mapStr)
if err != nil {
return errors.New(err, "could not marshal to YAML")
}

fmt.Println(string(data))
return nil
}

func printConfig(cfg *config.Config) error {
mapStr, err := cfg.ToMapStr()
if err != nil {
return err
}

return printMapStringConfig(mapStr)
}
211 changes: 211 additions & 0 deletions x-pack/elastic-agent/pkg/agent/application/introspect_output_cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
// 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 application

import (
"fmt"

"github.com/urso/ecslog"
"github.com/urso/ecslog/backend"
"github.com/urso/ecslog/backend/appender"
"github.com/urso/ecslog/backend/layout"

"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/filters"
"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/config"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/plugin/app/monitoring/noop"
)

// IntrospectOutputCmd is an introspect subcommand that shows configurations of the agent.
type IntrospectOutputCmd struct {
cfgPath string
output string
program string
}

// NewIntrospectOutputCmd creates a new introspect command.
func NewIntrospectOutputCmd(configPath, output, program string) (*IntrospectOutputCmd, error) {
return &IntrospectOutputCmd{
cfgPath: configPath,
output: output,
program: program,
}, nil
}

// Execute tries to enroll the agent into Fleet.
func (c *IntrospectOutputCmd) Execute() error {
if c.output == "" {
return c.introspectOutputs()
}

return c.introspectOutput()
}

func (c *IntrospectOutputCmd) introspectOutputs() error {
cfg, err := loadConfig(c.cfgPath)
if err != nil {
return err
}

isLocal, err := isLocalMode(cfg)
if err != nil {
return err
}

l, err := newErrorLogger()
if err != nil {
return err
}

if isLocal {
return listOutputsFromConfig(l, cfg)
}

fleetConfig, err := loadFleetConfig(cfg)
if err != nil {
return err
} else if fleetConfig == nil {
return errors.New("no fleet config retrieved yet")
}

return listOutputsFromMap(l, fleetConfig)
}

func listOutputsFromConfig(log *logger.Logger, cfg *config.Config) error {
programsGroup, err := getProgramsFromConfig(log, cfg)
if err != nil {
return err

}

for k := range programsGroup {
fmt.Println(k)
}

return nil
}

func listOutputsFromMap(log *logger.Logger, cfg map[string]interface{}) error {
c, err := config.NewConfigFrom(cfg)
if err != nil {
return err
}

return listOutputsFromConfig(log, c)
}

func (c *IntrospectOutputCmd) introspectOutput() error {
cfg, err := loadConfig(c.cfgPath)
if err != nil {
return err
}

l, err := newErrorLogger()
if err != nil {
return err
}

isLocal, err := isLocalMode(cfg)
if err != nil {
return err
}

if isLocal {
return printOutputFromConfig(l, c.output, c.program, cfg)
}

fleetConfig, err := loadFleetConfig(cfg)
if err != nil {
return err
} else if fleetConfig == nil {
return errors.New("no fleet config retrieved yet")
}

return printOutputFromMap(l, c.output, c.program, fleetConfig)
}

func printOutputFromConfig(log *logger.Logger, output, programName string, cfg *config.Config) error {
programsGroup, err := getProgramsFromConfig(log, cfg)
if err != nil {
return err

}

for k, programs := range programsGroup {
if k != output {
continue
}

var programFound bool
for _, p := range programs {
if programName != "" && programName != p.Spec.Cmd {
continue
}

programFound = true
fmt.Printf("[%s] %s:\n", k, p.Spec.Cmd)
printMapStringConfig(p.Configuration())
fmt.Println("---")
}

if !programFound {
fmt.Printf("program '%s' is not recognized within output '%s', try running `elastic-agent introspect output` to find available outputs.\n",
programName,
output)
}
return nil
}

fmt.Printf("output '%s' is not recognized, try running `elastic-agent introspect output` to find available outputs.\n", output)

return nil
}

func printOutputFromMap(log *logger.Logger, output, programName string, cfg map[string]interface{}) error {
c, err := config.NewConfigFrom(cfg)
if err != nil {
return err
}

return printOutputFromConfig(log, output, programName, c)
}

func getProgramsFromConfig(log *logger.Logger, cfg *config.Config) (map[string][]program.Program, error) {
monitor := noop.NewMonitor()
router := &inmemRouter{}
emit := emitter(
log,
router,
&configModifiers{
Decorators: []decoratorFunc{injectMonitoring},
Filters: []filterFunc{filters.ConstraintFilter},
},
monitor,
)

if err := emit(cfg); err != nil {
return nil, err
}
return router.programs, nil
}

type inmemRouter struct {
programs map[string][]program.Program
}

func (r *inmemRouter) Dispatch(id string, grpProg map[routingKey][]program.Program) error {
r.programs = grpProg
return nil
}

func newErrorLogger() (*logger.Logger, error) {
backend, err := appender.Console(backend.Error, layout.Text(true))
if err != nil {
return nil, err
}
return ecslog.New(backend), nil
}
1 change: 1 addition & 0 deletions x-pack/elastic-agent/pkg/agent/cmd/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func NewCommandWithArgs(args []string, streams *cli.IOStreams) *cobra.Command {
cmd.AddCommand(basecmd.NewDefaultCommandsWithArgs(args, streams)...)
cmd.AddCommand(newRunCommandWithArgs(flags, args, streams))
cmd.AddCommand(newEnrollCommandWithArgs(flags, args, streams))
cmd.AddCommand(newIntrospectCommandWithArgs(flags, args, streams))

return cmd
}
Loading

0 comments on commit 1c2e4b9

Please sign in to comment.