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

Reduce integration test retries #1546

Merged
merged 6 commits into from
Feb 12, 2024
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
14 changes: 6 additions & 8 deletions integration-tests/suites/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const (

containerStatsName = "container-stats"

defaultWaitTickSeconds = 30 * time.Second
defaultWaitTickSeconds = 5 * time.Second
)

type IntegrationTestSuiteBase struct {
Expand Down Expand Up @@ -259,9 +259,7 @@ func (s *IntegrationTestSuiteBase) launchContainer(name string, args ...string)
cmd := []string{common.RuntimeCommand, "run", "-d", "--name", name}
cmd = append(cmd, args...)

output, err := common.Retry(func() (string, error) {
return s.Executor().Exec(cmd...)
})
output, err := s.Executor().Exec(cmd...)

outLines := strings.Split(output, "\n")
return outLines[len(outLines)-1], err
Expand Down Expand Up @@ -388,20 +386,20 @@ func (s *IntegrationTestSuiteBase) execContainerShellScript(containerName string

func (s *IntegrationTestSuiteBase) cleanupContainers(containers ...string) {
for _, container := range containers {
s.Executor().Exec(common.RuntimeCommand, "kill", container)
s.Executor().Exec(common.RuntimeCommand, "rm", container)
s.Executor().KillContainer(container)
s.Executor().RemoveContainer(container)
}
}

func (s *IntegrationTestSuiteBase) stopContainers(containers ...string) {
for _, container := range containers {
s.Executor().Exec(common.RuntimeCommand, "stop", "-t", config.StopTimeout(), container)
s.Executor().StopContainer(container)
}
}

func (s *IntegrationTestSuiteBase) removeContainers(containers ...string) {
for _, container := range containers {
s.Executor().Exec(common.RuntimeCommand, "rm", container)
s.Executor().RemoveContainer(container)
}
}

Expand Down
6 changes: 3 additions & 3 deletions integration-tests/suites/common/collector_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,8 @@ func (c *CollectorManager) captureLogs(containerName string) (string, error) {
}

func (c *CollectorManager) killContainer(name string) error {
_, err1 := c.executor.Exec(RuntimeCommand, "kill", name)
_, err2 := c.executor.Exec(RuntimeCommand, "rm", "-fv", name)
_, err1 := c.executor.KillContainer(name)
_, err2 := c.executor.RemoveContainer(name)

var result error
if err1 != nil {
Expand All @@ -235,7 +235,7 @@ func (c *CollectorManager) killContainer(name string) error {
}

func (c *CollectorManager) stopContainer(name string) error {
_, err := c.executor.Exec(RuntimeCommand, "stop", name)
_, err := c.executor.StopContainer(name)
return err
}

Expand Down
74 changes: 56 additions & 18 deletions integration-tests/suites/common/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,12 @@ type Executor interface {
IsContainerRunning(image string) (bool, error)
ExitCode(container string) (int, error)
Exec(args ...string) (string, error)
ExecWithErrorCheck(errCheckFn func(string, error) error, args ...string) (string, error)
ExecWithStdin(pipedContent string, args ...string) (string, error)
ExecRetry(args ...string) (string, error)
ExecWithoutRetry(args ...string) (string, error)
KillContainer(name string) (string, error)
RemoveContainer(name string) (string, error)
StopContainer(name string) (string, error)
}

type CommandBuilder interface {
Expand Down Expand Up @@ -95,7 +99,7 @@ func NewExecutor() Executor {
return &e
}

// Execute provided command with retries on error.
// Exec executes the provided command with retries on non-zero error from the command.
func (e *executor) Exec(args ...string) (string, error) {
if args[0] == RuntimeCommand && RuntimeAsRoot {
args = append([]string{"sudo"}, args...)
Expand All @@ -105,25 +109,23 @@ func (e *executor) Exec(args ...string) (string, error) {
})
}

func (e *executor) ExecRetry(args ...string) (res string, err error) {
maxAttempts := 3
attempt := 0

// ExecWithErrorCheck executes the provided command, retrying if an error occurs
// and the command's output does not contain any of the accepted output contents.
func (e *executor) ExecWithErrorCheck(errCheckFn func(string, error) error, args ...string) (string, error) {
if args[0] == RuntimeCommand && RuntimeAsRoot {
args = append([]string{"sudo"}, args...)
}
return RetryWithErrorCheck(errCheckFn, func() (string, error) {
return e.RunCommand(e.builder.ExecCommand(args...))
})
}

for attempt < maxAttempts {
if attempt > 0 {
fmt.Printf("Retrying (%v) (%d of %d) Error: %v\n", args, attempt, maxAttempts, err)
}
attempt++
res, err = e.RunCommand(e.builder.ExecCommand(args...))
if err == nil {
break
}
// ExecWithoutRetry executes provided command once, without retries.
func (e *executor) ExecWithoutRetry(args ...string) (string, error) {
if args[0] == RuntimeCommand && RuntimeAsRoot {
args = append([]string{"sudo"}, args...)
}
return res, err
return e.RunCommand(e.builder.ExecCommand(args...))
}

func (e *executor) RunCommand(cmd *exec.Cmd) (string, error) {
Expand Down Expand Up @@ -188,12 +190,12 @@ func (e *executor) PullImage(image string) error {
if err == nil {
return nil
}
_, err = e.ExecRetry(RuntimeCommand, "pull", image)
_, err = e.Exec(RuntimeCommand, "pull", image)
return err
}

func (e *executor) IsContainerRunning(containerID string) (bool, error) {
result, err := e.Exec(RuntimeCommand, "inspect", containerID, "--format='{{.State.Running}}'")
result, err := e.ExecWithoutRetry(RuntimeCommand, "inspect", containerID, "--format='{{.State.Running}}'")
if err != nil {
return false, err
}
Expand All @@ -208,6 +210,42 @@ func (e *executor) ExitCode(containerID string) (int, error) {
return strconv.Atoi(strings.Trim(result, "\"'"))
}

// checkContainerCommandError returns nil if the output of the container
// command indicates retries are not needed.
func checkContainerCommandError(name string, cmd string, output string, err error) error {
for _, str := range []string{
"no such container",
"cannot " + cmd + " container",
"can only " + cmd + " running containers",
} {
if strings.Contains(strings.ToLower(output), strings.ToLower(str)) {
return nil
}
}
return err
}

func containerErrorCheckFunction(name string, cmd string) func(string, error) error {
return func(stdout string, err error) error {
return checkContainerCommandError(name, cmd, stdout, err)
}
}

// KillContainer runs the kill operation on the provided container name
func (e *executor) KillContainer(name string) (string, error) {
return e.ExecWithErrorCheck(containerErrorCheckFunction(name, "kill"), RuntimeCommand, "kill", name)
}

// RemoveContainer runs the remove operation on the provided container name
func (e *executor) RemoveContainer(name string) (string, error) {
return e.ExecWithErrorCheck(containerErrorCheckFunction(name, "remove"), RuntimeCommand, "rm", name)
}

// StopContainer runs the stop operation on the provided container name
func (e *executor) StopContainer(name string) (string, error) {
return e.ExecWithErrorCheck(containerErrorCheckFunction(name, "stop"), RuntimeCommand, "stop", name)
}

func (e *localCommandBuilder) ExecCommand(execArgs ...string) *exec.Cmd {
return exec.Command(execArgs[0], execArgs[1:]...)
}
Expand Down
8 changes: 7 additions & 1 deletion integration-tests/suites/common/retry.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@ const (
)

type retryable = func() (string, error)
type errorchecker = func(string, error) error

// Simple retry in loop for commands produce only one string output and error
func Retry(f retryable) (output string, err error) {
return RetryWithErrorCheck(func(s string, e error) error { return e }, f)
}

// Simple retry with error checker
func RetryWithErrorCheck(ec errorchecker, f retryable) (output string, err error) {
for i := 0; i < max_retries; i++ {
output, err = f()
if err == nil {
if ec(output, err) == nil {
return output, nil
} else if i != max_retries-1 {
time.Sleep(retry_wait_time)
Expand Down
3 changes: 1 addition & 2 deletions integration-tests/suites/image_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,5 @@ func (s *ImageLabelJSONTestSuite) TestRunImageWithJSONLabel() {
}

func (s *ImageLabelJSONTestSuite) TearDownSuite() {
s.StopCollector()
s.cleanupContainers("json-label")
s.cleanupContainers("jsonlabel")
}
2 changes: 0 additions & 2 deletions integration-tests/suites/process_network.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ func (s *ProcessNetworkTestSuite) SetupSuite() {
}

func (s *ProcessNetworkTestSuite) TearDownSuite() {
s.StopCollector()
s.cleanupContainers("nginx", "nginx-curl")
s.WritePerfResults()
}

Expand Down
Loading