Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cherry-pick #19248 to 7.x: [Elastic Agent] Support the install, control, and uninstall of Endpoint #19497

Merged
merged 1 commit into from
Jun 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions x-pack/elastic-agent/CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,4 @@
- Change stream.* to dataset.* fields {pull}18967[18967]
- Agent now runs the GRPC server and spawned application connect by to Agent {pull}18973[18973]
- Rename input.type logs to logfile {pull}19360[19360]
- Agent now installs/uninstalls Elastic Endpoint {pull}19248[19248]
6 changes: 3 additions & 3 deletions x-pack/elastic-agent/dev-tools/cmd/buildspec/buildspec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions x-pack/elastic-agent/pkg/agent/application/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,12 @@ func localConfigDefault() *localConfig {
type FleetAgentConfig struct {
API *APIAccess `config:"api" yaml:"api"`
Reporting *LogReporting `config:"reporting" yaml:"reporting"`
Info *AgentInfo `config:"agent_info" yaml:"agent_info"`
Info *AgentInfo `config:"agent" yaml:"agent"`
}

// AgentInfo is a set of agent information.
type AgentInfo struct {
ID string `json:"ID" yaml:"ID" config:"ID"`
ID string `json:"id" yaml:"id" config:"id"`
}

// APIAccess contains the required details to connect to the Kibana endpoint.
Expand Down
8 changes: 4 additions & 4 deletions x-pack/elastic-agent/pkg/agent/application/fleet_decorator.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ func injectFleet(cfg *config.Config) func(*logger.Logger, *transpiler.AST) error
}
api, ok := transpiler.Lookup(ast, "api")
if !ok {
return fmt.Errorf("failed to get api from fleet config")
return fmt.Errorf("failed to get api key from fleet config")
}
agentInfo, ok := transpiler.Lookup(ast, "agent_info")
agent, ok := transpiler.Lookup(ast, "agent")
if !ok {
return fmt.Errorf("failed to get agent_info from fleet config")
return fmt.Errorf("failed to get agent key from fleet config")
}
fleet := transpiler.NewDict([]transpiler.Node{agentInfo, api})
fleet := transpiler.NewDict([]transpiler.Node{agent, api})
err = transpiler.Insert(rootAst, fleet, "fleet")
if err != nil {
return err
Expand Down
4 changes: 2 additions & 2 deletions x-pack/elastic-agent/pkg/agent/application/info/agent_id.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ import (

// defaultAgentConfigFile is a name of file used to store agent information
const defaultAgentConfigFile = "fleet.yml"
const agentInfoKey = "agent_info"
const agentInfoKey = "agent"

// defaultAgentActionStoreFile is the file that will contains the action that can be replayed after restart.
const defaultAgentActionStoreFile = "action_store.yml"

type persistentAgentInfo struct {
ID string `json:"ID" yaml:"ID" config:"ID"`
ID string `json:"id" yaml:"id" config:"id"`
}

type ioStore interface {
Expand Down
7 changes: 7 additions & 0 deletions x-pack/elastic-agent/pkg/agent/application/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/stateresolver"
downloader "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact/download/localremote"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact/install"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact/uninstall"
"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/monitoring"
Expand Down Expand Up @@ -66,6 +67,11 @@ func newOperator(ctx context.Context, log *logger.Logger, id routingKey, config
return nil, errors.New(err, "initiating installer")
}

uninstaller, err := uninstall.NewUninstaller()
if err != nil {
return nil, errors.New(err, "initiating uninstaller")
}

stateResolver, err := stateresolver.NewStateResolver(log)
if err != nil {
return nil, err
Expand All @@ -79,6 +85,7 @@ func newOperator(ctx context.Context, log *logger.Logger, id routingKey, config
fetcher,
verifier,
installer,
uninstaller,
stateResolver,
srv,
r,
Expand Down
27 changes: 20 additions & 7 deletions x-pack/elastic-agent/pkg/agent/operation/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import (
"testing"
"time"

"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/program"

operatorCfg "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/operation/config"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/program"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/stateresolver"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact/download"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact/install"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact/uninstall"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/config"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/app"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger"
Expand Down Expand Up @@ -55,7 +55,8 @@ func getTestOperator(t *testing.T, downloadPath string, installPath string, p *a

fetcher := &DummyDownloader{}
verifier := &DummyVerifier{}
installer := &DummyInstaller{}
installer := &DummyInstallerChecker{}
uninstaller := &DummyUninstaller{}

stateResolver, err := stateresolver.NewStateResolver(l)
if err != nil {
Expand All @@ -70,7 +71,7 @@ func getTestOperator(t *testing.T, downloadPath string, installPath string, p *a
t.Fatal(err)
}

operator, err := NewOperator(context.Background(), l, "p1", cfg, fetcher, verifier, installer, stateResolver, srv, nil, noop.NewMonitor())
operator, err := NewOperator(context.Background(), l, "p1", cfg, fetcher, verifier, installer, uninstaller, stateResolver, srv, nil, noop.NewMonitor())
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -157,10 +158,22 @@ func (*DummyVerifier) Verify(p, v string) (bool, error) {

var _ download.Verifier = &DummyVerifier{}

type DummyInstaller struct{}
type DummyInstallerChecker struct{}

func (*DummyInstallerChecker) Check(_ context.Context, p, v, _ string) error {
return nil
}

func (*DummyInstallerChecker) Install(_ context.Context, p, v, _ string) error {
return nil
}

var _ install.InstallerChecker = &DummyInstallerChecker{}

type DummyUninstaller struct{}

func (*DummyInstaller) Install(p, v, _ string) error {
func (*DummyUninstaller) Uninstall(_ context.Context, p, v, _ string) error {
return nil
}

var _ install.Installer = &DummyInstaller{}
var _ uninstall.Uninstaller = &DummyUninstaller{}
8 changes: 5 additions & 3 deletions x-pack/elastic-agent/pkg/agent/operation/monitoring_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ func getMonitorableTestOperator(t *testing.T, installPath string, m monitoring.M

fetcher := &DummyDownloader{}
verifier := &DummyVerifier{}
installer := &DummyInstaller{}
installer := &DummyInstallerChecker{}
uninstaller := &DummyUninstaller{}

stateResolver, err := stateresolver.NewStateResolver(l)
if err != nil {
Expand All @@ -132,7 +133,7 @@ func getMonitorableTestOperator(t *testing.T, installPath string, m monitoring.M
}

ctx := context.Background()
operator, err := NewOperator(ctx, l, "p1", cfg, fetcher, verifier, installer, stateResolver, srv, nil, m)
operator, err := NewOperator(ctx, l, "p1", cfg, fetcher, verifier, installer, uninstaller, stateResolver, srv, nil, m)
if err != nil {
t.Fatal(err)
}
Expand All @@ -146,7 +147,8 @@ type testMonitorableApp struct {
monitor monitoring.Monitor
}

func (*testMonitorableApp) Name() string { return "" }
func (*testMonitorableApp) Name() string { return "" }
func (*testMonitorableApp) Started() bool { return false }
func (*testMonitorableApp) Start(_ context.Context, _ app.Taggable, cfg map[string]interface{}) error {
return nil
}
Expand Down
3 changes: 2 additions & 1 deletion x-pack/elastic-agent/pkg/agent/operation/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@ type operation interface {
// examples:
// - Start does not need to run if process is running
// - Fetch does not need to run if package is already present
Check(application Application) (bool, error)
Check(ctx context.Context, application Application) (bool, error)
// Run runs the operation
Run(ctx context.Context, application Application) error
}

// Application is an application capable of being started, stopped and configured.
type Application interface {
Name() string
Started() bool
Start(ctx context.Context, p app.Taggable, cfg map[string]interface{}) error
Stop()
Configure(ctx context.Context, config map[string]interface{}) error
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (o *operationConfig) Name() string {
// Check checks whether config needs to be run.
//
// Always returns true.
func (o *operationConfig) Check(_ Application) (bool, error) { return true, nil }
func (o *operationConfig) Check(_ context.Context, _ Application) (bool, error) { return true, nil }

// Run runs the operation
func (o *operationConfig) Run(ctx context.Context, application Application) (err error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (o *operationFetch) Name() string {
// Check checks whether fetch needs to occur.
//
// If the artifacts already exists then fetch will not be ran.
func (o *operationFetch) Check(_ Application) (bool, error) {
func (o *operationFetch) Check(_ context.Context, _ Application) (bool, error) {
downloadConfig := o.operatorConfig.DownloadConfig
fullPath, err := artifact.GetArtifactPath(o.program.BinaryName(), o.program.Version(), downloadConfig.OS(), downloadConfig.Arch(), downloadConfig.TargetDirectory)
if err != nil {
Expand Down
18 changes: 10 additions & 8 deletions x-pack/elastic-agent/pkg/agent/operation/operation_install.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package operation

import (
"context"
"os"

"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/operation/config"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact/install"
Expand All @@ -20,14 +19,14 @@ type operationInstall struct {
logger *logger.Logger
program Descriptor
operatorConfig *config.Config
installer install.Installer
installer install.InstallerChecker
}

func newOperationInstall(
logger *logger.Logger,
program Descriptor,
operatorConfig *config.Config,
installer install.Installer) *operationInstall {
installer install.InstallerChecker) *operationInstall {

return &operationInstall{
logger: logger,
Expand All @@ -45,10 +44,13 @@ func (o *operationInstall) Name() string {
// Check checks whether install needs to be ran.
//
// If the installation directory already exists then it will not be ran.
func (o *operationInstall) Check(_ Application) (bool, error) {
installDir := o.program.Directory()
_, err := os.Stat(installDir)
return os.IsNotExist(err), nil
func (o *operationInstall) Check(ctx context.Context, _ Application) (bool, error) {
err := o.installer.Check(ctx, o.program.BinaryName(), o.program.Version(), o.program.Directory())
if err != nil {
// don't return err, just state if Run should be called
return true, nil
}
return false, nil
}

// Run runs the operation
Expand All @@ -59,5 +61,5 @@ func (o *operationInstall) Run(ctx context.Context, application Application) (er
}
}()

return o.installer.Install(o.program.BinaryName(), o.program.Version(), o.program.Directory())
return o.installer.Install(ctx, o.program.BinaryName(), o.program.Version(), o.program.Directory())
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (o *operationRemove) Name() string {
// Check checks whether remove needs to run.
//
// Always returns false.
func (o *operationRemove) Check(_ Application) (bool, error) {
func (o *operationRemove) Check(_ context.Context, _ Application) (bool, error) {
return false, nil
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ func (o *retryableOperations) Name() string {
// examples:
// - Start does not need to run if process is running
// - Fetch does not need to run if package is already present
func (o *retryableOperations) Check(application Application) (bool, error) {
func (o *retryableOperations) Check(ctx context.Context, application Application) (bool, error) {
for _, op := range o.operations {
// finish early if at least one operation needs to be run or errored out
if run, err := op.Check(application); err != nil || run {
if run, err := op.Check(ctx, application); err != nil || run {
return run, err
}
}
Expand All @@ -71,7 +71,7 @@ func (o *retryableOperations) runOnce(application Application) func(context.Cont
return ctx.Err()
}

shouldRun, err := op.Check(application)
shouldRun, err := op.Check(ctx, application)
if err != nil {
return err
}
Expand Down
8 changes: 4 additions & 4 deletions x-pack/elastic-agent/pkg/agent/operation/operation_start.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ func (o *operationStart) Name() string {
// Only starts the application when in stopped state, any other state
// and the application is handled by the life cycle inside of the `Application`
// implementation.
func (o *operationStart) Check(application Application) (bool, error) {
if application.State().Status == state.Stopped {
return true, nil
func (o *operationStart) Check(_ context.Context, application Application) (bool, error) {
if application.Started() {
return false, nil
}
return false, nil
return true, nil
}

// Run runs the operation
Expand Down
2 changes: 1 addition & 1 deletion x-pack/elastic-agent/pkg/agent/operation/operation_stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (o *operationStop) Name() string {
// Check checks whether application needs to be stopped.
//
// If the application state is not stopped then stop should be performed.
func (o *operationStop) Check(application Application) (bool, error) {
func (o *operationStop) Check(_ context.Context, application Application) (bool, error) {
if application.State().Status != state.Stopped {
return true, nil
}
Expand Down
55 changes: 55 additions & 0 deletions x-pack/elastic-agent/pkg/agent/operation/operation_uninstall.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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 operation

import (
"context"

"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact/uninstall"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/state"
)

// operationUninstall uninstalls a artifact from predefined location
type operationUninstall struct {
logger *logger.Logger
program Descriptor
uninstaller uninstall.Uninstaller
}

func newOperationUninstall(
logger *logger.Logger,
program Descriptor,
uninstaller uninstall.Uninstaller) *operationUninstall {

return &operationUninstall{
logger: logger,
program: program,
uninstaller: uninstaller,
}
}

// Name is human readable name identifying an operation
func (o *operationUninstall) Name() string {
return "operation-uninstall"
}

// Check checks whether uninstall needs to be ran.
//
// Always true.
func (o *operationUninstall) Check(_ context.Context, _ Application) (bool, error) {
return true, nil
}

// Run runs the operation
func (o *operationUninstall) Run(ctx context.Context, application Application) (err error) {
defer func() {
if err != nil {
application.SetState(state.Failed, err.Error())
}
}()

return o.uninstaller.Uninstall(ctx, o.program.BinaryName(), o.program.Version(), o.program.Directory())
}
Loading