Skip to content
Open
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
42 changes: 42 additions & 0 deletions executor/linux/step.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"bufio"
"bytes"
"context"
"errors"
"fmt"
"io"
"strings"
Expand Down Expand Up @@ -224,19 +225,60 @@ func (c *client) ExecStep(ctx context.Context, ctn *pipeline.Container) error {
// wait for the runtime container
err = c.Runtime.WaitContainer(ctx, ctn)
if err != nil {
recordStepRuntimeError(ctn, _step, err)
return err
}

logger.Debug("inspecting container")
// inspect the runtime container
err = c.Runtime.InspectContainer(ctx, ctn)
if err != nil {
recordStepRuntimeError(ctn, _step, err)
return err
}

return nil
}

// recordStepRuntimeError updates the in-memory step so Snapshot/Upload won't report success.
func recordStepRuntimeError(ctn *pipeline.Container, s *api.Step, err error) {
if s == nil || err == nil {
return
}

s.SetError(err.Error())

exitCode := int32(0)
if ctn != nil {
exitCode = ctn.ExitCode
}

switch {
case errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded):
s.SetStatus(constants.StatusFailure)

if exitCode == 0 {
exitCode = 137
}
default:
s.SetStatus(constants.StatusError)

if exitCode == 0 {
exitCode = 1
}
}

if exitCode != 0 {
if ctn != nil {
ctn.ExitCode = exitCode
}

if s.GetExitCode() == 0 {
s.SetExitCode(exitCode)
}
}
}

// StreamStep tails the output for a step.
func (c *client) StreamStep(ctx context.Context, ctn *pipeline.Container) error {
if ctn.Name == constants.InitName {
Expand Down
136 changes: 136 additions & 0 deletions executor/linux/step_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,142 @@ func TestLinux_ExecStep(t *testing.T) {
}
}

func TestLinux_ExecStep_WaitTimeout(t *testing.T) {
_build := testBuild()

gin.SetMode(gin.TestMode)

s := httptest.NewServer(server.FakeHandler())
defer s.Close()

_client, err := vela.NewClient(s.URL, "", nil)
if err != nil {
t.Fatalf("unable to create Vela API client: %v", err)
}

_docker, err := docker.NewMock()
if err != nil {
t.Fatalf("unable to create docker runtime engine: %v", err)
}

streamRequests, done := message.MockStreamRequestsWithCancel(context.Background())
defer done()

_engine, err := New(
WithBuild(_build),
WithPipeline(new(pipeline.Build)),
WithRuntime(_docker),
WithVelaClient(_client),
withStreamRequests(streamRequests),
)
if err != nil {
t.Fatalf("unable to create executor engine: %v", err)
}

container := &pipeline.Container{
ID: "step_github_octocat_1_wait-timeout",
Directory: "/vela/src/github.com/github/octocat",
Environment: map[string]string{"FOO": "bar"},
Image: "alpine:latest",
Name: "echo",
Number: 1,
Pull: "not_present",
}

stepEntry := api.StepFromBuildContainer(_build, container)
_engine.steps.Store(container.ID, stepEntry)
_engine.stepLogs.Store(container.ID, new(api.Log))

err = _engine.ExecStep(context.Background(), container)
if err == nil {
t.Fatalf("ExecStep should have returned err")
}

if got := stepEntry.GetStatus(); got != constants.StatusFailure {
t.Errorf("step status = %s, want %s", got, constants.StatusFailure)
}

if got := stepEntry.GetExitCode(); got != 137 {
t.Errorf("step exit code = %d, want %d", got, 137)
}

if container.ExitCode != 137 {
t.Errorf("container exit code = %d, want %d", container.ExitCode, 137)
}

if stepEntry.GetError() == "" {
t.Error("expected step error to be recorded")
}
}

func TestLinux_ExecStep_WaitError(t *testing.T) {
_build := testBuild()

gin.SetMode(gin.TestMode)

s := httptest.NewServer(server.FakeHandler())
defer s.Close()

_client, err := vela.NewClient(s.URL, "", nil)
if err != nil {
t.Fatalf("unable to create Vela API client: %v", err)
}

_docker, err := docker.NewMock()
if err != nil {
t.Fatalf("unable to create docker runtime engine: %v", err)
}

streamRequests, done := message.MockStreamRequestsWithCancel(context.Background())
defer done()

_engine, err := New(
WithBuild(_build),
WithPipeline(new(pipeline.Build)),
WithRuntime(_docker),
WithVelaClient(_client),
withStreamRequests(streamRequests),
)
if err != nil {
t.Fatalf("unable to create executor engine: %v", err)
}

container := &pipeline.Container{
ID: "step_github_octocat_1_wait-error",
Directory: "/vela/src/github.com/github/octocat",
Environment: map[string]string{"FOO": "bar"},
Image: "alpine:latest",
Name: "echo",
Number: 1,
Pull: "not_present",
}

stepEntry := api.StepFromBuildContainer(_build, container)
_engine.steps.Store(container.ID, stepEntry)
_engine.stepLogs.Store(container.ID, new(api.Log))

err = _engine.ExecStep(context.Background(), container)
if err == nil {
t.Fatalf("ExecStep should have returned err")
}

if got := stepEntry.GetStatus(); got != constants.StatusError {
t.Errorf("step status = %s, want %s", got, constants.StatusError)
}

if got := stepEntry.GetExitCode(); got != 1 {
t.Errorf("step exit code = %d, want %d", got, 1)
}

if container.ExitCode != 1 {
t.Errorf("container exit code = %d, want %d", container.ExitCode, 1)
}

if stepEntry.GetError() == "" {
t.Error("expected step error to be recorded")
}
}

func TestLinux_StreamStep(t *testing.T) {
// setup types
_build := testBuild()
Expand Down
42 changes: 42 additions & 0 deletions executor/local/step.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package local
import (
"bufio"
"context"
"errors"
"fmt"
"time"

Expand Down Expand Up @@ -109,18 +110,59 @@ func (c *client) ExecStep(ctx context.Context, ctn *pipeline.Container) error {
// wait for the runtime container
err = c.Runtime.WaitContainer(ctx, ctn)
if err != nil {
recordStepRuntimeError(ctn, _step, err)
return err
}

// inspect the runtime container
err = c.Runtime.InspectContainer(ctx, ctn)
if err != nil {
recordStepRuntimeError(ctn, _step, err)
return err
}

return nil
}

// recordStepRuntimeError updates the in-memory step so Snapshot/Upload won't report success.
func recordStepRuntimeError(ctn *pipeline.Container, s *api.Step, err error) {
if s == nil || err == nil {
return
}

s.SetError(err.Error())

exitCode := int32(0)
if ctn != nil {
exitCode = ctn.ExitCode
}

switch {
case errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded):
s.SetStatus(constants.StatusFailure)

if exitCode == 0 {
exitCode = 137
}
default:
s.SetStatus(constants.StatusError)

if exitCode == 0 {
exitCode = 1
}
}

if exitCode != 0 {
if ctn != nil {
ctn.ExitCode = exitCode
}

if s.GetExitCode() == 0 {
s.SetExitCode(exitCode)
}
}
}

// StreamStep tails the output for a step.
func (c *client) StreamStep(ctx context.Context, ctn *pipeline.Container) error {
if ctn.Name == constants.InitName {
Expand Down
104 changes: 104 additions & 0 deletions executor/local/step_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,110 @@ func TestLocal_ExecStep(t *testing.T) {
}
}

func TestLocal_ExecStep_WaitTimeout(t *testing.T) {
_build := testBuild()

_runtime, err := docker.NewMock()
if err != nil {
t.Fatalf("unable to create runtime engine: %v", err)
}

streamRequests, done := message.MockStreamRequestsWithCancel(context.Background())
defer done()

_engine, err := New(
WithBuild(_build),
WithPipeline(new(pipeline.Build)),
WithRuntime(_runtime),
withStreamRequests(streamRequests),
)
if err != nil {
t.Fatalf("unable to create executor engine: %v", err)
}

container := &pipeline.Container{
ID: "step_github_octocat_1_wait-timeout",
Directory: "/vela/src/github.com/github/octocat",
Environment: map[string]string{"FOO": "bar"},
Image: "alpine:latest",
Name: "echo",
Number: 1,
Pull: "not_present",
}

stepEntry := api.StepFromBuildContainer(_build, container)
_engine.steps.Store(container.ID, stepEntry)

err = _engine.ExecStep(context.Background(), container)
if err == nil {
t.Fatalf("ExecStep should have returned err")
}

if got := stepEntry.GetStatus(); got != constants.StatusFailure {
t.Errorf("step status = %s, want %s", got, constants.StatusFailure)
}

if container.ExitCode != 137 {
t.Errorf("container exit code = %d, want %d", container.ExitCode, 137)
}

if stepEntry.GetError() == "" {
t.Error("expected step error to be recorded")
}
}

func TestLocal_ExecStep_WaitError(t *testing.T) {
_build := testBuild()

_runtime, err := docker.NewMock()
if err != nil {
t.Fatalf("unable to create runtime engine: %v", err)
}

streamRequests, done := message.MockStreamRequestsWithCancel(context.Background())
defer done()

_engine, err := New(
WithBuild(_build),
WithPipeline(new(pipeline.Build)),
WithRuntime(_runtime),
withStreamRequests(streamRequests),
)
if err != nil {
t.Fatalf("unable to create executor engine: %v", err)
}

container := &pipeline.Container{
ID: "step_github_octocat_1_wait-error",
Directory: "/vela/src/github.com/github/octocat",
Environment: map[string]string{"FOO": "bar"},
Image: "alpine:latest",
Name: "echo",
Number: 1,
Pull: "not_present",
}

stepEntry := api.StepFromBuildContainer(_build, container)
_engine.steps.Store(container.ID, stepEntry)

err = _engine.ExecStep(context.Background(), container)
if err == nil {
t.Fatalf("ExecStep should have returned err")
}

if got := stepEntry.GetStatus(); got != constants.StatusError {
t.Errorf("step status = %s, want %s", got, constants.StatusError)
}

if container.ExitCode != 1 {
t.Errorf("container exit code = %d, want %d", container.ExitCode, 1)
}

if stepEntry.GetError() == "" {
t.Error("expected step error to be recorded")
}
}

func TestLocal_StreamStep(t *testing.T) {
// setup types
_build := testBuild()
Expand Down
Loading
Loading