-
Notifications
You must be signed in to change notification settings - Fork 148
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
V2 command work dir #1061
V2 command work dir #1061
Changes from 5 commits
852f5fc
cd849a8
57406ea
64500cc
023f220
919cd40
62f0b51
0f68dae
0207c94
26da0a0
9626365
bbec73b
55ea636
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,8 +6,11 @@ package component | |
|
||
import ( | ||
"fmt" | ||
"os" | ||
"strings" | ||
|
||
"github.com/elastic/elastic-agent/pkg/utils" | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are these newlines coming from autoimport? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes its nice, but also a pain. Fixed. |
||
"github.com/elastic/elastic-agent-client/v7/pkg/client" | ||
"github.com/elastic/elastic-agent-client/v7/pkg/proto" | ||
|
||
|
@@ -87,6 +90,10 @@ func (r *RuntimeSpecs) ToComponents(policy map[string]interface{}) ([]Component, | |
} | ||
|
||
// set the runtime variables that are available in the input specification runtime checks | ||
hasRoot, err := utils.HasRoot() | ||
if err != nil { | ||
return nil, err | ||
} | ||
vars, err := transpiler.NewVars(map[string]interface{}{ | ||
"runtime": map[string]interface{}{ | ||
"platform": r.platform.String(), | ||
|
@@ -96,6 +103,11 @@ func (r *RuntimeSpecs) ToComponents(policy map[string]interface{}) ([]Component, | |
"major": r.platform.Major, | ||
"minor": r.platform.Minor, | ||
}, | ||
"user": map[string]interface{}{ | ||
cmacknz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"uid": os.Geteuid(), | ||
"gid": os.Getgid(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dont we want There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes we do. Changed it. |
||
"root": hasRoot, | ||
}, | ||
}, nil) | ||
if err != nil { | ||
return nil, err | ||
|
@@ -260,12 +272,16 @@ func toIntermediate(policy map[string]interface{}) (map[string]outputI, error) { | |
} | ||
idRaw, ok := input[idKey] | ||
if !ok { | ||
return nil, fmt.Errorf("invalid 'inputs.%d', 'id' missing", idx) | ||
// no ID; fallback to type | ||
idRaw = t | ||
} | ||
id, ok := idRaw.(string) | ||
if !ok { | ||
return nil, fmt.Errorf("invalid 'inputs.%d.id', expected a string not a %T", idx, idRaw) | ||
} | ||
if hasDuplicate(outputsMap, id) { | ||
return nil, fmt.Errorf("invalid 'inputs.%d.id', has a duplicate id (id is required to be unique)", idx) | ||
blakerouse marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
outputName := "default" | ||
if outputRaw, ok := input[useKey]; ok { | ||
outputNameVal, ok := outputRaw.(string) | ||
|
@@ -346,6 +362,19 @@ func validateRuntimeChecks(spec *InputSpec, store eql.VarStore) error { | |
return nil | ||
} | ||
|
||
func hasDuplicate(outputsMap map[string]outputI, id string) bool { | ||
for _, o := range outputsMap { | ||
for _, i := range o.inputs { | ||
for _, j := range i { | ||
if j.id == id { | ||
return true | ||
} | ||
} | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func getLogLevel(val map[string]interface{}) (client.UnitLogLevel, error) { | ||
const logLevelKey = "log_level" | ||
|
||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -10,8 +10,12 @@ import ( | |||||||||||||||||||||
"fmt" | ||||||||||||||||||||||
"os" | ||||||||||||||||||||||
"os/exec" | ||||||||||||||||||||||
"path/filepath" | ||||||||||||||||||||||
"time" | ||||||||||||||||||||||
|
||||||||||||||||||||||
"github.com/elastic/elastic-agent/internal/pkg/agent/application/paths" | ||||||||||||||||||||||
"github.com/elastic/elastic-agent/pkg/utils" | ||||||||||||||||||||||
|
||||||||||||||||||||||
"github.com/elastic/elastic-agent-client/v7/pkg/client" | ||||||||||||||||||||||
"github.com/elastic/elastic-agent/pkg/component" | ||||||||||||||||||||||
"github.com/elastic/elastic-agent/pkg/core/process" | ||||||||||||||||||||||
|
@@ -22,6 +26,11 @@ type actionMode int | |||||||||||||||||||||
const ( | ||||||||||||||||||||||
actionStart = actionMode(0) | ||||||||||||||||||||||
actionStop = actionMode(1) | ||||||||||||||||||||||
|
||||||||||||||||||||||
runDirMod = 0770 | ||||||||||||||||||||||
|
||||||||||||||||||||||
envAgentComponentID = "AGENT_COMPONENT_ID" | ||||||||||||||||||||||
envAgentComponentInputType = "AGENT_COMPONENT_INPUT_TYPE" | ||||||||||||||||||||||
) | ||||||||||||||||||||||
|
||||||||||||||||||||||
type procState struct { | ||||||||||||||||||||||
|
@@ -69,6 +78,7 @@ func NewCommandRuntime(comp component.Component) (ComponentRuntime, error) { | |||||||||||||||||||||
// ever be called again. | ||||||||||||||||||||||
func (c *CommandRuntime) Run(ctx context.Context, comm Communicator) error { | ||||||||||||||||||||||
checkinPeriod := c.current.Spec.Spec.Command.Timeouts.Checkin | ||||||||||||||||||||||
restartPeriod := c.current.Spec.Spec.Command.Timeouts.Restart | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we run into nil pointer somewhere here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is checked in the initialization of the struct. The depth after that does not have any pointers.
|
||||||||||||||||||||||
c.forceCompState(client.UnitStateStarting, "Starting") | ||||||||||||||||||||||
t := time.NewTicker(checkinPeriod) | ||||||||||||||||||||||
defer t.Stop() | ||||||||||||||||||||||
|
@@ -81,25 +91,22 @@ func (c *CommandRuntime) Run(ctx context.Context, comm Communicator) error { | |||||||||||||||||||||
switch as { | ||||||||||||||||||||||
case actionStart: | ||||||||||||||||||||||
if err := c.start(comm); err != nil { | ||||||||||||||||||||||
c.forceCompState(client.UnitStateFailed, err.Error()) | ||||||||||||||||||||||
c.forceCompState(client.UnitStateFailed, fmt.Sprintf("Failed: %s", err)) | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we add even more context? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. besides, it's already an error, just Failed is redundant. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All the context should already be coming from the |
||||||||||||||||||||||
} | ||||||||||||||||||||||
t.Reset(checkinPeriod) | ||||||||||||||||||||||
case actionStop: | ||||||||||||||||||||||
if err := c.stop(ctx); err != nil { | ||||||||||||||||||||||
c.forceCompState(client.UnitStateFailed, err.Error()) | ||||||||||||||||||||||
c.forceCompState(client.UnitStateFailed, fmt.Sprintf("Failed: %s", err)) | ||||||||||||||||||||||
blakerouse marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
case ps := <-c.procCh: | ||||||||||||||||||||||
// ignores old processes | ||||||||||||||||||||||
if ps.proc == c.proc { | ||||||||||||||||||||||
c.proc = nil | ||||||||||||||||||||||
if c.handleProc(ps.state) { | ||||||||||||||||||||||
// start again | ||||||||||||||||||||||
if err := c.start(comm); err != nil { | ||||||||||||||||||||||
c.forceCompState(client.UnitStateFailed, err.Error()) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
// start again after restart period | ||||||||||||||||||||||
t.Reset(restartPeriod) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
t.Reset(checkinPeriod) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
case newComp := <-c.compCh: | ||||||||||||||||||||||
sendExpected := c.state.syncExpected(&newComp) | ||||||||||||||||||||||
|
@@ -140,30 +147,37 @@ func (c *CommandRuntime) Run(ctx context.Context, comm Communicator) error { | |||||||||||||||||||||
c.sendObserved() | ||||||||||||||||||||||
} | ||||||||||||||||||||||
case <-t.C: | ||||||||||||||||||||||
if c.proc != nil && c.actionState == actionStart { | ||||||||||||||||||||||
// running and should be running | ||||||||||||||||||||||
now := time.Now().UTC() | ||||||||||||||||||||||
if c.lastCheckin.IsZero() { | ||||||||||||||||||||||
// never checked-in | ||||||||||||||||||||||
c.missedCheckins++ | ||||||||||||||||||||||
} else if now.Sub(c.lastCheckin) > checkinPeriod { | ||||||||||||||||||||||
// missed check-in during required period | ||||||||||||||||||||||
c.missedCheckins++ | ||||||||||||||||||||||
} else if now.Sub(c.lastCheckin) <= checkinPeriod { | ||||||||||||||||||||||
c.missedCheckins = 0 | ||||||||||||||||||||||
} | ||||||||||||||||||||||
if c.missedCheckins == 0 { | ||||||||||||||||||||||
c.compState(client.UnitStateHealthy) | ||||||||||||||||||||||
} else if c.missedCheckins > 0 && c.missedCheckins < maxCheckinMisses { | ||||||||||||||||||||||
c.compState(client.UnitStateDegraded) | ||||||||||||||||||||||
} else if c.missedCheckins >= maxCheckinMisses { | ||||||||||||||||||||||
// something is wrong; the command should be checking in | ||||||||||||||||||||||
// | ||||||||||||||||||||||
// at this point it is assumed the sub-process has locked up and will not respond to a nice | ||||||||||||||||||||||
// termination signal, so we jump directly to killing the process | ||||||||||||||||||||||
msg := fmt.Sprintf("Failed: pid '%d' missed %d check-ins and will be killed", c.proc.PID, maxCheckinMisses) | ||||||||||||||||||||||
c.forceCompState(client.UnitStateFailed, msg) | ||||||||||||||||||||||
_ = c.proc.Kill() // watcher will handle it from here | ||||||||||||||||||||||
if c.actionState == actionStart { | ||||||||||||||||||||||
if c.proc == nil { | ||||||||||||||||||||||
// not running, but should be running | ||||||||||||||||||||||
if err := c.start(comm); err != nil { | ||||||||||||||||||||||
c.forceCompState(client.UnitStateFailed, fmt.Sprintf("Failed: %s", err)) | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as the others |
||||||||||||||||||||||
} | ||||||||||||||||||||||
} else { | ||||||||||||||||||||||
Comment on lines
+154
to
+159
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] |
||||||||||||||||||||||
// running and should be running | ||||||||||||||||||||||
now := time.Now().UTC() | ||||||||||||||||||||||
if c.lastCheckin.IsZero() { | ||||||||||||||||||||||
// never checked-in | ||||||||||||||||||||||
c.missedCheckins++ | ||||||||||||||||||||||
} else if now.Sub(c.lastCheckin) > checkinPeriod { | ||||||||||||||||||||||
// missed check-in during required period | ||||||||||||||||||||||
c.missedCheckins++ | ||||||||||||||||||||||
Comment on lines
+162
to
+167
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion]
Suggested change
|
||||||||||||||||||||||
} else if now.Sub(c.lastCheckin) <= checkinPeriod { | ||||||||||||||||||||||
c.missedCheckins = 0 | ||||||||||||||||||||||
} | ||||||||||||||||||||||
if c.missedCheckins == 0 { | ||||||||||||||||||||||
c.compState(client.UnitStateHealthy) | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] |
||||||||||||||||||||||
} else if c.missedCheckins > 0 && c.missedCheckins < maxCheckinMisses { | ||||||||||||||||||||||
c.compState(client.UnitStateDegraded) | ||||||||||||||||||||||
} else if c.missedCheckins >= maxCheckinMisses { | ||||||||||||||||||||||
// something is wrong; the command should be checking in | ||||||||||||||||||||||
// | ||||||||||||||||||||||
// at this point it is assumed the sub-process has locked up and will not respond to a nice | ||||||||||||||||||||||
// termination signal, so we jump directly to killing the process | ||||||||||||||||||||||
msg := fmt.Sprintf("Failed: pid '%d' missed %d check-ins and will be killed", c.proc.PID, maxCheckinMisses) | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] |
||||||||||||||||||||||
c.forceCompState(client.UnitStateFailed, msg) | ||||||||||||||||||||||
_ = c.proc.Kill() // watcher will handle it from here | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if there a logger here? if so, I'd log the error |
||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
@@ -243,11 +257,26 @@ func (c *CommandRuntime) start(comm Communicator) error { | |||||||||||||||||||||
return nil | ||||||||||||||||||||||
} | ||||||||||||||||||||||
cmdSpec := c.current.Spec.Spec.Command | ||||||||||||||||||||||
env := make([]string, 0, len(cmdSpec.Env)) | ||||||||||||||||||||||
env := make([]string, 0, len(cmdSpec.Env)+2) | ||||||||||||||||||||||
cmacknz marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||
for _, e := range cmdSpec.Env { | ||||||||||||||||||||||
env = append(env, fmt.Sprintf("%s=%s", e.Name, e.Value)) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
proc, err := process.Start(c.current.Spec.BinaryPath, os.Geteuid(), os.Getgid(), cmdSpec.Args, env, attachOutErr) | ||||||||||||||||||||||
env = append(env, fmt.Sprintf("%s=%s", envAgentComponentID, c.current.ID)) | ||||||||||||||||||||||
env = append(env, fmt.Sprintf("%s=%s", envAgentComponentInputType, c.current.Spec.InputType)) | ||||||||||||||||||||||
uid, gid := os.Geteuid(), os.Getgid() | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. again: getegid vs getgid There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. |
||||||||||||||||||||||
workDir, err := c.workDir(uid, gid) | ||||||||||||||||||||||
cmacknz marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||
return err | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. more context please. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||
} | ||||||||||||||||||||||
path, err := filepath.Abs(c.current.Spec.BinaryPath) | ||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||
return fmt.Errorf("failed to determine absolute path: %w", err) | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 |
||||||||||||||||||||||
} | ||||||||||||||||||||||
err = utils.HasStrictExecPerms(path, uid) | ||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||
return fmt.Errorf("execution of component prevented: %w", err) | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Supperstion] |
||||||||||||||||||||||
} | ||||||||||||||||||||||
proc, err := process.Start(path, uid, gid, cmdSpec.Args, env, attachOutErr, dirPath(workDir)) | ||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||
return err | ||||||||||||||||||||||
blakerouse marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||
} | ||||||||||||||||||||||
|
@@ -261,7 +290,14 @@ func (c *CommandRuntime) start(comm Communicator) error { | |||||||||||||||||||||
|
||||||||||||||||||||||
func (c *CommandRuntime) stop(ctx context.Context) error { | ||||||||||||||||||||||
if c.proc == nil { | ||||||||||||||||||||||
// already stopped | ||||||||||||||||||||||
// already stopped ensure that state of the component is also stopped | ||||||||||||||||||||||
blakerouse marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||
if c.state.State != client.UnitStateStopped { | ||||||||||||||||||||||
if c.state.State == client.UnitStateFailed { | ||||||||||||||||||||||
c.forceCompState(client.UnitStateStopped, "Stopped: never started successfully") | ||||||||||||||||||||||
} else { | ||||||||||||||||||||||
c.forceCompState(client.UnitStateStopped, "Stopped: already stopped") | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
return nil | ||||||||||||||||||||||
} | ||||||||||||||||||||||
cmdSpec := c.current.Spec.Spec.Command | ||||||||||||||||||||||
|
@@ -314,8 +350,32 @@ func (c *CommandRuntime) handleProc(state *os.ProcessState) bool { | |||||||||||||||||||||
return false | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
func (c *CommandRuntime) workDir(uid int, gid int) (string, error) { | ||||||||||||||||||||||
path := filepath.Join(paths.Run(), c.current.ID) | ||||||||||||||||||||||
err := os.MkdirAll(path, runDirMod) | ||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||
return "", fmt.Errorf("failed to create path: %s, %w", path, err) | ||||||||||||||||||||||
blakerouse marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||
} | ||||||||||||||||||||||
err = os.Chown(path, uid, gid) | ||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||
return "", fmt.Errorf("failed to chown %s: %w", path, err) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
err = os.Chmod(path, runDirMod) | ||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||
return "", fmt.Errorf("failed to chmod: %s, %w", path, err) | ||||||||||||||||||||||
blakerouse marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||
} | ||||||||||||||||||||||
return path, nil | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
func attachOutErr(cmd *exec.Cmd) error { | ||||||||||||||||||||||
cmd.Stdout = os.Stdout | ||||||||||||||||||||||
cmd.Stderr = os.Stderr | ||||||||||||||||||||||
return nil | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
func dirPath(path string) process.Option { | ||||||||||||||||||||||
return func(cmd *exec.Cmd) error { | ||||||||||||||||||||||
cmd.Dir = path | ||||||||||||||||||||||
return nil | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍