github.com/aminovpavel/nomad@v0.11.8/drivers/docker/reconciler_test.go (about)

     1  package docker
     2  
     3  import (
     4  	"encoding/json"
     5  	"os"
     6  	"testing"
     7  	"time"
     8  
     9  	docker "github.com/fsouza/go-dockerclient"
    10  	"github.com/hashicorp/nomad/client/testutil"
    11  	"github.com/hashicorp/nomad/helper/freeport"
    12  	"github.com/hashicorp/nomad/helper/uuid"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  func fakeContainerList(t *testing.T) (nomadContainer, nonNomadContainer docker.APIContainers) {
    17  	path := "./test-resources/docker/reconciler_containers_list.json"
    18  
    19  	f, err := os.Open(path)
    20  	if err != nil {
    21  		t.Fatalf("failed to open file: %v", err)
    22  	}
    23  
    24  	var sampleContainerList []docker.APIContainers
    25  	err = json.NewDecoder(f).Decode(&sampleContainerList)
    26  	if err != nil {
    27  		t.Fatalf("failed to decode container list: %v", err)
    28  	}
    29  
    30  	return sampleContainerList[0], sampleContainerList[1]
    31  }
    32  
    33  func Test_HasMount(t *testing.T) {
    34  	nomadContainer, nonNomadContainer := fakeContainerList(t)
    35  
    36  	require.True(t, hasMount(nomadContainer, "/alloc"))
    37  	require.True(t, hasMount(nomadContainer, "/data"))
    38  	require.True(t, hasMount(nomadContainer, "/secrets"))
    39  	require.False(t, hasMount(nomadContainer, "/random"))
    40  
    41  	require.False(t, hasMount(nonNomadContainer, "/alloc"))
    42  	require.False(t, hasMount(nonNomadContainer, "/data"))
    43  	require.False(t, hasMount(nonNomadContainer, "/secrets"))
    44  	require.False(t, hasMount(nonNomadContainer, "/random"))
    45  }
    46  
    47  func Test_HasNomadName(t *testing.T) {
    48  	nomadContainer, nonNomadContainer := fakeContainerList(t)
    49  
    50  	require.True(t, hasNomadName(nomadContainer))
    51  	require.False(t, hasNomadName(nonNomadContainer))
    52  }
    53  
    54  // TestDanglingContainerRemoval asserts containers without corresponding tasks
    55  // are removed after the creation grace period.
    56  func TestDanglingContainerRemoval(t *testing.T) {
    57  	testutil.DockerCompatible(t)
    58  
    59  	// start two containers: one tracked nomad container, and one unrelated container
    60  	task, cfg, ports := dockerTask(t)
    61  	defer freeport.Return(ports)
    62  	require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
    63  
    64  	client, d, handle, cleanup := dockerSetup(t, task, nil)
    65  	defer cleanup()
    66  	require.NoError(t, d.WaitUntilStarted(task.ID, 5*time.Second))
    67  
    68  	nonNomadContainer, err := client.CreateContainer(docker.CreateContainerOptions{
    69  		Name: "mytest-image-" + uuid.Generate(),
    70  		Config: &docker.Config{
    71  			Image: cfg.Image,
    72  			Cmd:   append([]string{cfg.Command}, cfg.Args...),
    73  		},
    74  	})
    75  	require.NoError(t, err)
    76  	defer client.RemoveContainer(docker.RemoveContainerOptions{
    77  		ID:    nonNomadContainer.ID,
    78  		Force: true,
    79  	})
    80  
    81  	err = client.StartContainer(nonNomadContainer.ID, nil)
    82  	require.NoError(t, err)
    83  
    84  	untrackedNomadContainer, err := client.CreateContainer(docker.CreateContainerOptions{
    85  		Name: "mytest-image-" + uuid.Generate(),
    86  		Config: &docker.Config{
    87  			Image: cfg.Image,
    88  			Cmd:   append([]string{cfg.Command}, cfg.Args...),
    89  			Labels: map[string]string{
    90  				dockerLabelAllocID: uuid.Generate(),
    91  			},
    92  		},
    93  	})
    94  	require.NoError(t, err)
    95  	defer client.RemoveContainer(docker.RemoveContainerOptions{
    96  		ID:    untrackedNomadContainer.ID,
    97  		Force: true,
    98  	})
    99  
   100  	err = client.StartContainer(untrackedNomadContainer.ID, nil)
   101  	require.NoError(t, err)
   102  
   103  	dd := d.Impl().(*Driver)
   104  
   105  	reconciler := newReconciler(dd)
   106  	trackedContainers := map[string]bool{handle.containerID: true}
   107  
   108  	tf := reconciler.trackedContainers()
   109  	require.Contains(t, tf, handle.containerID)
   110  	require.NotContains(t, tf, untrackedNomadContainer)
   111  	require.NotContains(t, tf, nonNomadContainer.ID)
   112  
   113  	// assert tracked containers should never be untracked
   114  	untracked, err := reconciler.untrackedContainers(trackedContainers, time.Now())
   115  	require.NoError(t, err)
   116  	require.NotContains(t, untracked, handle.containerID)
   117  	require.NotContains(t, untracked, nonNomadContainer.ID)
   118  	require.Contains(t, untracked, untrackedNomadContainer.ID)
   119  
   120  	// assert we recognize nomad containers with appropriate cutoff
   121  	untracked, err = reconciler.untrackedContainers(map[string]bool{}, time.Now())
   122  	require.NoError(t, err)
   123  	require.Contains(t, untracked, handle.containerID)
   124  	require.Contains(t, untracked, untrackedNomadContainer.ID)
   125  	require.NotContains(t, untracked, nonNomadContainer.ID)
   126  
   127  	// but ignore if creation happened before cutoff
   128  	untracked, err = reconciler.untrackedContainers(map[string]bool{}, time.Now().Add(-1*time.Minute))
   129  	require.NoError(t, err)
   130  	require.NotContains(t, untracked, handle.containerID)
   131  	require.NotContains(t, untracked, untrackedNomadContainer.ID)
   132  	require.NotContains(t, untracked, nonNomadContainer.ID)
   133  
   134  	// a full integration tests to assert that containers are removed
   135  	prestineDriver := dockerDriverHarness(t, nil).Impl().(*Driver)
   136  	prestineDriver.config.GC.DanglingContainers = ContainerGCConfig{
   137  		Enabled:       true,
   138  		period:        1 * time.Second,
   139  		CreationGrace: 0 * time.Second,
   140  	}
   141  	nReconciler := newReconciler(prestineDriver)
   142  
   143  	require.NoError(t, nReconciler.removeDanglingContainersIteration())
   144  
   145  	_, err = client.InspectContainer(nonNomadContainer.ID)
   146  	require.NoError(t, err)
   147  
   148  	_, err = client.InspectContainer(handle.containerID)
   149  	require.Error(t, err)
   150  	require.Contains(t, err.Error(), NoSuchContainerError)
   151  
   152  	_, err = client.InspectContainer(untrackedNomadContainer.ID)
   153  	require.Error(t, err)
   154  	require.Contains(t, err.Error(), NoSuchContainerError)
   155  }
   156  
   157  // TestDanglingContainerRemoval_Stopped asserts stopped containers without
   158  // corresponding tasks are not removed even if after creation grace period.
   159  func TestDanglingContainerRemoval_Stopped(t *testing.T) {
   160  	testutil.DockerCompatible(t)
   161  
   162  	_, cfg, ports := dockerTask(t)
   163  	defer freeport.Return(ports)
   164  
   165  	client := newTestDockerClient(t)
   166  	container, err := client.CreateContainer(docker.CreateContainerOptions{
   167  		Name: "mytest-image-" + uuid.Generate(),
   168  		Config: &docker.Config{
   169  			Image: cfg.Image,
   170  			Cmd:   append([]string{cfg.Command}, cfg.Args...),
   171  			Labels: map[string]string{
   172  				dockerLabelAllocID: uuid.Generate(),
   173  			},
   174  		},
   175  	})
   176  	require.NoError(t, err)
   177  	defer client.RemoveContainer(docker.RemoveContainerOptions{
   178  		ID:    container.ID,
   179  		Force: true,
   180  	})
   181  
   182  	err = client.StartContainer(container.ID, nil)
   183  	require.NoError(t, err)
   184  
   185  	err = client.StopContainer(container.ID, 60)
   186  	require.NoError(t, err)
   187  
   188  	dd := dockerDriverHarness(t, nil).Impl().(*Driver)
   189  	reconciler := newReconciler(dd)
   190  
   191  	// assert nomad container is tracked, and we ignore stopped one
   192  	tf := reconciler.trackedContainers()
   193  	require.NotContains(t, tf, container.ID)
   194  
   195  	untracked, err := reconciler.untrackedContainers(map[string]bool{}, time.Now())
   196  	require.NoError(t, err)
   197  	require.NotContains(t, untracked, container.ID)
   198  
   199  	// if we start container again, it'll be marked as untracked
   200  	require.NoError(t, client.StartContainer(container.ID, nil))
   201  
   202  	untracked, err = reconciler.untrackedContainers(map[string]bool{}, time.Now())
   203  	require.NoError(t, err)
   204  	require.Contains(t, untracked, container.ID)
   205  }