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

feat: output to environment variable #107

Merged
merged 1 commit into from
May 18, 2022
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
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
- [Conditional Logic](#conditional-logic)
- [State Handlers](#state-handlers)
- [Redirection](#redirection)
- [Output](#output)
- [Repeating Task](#repeating-task)
- [All Available Fields](#all-available-fields)
- [Admin Configuration](#admin-configuration)
Expand Down Expand Up @@ -201,7 +202,7 @@ steps:

### Redirection

Sometimes you want to redirect standard out to a file to use in subsequent tasks. You can use `stdout` field to do so.
`stdout` field can be used to write standard output to a file.

```yaml
name: example
Expand All @@ -211,6 +212,18 @@ steps:
stdout: "/tmp/hello" # the content will be "hello\n"
```

### Output

`output` field can be used to write standard output to a environment variable. Leading and trailing space will be trimmed automatically.

```yaml
name: example
steps:
- name: step 1
command: "echo foo"
output: FOO # will contain "foo"
```

### State Handlers

It is often desirable to take action when a specific event happens, for example, when a workflow fails. To achieve this, you can use `handlerOn` fields.
Expand Down
2 changes: 1 addition & 1 deletion cmd/retry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func Test_retryCommand(t *testing.T) {
require.NoError(t, err)

for _, n := range status.Status.Nodes {
n.Command = "true"
n.CmdWithArgs = "echo parameter is $1"
}
err = dw.Write(status.Status)
require.NoError(t, err)
Expand Down
2 changes: 1 addition & 1 deletion internal/agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func TestRetry(t *testing.T) {
assert.Equal(t, scheduler.SchedulerStatus_Error, status.Status)

for _, n := range status.Nodes {
n.Command = "true"
n.CmdWithArgs = "true"
}
a := &Agent{
Config: &Config{
Expand Down
4 changes: 3 additions & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,10 @@ func buildStep(variables []string, def *stepDef) (*Step, error) {
step := &Step{}
step.Name = def.Name
step.Description = def.Description
step.Command, step.Args = utils.SplitCommand(def.Command)
step.CmdWithArgs = def.Command
step.Command, step.Args = utils.SplitCommand(step.CmdWithArgs) // Will be eva
step.Stdout = os.ExpandEnv(def.Stdout)
step.Output = def.Output
step.Dir = os.ExpandEnv(def.Dir)
step.Variables = variables
step.Depends = def.Depends
Expand Down
1 change: 1 addition & 0 deletions internal/config/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type stepDef struct {
Dir string
Command string
Stdout string
Output string
Depends []string
ContinueOn *continueOnDef
RetryPolicy *retryPolicyDef
Expand Down
16 changes: 10 additions & 6 deletions internal/config/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ func TestLoadConfig(t *testing.T) {

steps := []*Step{
{
Name: "1",
Dir: testHomeDir,
Command: "true",
Args: []string{},
Variables: testEnv,
Name: "1",
Dir: testHomeDir,
CmdWithArgs: "true",
Command: "true",
Args: []string{},
Variables: testEnv,
Preconditions: []*Condition{
{
Condition: "`echo test`",
Expand All @@ -50,6 +51,7 @@ func TestLoadConfig(t *testing.T) {
{
Name: "2",
Dir: testDir,
CmdWithArgs: "false",
Command: "false",
Args: []string{},
Variables: testEnv,
Expand All @@ -65,10 +67,12 @@ func TestLoadConfig(t *testing.T) {
}

makeTestStepFunc := func(name string) *Step {
c := fmt.Sprintf("%s.sh", name)
return &Step{
Name: name,
Dir: testDir,
Command: fmt.Sprintf("%s.sh", name),
CmdWithArgs: c,
Command: c,
Args: []string{},
Variables: testEnv,
Preconditions: []*Condition{},
Expand Down
2 changes: 2 additions & 0 deletions internal/config/step.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ type Step struct {
Description string
Variables []string
Dir string
CmdWithArgs string
Command string
Stdout string
Output string
Args []string
Depends []string
ContinueOn ContinueOn
Expand Down
24 changes: 24 additions & 0 deletions internal/scheduler/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package scheduler

import (
"bufio"
"bytes"
"context"
"fmt"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
"syscall"
"time"
Expand Down Expand Up @@ -58,6 +60,8 @@ type Node struct {
logWriter *bufio.Writer
stdoutFile *os.File
stdoutWriter *bufio.Writer
outputWriter *os.File
outputReader *os.File
}

type NodeState struct {
Expand All @@ -73,6 +77,9 @@ type NodeState struct {
func (n *Node) Execute() error {
ctx, fn := context.WithCancel(context.Background())
n.cancelFunc = fn
if n.CmdWithArgs != "" {
n.Command, n.Args = utils.SplitCommand(os.ExpandEnv(n.CmdWithArgs))
}
n.cmd = exec.CommandContext(ctx, n.Command, n.Args...)
cmd := n.cmd
cmd.Dir = n.Dir
Expand All @@ -94,7 +101,24 @@ func (n *Node) Execute() error {
cmd.Stdout = io.MultiWriter(n.logWriter, n.stdoutWriter)
}

if n.Output != "" {
var err error
if n.outputReader, n.outputWriter, err = os.Pipe(); err != nil {
return err
}
cmd.Stdout = io.MultiWriter(cmd.Stdout, n.outputWriter)
}

n.Error = cmd.Run()

if n.outputReader != nil && n.Output != "" {
utils.LogIgnoreErr("close pipe writer", n.outputWriter.Close())
var buf bytes.Buffer
_, _ = io.Copy(&buf, n.outputReader)
ret := buf.String()
os.Setenv(n.Output, strings.TrimSpace(ret))
}

return n.Error
}

Expand Down
29 changes: 28 additions & 1 deletion internal/scheduler/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func TestSignal(t *testing.T) {
require.Equal(t, n.Status, NodeStatus_Cancel)
}

func TestOutputLogAndStdout(t *testing.T) {
func TestLogAndStdout(t *testing.T) {
n := &Node{
Step: &config.Step{
Command: "echo",
Expand All @@ -77,3 +77,30 @@ func TestOutputLogAndStdout(t *testing.T) {
dat, _ = os.ReadFile(n.logFile.Name())
require.Equal(t, "done\n", string(dat))
}

func TestOutput(t *testing.T) {
n := &Node{
Step: &config.Step{
Command: "echo",
Args: []string{"hello"},
Dir: os.Getenv("HOME"),
Output: "OUTPUT_TEST",
},
}
err := n.setup(os.Getenv("HOME"), "test-request-id-output")
require.NoError(t, err)
defer func() {
_ = n.teardown()
}()

err = n.Execute()
require.NoError(t, err)
err = n.teardown()
require.NoError(t, err)

dat, _ := os.ReadFile(n.logFile.Name())
require.Equal(t, "hello\n", string(dat))

val := os.Getenv("OUTPUT_TEST")
require.Equal(t, "hello", val)
}
2 changes: 1 addition & 1 deletion internal/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func FormatDuration(t time.Duration, defaultVal string) string {
}

func SplitCommand(cmd string) (program string, args []string) {
vals := strings.SplitN(os.ExpandEnv(cmd), " ", 2)
vals := strings.SplitN(cmd, " ", 2)
if len(vals) > 1 {
return vals[0], strings.Split(vals[1], " ")
}
Expand Down
2 changes: 1 addition & 1 deletion tests/testdata/cmd_retry.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "agent retry"
name: "retry"
params: "param-value"
steps:
- name: "1"
Expand Down