From 5c2eb42795b5c84856c06a90ae2a0da8f43b45e9 Mon Sep 17 00:00:00 2001 From: Michal Pristas Date: Wed, 1 Apr 2020 10:46:57 +0200 Subject: [PATCH] [Agent] Enable post install hooks (#17241) [Agent] Enable post install hooks (#17241) --- x-pack/agent/CHANGELOG.asciidoc | 1 + x-pack/agent/pkg/agent/program/spec.go | 13 +- x-pack/agent/pkg/agent/program/spec_test.go | 12 + x-pack/agent/pkg/agent/program/supported.go | 2 +- x-pack/agent/pkg/agent/transpiler/rules.go | 2 +- x-pack/agent/pkg/agent/transpiler/steps.go | 248 ++++++++++++++++++ .../agent/pkg/agent/transpiler/steps_test.go | 66 +++++ .../artifact/install/hooks/hooks_installer.go | 57 ++++ .../agent/pkg/artifact/install/installer.go | 13 +- x-pack/agent/spec/metricbeat.yml | 4 + 10 files changed, 408 insertions(+), 10 deletions(-) create mode 100644 x-pack/agent/pkg/agent/transpiler/steps.go create mode 100644 x-pack/agent/pkg/agent/transpiler/steps_test.go create mode 100644 x-pack/agent/pkg/artifact/install/hooks/hooks_installer.go diff --git a/x-pack/agent/CHANGELOG.asciidoc b/x-pack/agent/CHANGELOG.asciidoc index 8347f781cf2..1f28dd9d08d 100644 --- a/x-pack/agent/CHANGELOG.asciidoc +++ b/x-pack/agent/CHANGELOG.asciidoc @@ -19,5 +19,6 @@ - Generate index name in a format type-dataset-namespace {pull}16903[16903] - OS agnostic default configuration {pull}17016[17016] +- Introduced post install hooks {pull}17241[17241] - Support for config constraints {pull}17112[17112] - Display the stability of the agent at enroll and start. {pull}17336[17336] diff --git a/x-pack/agent/pkg/agent/program/spec.go b/x-pack/agent/pkg/agent/program/spec.go index 831406bf995..c9cfe9a7d0b 100644 --- a/x-pack/agent/pkg/agent/program/spec.go +++ b/x-pack/agent/pkg/agent/program/spec.go @@ -27,12 +27,13 @@ var ErrMissingWhen = errors.New("program must define a 'When' expression") // NOTE: Current spec are build at compile time, we want to revisit that to allow other program // to register their spec in a secure way. type Spec struct { - Name string `yaml:"name"` - Cmd string `yaml:"cmd"` - Configurable string `yaml:"configurable"` - Args []string `yaml:"args"` - Rules *transpiler.RuleList `yaml:"rules"` - When string `yaml:"when"` + Name string `yaml:"name"` + Cmd string `yaml:"cmd"` + Configurable string `yaml:"configurable"` + Args []string `yaml:"args"` + Rules *transpiler.RuleList `yaml:"rules"` + PostInstallSteps *transpiler.StepList `yaml:"post_install"` + When string `yaml:"when"` } // ReadSpecs reads all the specs that match the provided globbing path. diff --git a/x-pack/agent/pkg/agent/program/spec_test.go b/x-pack/agent/pkg/agent/program/spec_test.go index 67fcb6ac759..fd3db0374b2 100644 --- a/x-pack/agent/pkg/agent/program/spec_test.go +++ b/x-pack/agent/pkg/agent/program/spec_test.go @@ -44,6 +44,10 @@ func TestSerialization(t *testing.T) { "log", ), ), + PostInstallSteps: transpiler.NewStepList( + transpiler.DeleteFile("d-1", true), + transpiler.MoveFile("m-1", "m-2", false), + ), When: "1 == 1", } yml := `name: hello @@ -85,6 +89,14 @@ rules: key: type values: - log +post_install: +- delete_file: + path: d-1 + fail_on_missing: true +- move_file: + path: m-1 + target: m-2 + fail_on_missing: false when: 1 == 1 ` t.Run("serialization", func(t *testing.T) { diff --git a/x-pack/agent/pkg/agent/program/supported.go b/x-pack/agent/pkg/agent/program/supported.go index 0640627860b..b6b8cbca19d 100644 --- a/x-pack/agent/pkg/agent/program/supported.go +++ b/x-pack/agent/pkg/agent/program/supported.go @@ -19,7 +19,7 @@ func init() { // Packed Files // spec/filebeat.yml // spec/metricbeat.yml - unpacked := packer.MustUnpack("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") + unpacked := packer.MustUnpack("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") SupportedMap = make(map[string]bool) for f, v := range unpacked { diff --git a/x-pack/agent/pkg/agent/transpiler/rules.go b/x-pack/agent/pkg/agent/transpiler/rules.go index ebc1d5b2eb0..96f16d18b6d 100644 --- a/x-pack/agent/pkg/agent/transpiler/rules.go +++ b/x-pack/agent/pkg/agent/transpiler/rules.go @@ -85,7 +85,7 @@ func (r *RuleList) MarshalYAML() (interface{}, error) { return doc, nil } -// UnmarshalYAML unmashal a YAML document into a RuleList. +// UnmarshalYAML unmarshal a YAML document into a RuleList. func (r *RuleList) UnmarshalYAML(unmarshal func(interface{}) error) error { var unpackTo []map[string]interface{} diff --git a/x-pack/agent/pkg/agent/transpiler/steps.go b/x-pack/agent/pkg/agent/transpiler/steps.go new file mode 100644 index 00000000000..d436999a432 --- /dev/null +++ b/x-pack/agent/pkg/agent/transpiler/steps.go @@ -0,0 +1,248 @@ +// 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 transpiler + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "strings" + + "gopkg.in/yaml.v2" +) + +// StepList is a container that allow the same tree to be executed on multiple defined Step. +type StepList struct { + Steps []Step +} + +// NewStepList returns a new list of rules to be executed. +func NewStepList(steps ...Step) *StepList { + return &StepList{Steps: steps} +} + +// Step is an execution step which needs to be run. +type Step interface { + Execute(rootDir string) error +} + +// Execute executes a list of steps. +func (r *StepList) Execute(rootDir string) error { + var err error + for _, step := range r.Steps { + err = step.Execute(rootDir) + if err != nil { + return err + } + } + + return nil +} + +// MarshalYAML marsharl a steps list to YAML. +func (r *StepList) MarshalYAML() (interface{}, error) { + doc := make([]map[string]Step, 0, len(r.Steps)) + + for _, step := range r.Steps { + var name string + switch step.(type) { + case *DeleteFileStep: + name = "delete_file" + case *MoveFileStep: + name = "move_file" + + default: + return nil, fmt.Errorf("unknown rule of type %T", step) + } + + subdoc := map[string]Step{ + name: step, + } + + doc = append(doc, subdoc) + } + return doc, nil +} + +// UnmarshalYAML unmarshal a YAML document into a RuleList. +func (r *StepList) UnmarshalYAML(unmarshal func(interface{}) error) error { + var unpackTo []map[string]interface{} + + err := unmarshal(&unpackTo) + if err != nil { + return err + } + + // NOTE: this is a bit of a hack because I want to make sure + // the unpack strategy stay in the struct implementation and yaml + // doesn't have a RawMessage similar to the JSON package, so partial unpack + // is not possible. + unpack := func(in interface{}, out interface{}) error { + b, err := yaml.Marshal(in) + if err != nil { + return err + } + return yaml.Unmarshal(b, out) + } + + var steps []Step + + for _, m := range unpackTo { + ks := keys(m) + if len(ks) > 1 { + return fmt.Errorf("unknown rule identifier, expecting one identifier and received %d", len(ks)) + } + + name := ks[0] + fields := m[name] + + var s Step + switch name { + case "delete_file": + s = &DeleteFileStep{} + case "move_file": + s = &MoveFileStep{} + default: + return fmt.Errorf("unknown rule of type %s", name) + } + + if err := unpack(fields, s); err != nil { + return err + } + + steps = append(steps, s) + } + r.Steps = steps + return nil +} + +// DeleteFileStep removes a file from disk. +type DeleteFileStep struct { + Path string + // FailOnMissing fails if file is already missing + FailOnMissing bool `yaml:"fail_on_missing" config:"fail_on_missing"` +} + +// Execute executes delete file step. +func (r *DeleteFileStep) Execute(rootDir string) error { + path, isSubpath := joinPaths(rootDir, r.Path) + if !isSubpath { + return fmt.Errorf("invalid path value for operation 'Delete': %s", path) + } + + err := os.Remove(path) + + if os.IsNotExist(err) && r.FailOnMissing { + // is not found and should be reported + return err + } + + if err != nil && !os.IsNotExist(err) { + // report others + return err + } + + return nil +} + +// DeleteFile creates a DeleteFileStep +func DeleteFile(path string, failOnMissing bool) *DeleteFileStep { + return &DeleteFileStep{ + Path: path, + FailOnMissing: failOnMissing, + } +} + +// MoveFileStep moves a file to a new location. +type MoveFileStep struct { + Path string + Target string + // FailOnMissing fails if file is already missing + FailOnMissing bool `yaml:"fail_on_missing" config:"fail_on_missing"` +} + +// Execute executes move file step. +func (r *MoveFileStep) Execute(rootDir string) error { + path, isSubpath := joinPaths(rootDir, r.Path) + if !isSubpath { + return fmt.Errorf("invalid path value for operation 'Move': %s", path) + } + + target, isSubpath := joinPaths(rootDir, r.Target) + if !isSubpath { + return fmt.Errorf("invalid target value for operation 'Move': %s", target) + } + + err := os.Rename(path, target) + + if os.IsNotExist(err) && r.FailOnMissing { + // is not found and should be reported + return err + } + + if err != nil && !os.IsNotExist(err) { + // report others + return err + } + + return nil +} + +// MoveFile creates a MoveFileStep +func MoveFile(path, target string, failOnMissing bool) *MoveFileStep { + return &MoveFileStep{ + Path: path, + Target: target, + FailOnMissing: failOnMissing, + } +} + +// joinPaths joins paths and returns true if path is subpath of rootDir +func joinPaths(rootDir, path string) (string, bool) { + if !filepath.IsAbs(path) { + path = filepath.Join(rootDir, path) + } + + absRoot := filepath.Clean(filepath.FromSlash(rootDir)) + absPath := filepath.Clean(filepath.FromSlash(path)) + + // path on windows are case insensitive + if !isFsCaseSensitive(rootDir) { + absRoot = strings.ToLower(absRoot) + absPath = strings.ToLower(absPath) + } + + return absPath, strings.HasPrefix(absPath, absRoot) +} + +func isFsCaseSensitive(rootDir string) bool { + defaultCaseSens := runtime.GOOS != "windows" && runtime.GOOS != "darwin" + + dir := filepath.Dir(rootDir) + base := filepath.Base(rootDir) + // if rootdir not exist create it + if _, err := os.Stat(rootDir); os.IsNotExist(err) { + os.MkdirAll(rootDir, 0775) + defer os.RemoveAll(rootDir) + } + + lowDir := filepath.Join(base, strings.ToLower(dir)) + upDir := filepath.Join(base, strings.ToUpper(dir)) + + if _, err := os.Stat(rootDir); err != nil { + return defaultCaseSens + } + + // check lower/upper dir + if _, lowErr := os.Stat(lowDir); os.IsNotExist(lowErr) { + return true + } + if _, upErr := os.Stat(upDir); os.IsNotExist(upErr) { + return true + } + + return defaultCaseSens +} diff --git a/x-pack/agent/pkg/agent/transpiler/steps_test.go b/x-pack/agent/pkg/agent/transpiler/steps_test.go new file mode 100644 index 00000000000..eedff0a703c --- /dev/null +++ b/x-pack/agent/pkg/agent/transpiler/steps_test.go @@ -0,0 +1,66 @@ +// 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 transpiler + +import ( + "fmt" + "runtime" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIsSubpath(t *testing.T) { + testCases := map[string][]struct { + root string + path string + resultPath string + isSubpath bool + }{ + "linux": { + {"/", "a", "/a", true}, + {"/a", "b", "/a/b", true}, + {"/a", "b/c", "/a/b/c", true}, + {"/a/b", "/a/c", "/a/c", false}, + {"/a/b", "/a/b/../c", "/a/c", false}, + {"/a/b", "../c", "/a/c", false}, + {"/a", "/a/b/c", "/a/b/c", true}, + {"/a", "/A/b/c", "/A/b/c", false}, + }, + "darwin": { + {"/", "a", "/a", true}, + {"/a", "b", "/a/b", true}, + {"/a", "b/c", "/a/b/c", true}, + {"/a/b", "/a/c", "/a/c", false}, + {"/a/b", "/a/b/../c", "/a/c", false}, + {"/a/b", "../c", "/a/c", false}, + {"/a", "/a/b/c", "/a/b/c", true}, + {"/a", "/A/b/c", "/a/b/c", true}, + }, + "windows": { + {"/", "a", "\\a", true}, + {"/a", "b", "\\a\\b", true}, + {"/a", "b/c", "\\a\\b\\c", true}, + {"/a/b", "/a/c", "\\a\\c", false}, + {"/a/b", "/a/b/../c", "\\a\\c", false}, + {"/a/b", "../c", "\\a\\c", false}, + {"/a", "/a/b/c", "\\a\\b\\c", true}, + {"/a", "/A/b/c", "\\a\\b\\c", true}, + }, + } + + osSpecificTests, found := testCases[runtime.GOOS] + if !found { + return + } + + for _, test := range osSpecificTests { + t.Run(fmt.Sprintf("[%s]'%s-%s'", runtime.GOOS, test.root, test.path), func(t *testing.T) { + newPath, result := joinPaths(test.root, test.path) + assert.Equal(t, test.resultPath, newPath) + assert.Equal(t, test.isSubpath, result) + }) + } +} diff --git a/x-pack/agent/pkg/artifact/install/hooks/hooks_installer.go b/x-pack/agent/pkg/artifact/install/hooks/hooks_installer.go new file mode 100644 index 00000000000..8dd4c8c057f --- /dev/null +++ b/x-pack/agent/pkg/artifact/install/hooks/hooks_installer.go @@ -0,0 +1,57 @@ +// 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 hooks + +import ( + "strings" + + "github.com/elastic/beats/v7/x-pack/agent/pkg/agent/program" +) + +type embeddedInstaller interface { + Install(programName, version, installDir string) error +} + +// Installer or zip packages +type Installer struct { + installer embeddedInstaller +} + +// NewInstaller creates an installer able to install zip packages +func NewInstaller(i embeddedInstaller) (*Installer, error) { + return &Installer{ + installer: i, + }, nil +} + +// Install performs installation of program in a specific version. +// It expects package to be already downloaded. +func (i *Installer) Install(programName, version, installDir string) error { + if err := i.installer.Install(programName, version, installDir); err != nil { + return err + } + + // post install hooks + nameLower := strings.ToLower(programName) + _, isSupported := program.SupportedMap[nameLower] + if !isSupported { + return nil + } + + for _, spec := range program.Supported { + if strings.ToLower(spec.Name) != nameLower { + continue + } + + if spec.PostInstallSteps != nil { + return spec.PostInstallSteps.Execute(installDir) + } + + // only one spec for type + break + } + + return nil +} diff --git a/x-pack/agent/pkg/artifact/install/installer.go b/x-pack/agent/pkg/artifact/install/installer.go index b8860f6b003..0ba1a07aca6 100644 --- a/x-pack/agent/pkg/artifact/install/installer.go +++ b/x-pack/agent/pkg/artifact/install/installer.go @@ -9,6 +9,7 @@ import ( "runtime" "github.com/elastic/beats/v7/x-pack/agent/pkg/artifact" + "github.com/elastic/beats/v7/x-pack/agent/pkg/artifact/install/hooks" "github.com/elastic/beats/v7/x-pack/agent/pkg/artifact/install/tar" "github.com/elastic/beats/v7/x-pack/agent/pkg/artifact/install/zip" ) @@ -36,9 +37,17 @@ func NewInstaller(config *artifact.Config) (Installer, error) { return nil, ErrConfigNotProvided } + var installer Installer + var err error if runtime.GOOS == "windows" { - return zip.NewInstaller(config) + installer, err = zip.NewInstaller(config) + } else { + installer, err = tar.NewInstaller(config) } - return tar.NewInstaller(config) + if err != nil { + return nil, err + } + + return hooks.NewInstaller(installer) } diff --git a/x-pack/agent/spec/metricbeat.yml b/x-pack/agent/spec/metricbeat.yml index 178a797430f..390749a3327 100644 --- a/x-pack/agent/spec/metricbeat.yml +++ b/x-pack/agent/spec/metricbeat.yml @@ -2,6 +2,10 @@ name: Metricbeat cmd: metricbeat args: ["-E", "setup.ilm.enabled=false", "-E", "setup.template.enabled=false", "-E", "management.mode=x-pack-fleet", "-E", "management.enabled=true"] configurable: grpc +post_install: + - move_file: + path: "modules.d/system.yml" + target: "modules.d/system.yml.disabled" rules: - inject_index: type: metrics