github.com/containerd/nerdctl@v1.7.7/cmd/nerdctl/container_run_restart_linux_test.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"os/exec"
    23  	"strings"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/containerd/nerdctl/pkg/testutil"
    28  	"github.com/containerd/nerdctl/pkg/testutil/nettestutil"
    29  
    30  	"gotest.tools/v3/assert"
    31  	"gotest.tools/v3/poll"
    32  )
    33  
    34  func TestRunRestart(t *testing.T) {
    35  	const (
    36  		hostPort = 8080
    37  	)
    38  	testContainerName := testutil.Identifier(t)
    39  	if testing.Short() {
    40  		t.Skipf("test is long")
    41  	}
    42  	base := testutil.NewBase(t)
    43  	if !base.DaemonIsKillable {
    44  		t.Skip("daemon is not killable (hint: set \"-test.kill-daemon\")")
    45  	}
    46  	t.Log("NOTE: this test may take a while")
    47  
    48  	defer base.Cmd("rm", "-f", testContainerName).Run()
    49  
    50  	base.Cmd("run", "-d",
    51  		"--restart=always",
    52  		"--name", testContainerName,
    53  		"-p", fmt.Sprintf("127.0.0.1:%d:80", hostPort),
    54  		testutil.NginxAlpineImage).AssertOK()
    55  
    56  	check := func(httpGetRetry int) error {
    57  		resp, err := nettestutil.HTTPGet(fmt.Sprintf("http://127.0.0.1:%d", hostPort), httpGetRetry, false)
    58  		if err != nil {
    59  			return err
    60  		}
    61  		respBody, err := io.ReadAll(resp.Body)
    62  		if err != nil {
    63  			return err
    64  		}
    65  		if !strings.Contains(string(respBody), testutil.NginxAlpineIndexHTMLSnippet) {
    66  			return fmt.Errorf("expected contain %q, got %q",
    67  				testutil.NginxAlpineIndexHTMLSnippet, string(respBody))
    68  		}
    69  		return nil
    70  	}
    71  	assert.NilError(t, check(30))
    72  
    73  	base.KillDaemon()
    74  	base.EnsureDaemonActive()
    75  
    76  	const (
    77  		maxRetry = 30
    78  		sleep    = 3 * time.Second
    79  	)
    80  	for i := 0; i < maxRetry; i++ {
    81  		t.Logf("(retry %d) ps -a: %q", i, base.Cmd("ps", "-a").Run().Combined())
    82  		err := check(1)
    83  		if err == nil {
    84  			t.Logf("test is passing, after %d retries", i)
    85  			return
    86  		}
    87  		time.Sleep(sleep)
    88  	}
    89  	base.DumpDaemonLogs(10)
    90  	t.Fatalf("the container does not seem to be restarted")
    91  }
    92  
    93  func TestRunRestartWithOnFailure(t *testing.T) {
    94  	base := testutil.NewBase(t)
    95  	if testutil.GetTarget() == testutil.Nerdctl {
    96  		testutil.RequireContainerdPlugin(base, "io.containerd.internal.v1", "restart", []string{"on-failure"})
    97  	}
    98  	tID := testutil.Identifier(t)
    99  	defer base.Cmd("rm", "-f", tID).Run()
   100  	base.Cmd("run", "-d", "--restart=on-failure:2", "--name", tID, testutil.AlpineImage, "sh", "-c", "exit 1").AssertOK()
   101  
   102  	check := func(log poll.LogT) poll.Result {
   103  		inspect := base.InspectContainer(tID)
   104  		if inspect.State != nil && inspect.State.Status == "exited" {
   105  			return poll.Success()
   106  		}
   107  		return poll.Continue("container is not yet exited")
   108  	}
   109  	poll.WaitOn(t, check, poll.WithDelay(100*time.Microsecond), poll.WithTimeout(60*time.Second))
   110  	inspect := base.InspectContainer(tID)
   111  	assert.Equal(t, inspect.RestartCount, 2)
   112  }
   113  
   114  func TestRunRestartWithUnlessStopped(t *testing.T) {
   115  	base := testutil.NewBase(t)
   116  	if testutil.GetTarget() == testutil.Nerdctl {
   117  		testutil.RequireContainerdPlugin(base, "io.containerd.internal.v1", "restart", []string{"unless-stopped"})
   118  	}
   119  	tID := testutil.Identifier(t)
   120  	defer base.Cmd("rm", "-f", tID).Run()
   121  	base.Cmd("run", "-d", "--restart=unless-stopped", "--name", tID, testutil.AlpineImage, "sh", "-c", "exit 1").AssertOK()
   122  
   123  	check := func(log poll.LogT) poll.Result {
   124  		inspect := base.InspectContainer(tID)
   125  		if inspect.State != nil && inspect.State.Status == "exited" {
   126  			return poll.Success()
   127  		}
   128  		if inspect.RestartCount == 2 {
   129  			base.Cmd("stop", tID).AssertOK()
   130  		}
   131  		return poll.Continue("container is not yet exited")
   132  	}
   133  	poll.WaitOn(t, check, poll.WithDelay(100*time.Microsecond), poll.WithTimeout(60*time.Second))
   134  	inspect := base.InspectContainer(tID)
   135  	assert.Equal(t, inspect.RestartCount, 2)
   136  }
   137  
   138  func TestUpdateRestartPolicy(t *testing.T) {
   139  	base := testutil.NewBase(t)
   140  	if testutil.GetTarget() == testutil.Nerdctl {
   141  		testutil.RequireContainerdPlugin(base, "io.containerd.internal.v1", "restart", []string{"on-failure"})
   142  	}
   143  	tID := testutil.Identifier(t)
   144  	defer base.Cmd("rm", "-f", tID).Run()
   145  	base.Cmd("run", "-d", "--restart=on-failure:1", "--name", tID, testutil.AlpineImage, "sh", "-c", "exit 1").AssertOK()
   146  	base.Cmd("update", "--restart=on-failure:2", tID).AssertOK()
   147  	check := func(log poll.LogT) poll.Result {
   148  		inspect := base.InspectContainer(tID)
   149  		if inspect.State != nil && inspect.State.Status == "exited" {
   150  			return poll.Success()
   151  		}
   152  		return poll.Continue("container is not yet exited")
   153  	}
   154  	poll.WaitOn(t, check, poll.WithDelay(100*time.Microsecond), poll.WithTimeout(60*time.Second))
   155  	inspect := base.InspectContainer(tID)
   156  	assert.Equal(t, inspect.RestartCount, 2)
   157  }
   158  
   159  // The test is to add a restart policy to a container which has not restart policy before,
   160  // and check it can work correctly.
   161  func TestAddRestartPolicy(t *testing.T) {
   162  	base := testutil.NewBase(t)
   163  	if testutil.GetTarget() == testutil.Nerdctl {
   164  		testutil.RequireContainerdPlugin(base, "io.containerd.internal.v1", "restart", []string{"on-failure"})
   165  	}
   166  	tID := testutil.Identifier(t)
   167  	defer base.Cmd("rm", "-f", tID).Run()
   168  	base.Cmd("run", "-d", "--name", tID, testutil.NginxAlpineImage).AssertOK()
   169  	base.Cmd("update", "--restart=on-failure", tID).AssertOK()
   170  	inspect := base.InspectContainer(tID)
   171  	orgialPid := inspect.State.Pid
   172  	exec.Command("kill", "-9", fmt.Sprintf("%v", orgialPid)).Run()
   173  
   174  	check := func(log poll.LogT) poll.Result {
   175  		inspect := base.InspectContainer(tID)
   176  		if inspect.State != nil && inspect.State.Status == "running" && inspect.State.Pid != orgialPid {
   177  			return poll.Success()
   178  		}
   179  		return poll.Continue("container is not yet running")
   180  	}
   181  	poll.WaitOn(t, check, poll.WithDelay(100*time.Microsecond), poll.WithTimeout(60*time.Second))
   182  	inspect = base.InspectContainer(tID)
   183  	assert.Equal(t, inspect.RestartCount, 1)
   184  }