github.com/zhizhiboom/nomad@v0.8.5-0.20180907175415-f28fd3a1a056/client/driver/docker_coordinator_test.go (about)

     1  package driver
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	docker "github.com/fsouza/go-dockerclient"
     9  	"github.com/hashicorp/nomad/helper/testlog"
    10  	"github.com/hashicorp/nomad/helper/uuid"
    11  	"github.com/hashicorp/nomad/testutil"
    12  )
    13  
    14  type mockImageClient struct {
    15  	pulled    map[string]int
    16  	idToName  map[string]string
    17  	removed   map[string]int
    18  	pullDelay time.Duration
    19  }
    20  
    21  func newMockImageClient(idToName map[string]string, pullDelay time.Duration) *mockImageClient {
    22  	return &mockImageClient{
    23  		pulled:    make(map[string]int),
    24  		removed:   make(map[string]int),
    25  		idToName:  idToName,
    26  		pullDelay: pullDelay,
    27  	}
    28  }
    29  
    30  func (m *mockImageClient) PullImage(opts docker.PullImageOptions, auth docker.AuthConfiguration) error {
    31  	time.Sleep(m.pullDelay)
    32  	m.pulled[opts.Repository]++
    33  	return nil
    34  }
    35  
    36  func (m *mockImageClient) InspectImage(id string) (*docker.Image, error) {
    37  	return &docker.Image{
    38  		ID: m.idToName[id],
    39  	}, nil
    40  }
    41  
    42  func (m *mockImageClient) RemoveImage(id string) error {
    43  	m.removed[id]++
    44  	return nil
    45  }
    46  
    47  func TestDockerCoordinator_ConcurrentPulls(t *testing.T) {
    48  	t.Parallel()
    49  	image := "foo"
    50  	imageID := uuid.Generate()
    51  	mapping := map[string]string{imageID: image}
    52  
    53  	// Add a delay so we can get multiple queued up
    54  	mock := newMockImageClient(mapping, 10*time.Millisecond)
    55  	config := &dockerCoordinatorConfig{
    56  		logger:      testlog.Logger(t),
    57  		cleanup:     true,
    58  		client:      mock,
    59  		removeDelay: 100 * time.Millisecond,
    60  	}
    61  
    62  	// Create a coordinator
    63  	coordinator := NewDockerCoordinator(config)
    64  
    65  	id := ""
    66  	for i := 0; i < 10; i++ {
    67  		go func() {
    68  			id, _ = coordinator.PullImage(image, nil, uuid.Generate(), nil)
    69  		}()
    70  	}
    71  
    72  	testutil.WaitForResult(func() (bool, error) {
    73  		p := mock.pulled[image]
    74  		if p >= 10 {
    75  			return false, fmt.Errorf("Wrong number of pulls: %d", p)
    76  		}
    77  
    78  		// Check the reference count
    79  		if references := coordinator.imageRefCount[id]; len(references) != 10 {
    80  			return false, fmt.Errorf("Got reference count %d; want %d", len(references), 10)
    81  		}
    82  
    83  		// Ensure there is no pull future
    84  		if len(coordinator.pullFutures) != 0 {
    85  			return false, fmt.Errorf("Pull future exists after pull finished")
    86  		}
    87  
    88  		return true, nil
    89  	}, func(err error) {
    90  		t.Fatalf("err: %v", err)
    91  	})
    92  }
    93  
    94  func TestDockerCoordinator_Pull_Remove(t *testing.T) {
    95  	t.Parallel()
    96  	image := "foo"
    97  	imageID := uuid.Generate()
    98  	mapping := map[string]string{imageID: image}
    99  
   100  	// Add a delay so we can get multiple queued up
   101  	mock := newMockImageClient(mapping, 10*time.Millisecond)
   102  	config := &dockerCoordinatorConfig{
   103  		logger:      testlog.Logger(t),
   104  		cleanup:     true,
   105  		client:      mock,
   106  		removeDelay: 1 * time.Millisecond,
   107  	}
   108  
   109  	// Create a coordinator
   110  	coordinator := NewDockerCoordinator(config)
   111  
   112  	id := ""
   113  	callerIDs := make([]string, 10, 10)
   114  	for i := 0; i < 10; i++ {
   115  		callerIDs[i] = uuid.Generate()
   116  		id, _ = coordinator.PullImage(image, nil, callerIDs[i], nil)
   117  	}
   118  
   119  	// Check the reference count
   120  	if references := coordinator.imageRefCount[id]; len(references) != 10 {
   121  		t.Fatalf("Got reference count %d; want %d", len(references), 10)
   122  	}
   123  
   124  	// Remove some
   125  	for i := 0; i < 8; i++ {
   126  		coordinator.RemoveImage(id, callerIDs[i])
   127  	}
   128  
   129  	// Check the reference count
   130  	if references := coordinator.imageRefCount[id]; len(references) != 2 {
   131  		t.Fatalf("Got reference count %d; want %d", len(references), 2)
   132  	}
   133  
   134  	// Remove all
   135  	for i := 8; i < 10; i++ {
   136  		coordinator.RemoveImage(id, callerIDs[i])
   137  	}
   138  
   139  	// Check the reference count
   140  	if references := coordinator.imageRefCount[id]; len(references) != 0 {
   141  		t.Fatalf("Got reference count %d; want %d", len(references), 0)
   142  	}
   143  
   144  	// Check that only one delete happened
   145  	testutil.WaitForResult(func() (bool, error) {
   146  		removes := mock.removed[id]
   147  		return removes == 1, fmt.Errorf("Wrong number of removes: %d", removes)
   148  	}, func(err error) {
   149  		t.Fatalf("err: %v", err)
   150  	})
   151  
   152  	// Make sure there is no future still
   153  	if _, ok := coordinator.deleteFuture[id]; ok {
   154  		t.Fatal("Got delete future")
   155  	}
   156  }
   157  
   158  func TestDockerCoordinator_Remove_Cancel(t *testing.T) {
   159  	t.Parallel()
   160  	image := "foo"
   161  	imageID := uuid.Generate()
   162  	mapping := map[string]string{imageID: image}
   163  
   164  	mock := newMockImageClient(mapping, 1*time.Millisecond)
   165  	config := &dockerCoordinatorConfig{
   166  		logger:      testlog.Logger(t),
   167  		cleanup:     true,
   168  		client:      mock,
   169  		removeDelay: 100 * time.Millisecond,
   170  	}
   171  
   172  	// Create a coordinator
   173  	coordinator := NewDockerCoordinator(config)
   174  	callerID := uuid.Generate()
   175  
   176  	// Pull image
   177  	id, _ := coordinator.PullImage(image, nil, callerID, nil)
   178  
   179  	// Check the reference count
   180  	if references := coordinator.imageRefCount[id]; len(references) != 1 {
   181  		t.Fatalf("Got reference count %d; want %d", len(references), 1)
   182  	}
   183  
   184  	// Remove image
   185  	coordinator.RemoveImage(id, callerID)
   186  
   187  	// Check the reference count
   188  	if references := coordinator.imageRefCount[id]; len(references) != 0 {
   189  		t.Fatalf("Got reference count %d; want %d", len(references), 0)
   190  	}
   191  
   192  	// Pull image again within delay
   193  	id, _ = coordinator.PullImage(image, nil, callerID, nil)
   194  
   195  	// Check the reference count
   196  	if references := coordinator.imageRefCount[id]; len(references) != 1 {
   197  		t.Fatalf("Got reference count %d; want %d", len(references), 1)
   198  	}
   199  
   200  	// Check that only no delete happened
   201  	if removes := mock.removed[id]; removes != 0 {
   202  		t.Fatalf("Image deleted when it shouldn't have")
   203  	}
   204  }
   205  
   206  func TestDockerCoordinator_No_Cleanup(t *testing.T) {
   207  	t.Parallel()
   208  	image := "foo"
   209  	imageID := uuid.Generate()
   210  	mapping := map[string]string{imageID: image}
   211  
   212  	mock := newMockImageClient(mapping, 1*time.Millisecond)
   213  	config := &dockerCoordinatorConfig{
   214  		logger:      testlog.Logger(t),
   215  		cleanup:     false,
   216  		client:      mock,
   217  		removeDelay: 1 * time.Millisecond,
   218  	}
   219  
   220  	// Create a coordinator
   221  	coordinator := NewDockerCoordinator(config)
   222  	callerID := uuid.Generate()
   223  
   224  	// Pull image
   225  	id, _ := coordinator.PullImage(image, nil, callerID, nil)
   226  
   227  	// Check the reference count
   228  	if references := coordinator.imageRefCount[id]; len(references) != 0 {
   229  		t.Fatalf("Got reference count %d; want %d", len(references), 0)
   230  	}
   231  
   232  	// Remove image
   233  	coordinator.RemoveImage(id, callerID)
   234  
   235  	// Check that only no delete happened
   236  	if removes := mock.removed[id]; removes != 0 {
   237  		t.Fatalf("Image deleted when it shouldn't have")
   238  	}
   239  }