func TestContainerStatsCollectionReconnection(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockDockerClient := ecsengine.NewMockDockerClient(ctrl)

	dockerID := "container1"
	ctx, cancel := context.WithCancel(context.TODO())

	statChan := make(chan *docker.Stats)
	statErr := fmt.Errorf("test error")
	closedChan := make(chan *docker.Stats)
	close(closedChan)
	gomock.InOrder(
		mockDockerClient.EXPECT().Stats(dockerID, ctx).Return(nil, statErr),
		mockDockerClient.EXPECT().Stats(dockerID, ctx).Return(closedChan, nil),
		mockDockerClient.EXPECT().Stats(dockerID, ctx).Return(statChan, nil),
	)

	container := &StatsContainer{
		containerMetadata: &ContainerMetadata{
			DockerID: dockerID,
		},
		ctx:    ctx,
		cancel: cancel,
		client: mockDockerClient,
	}
	container.StartStatsCollection()
	time.Sleep(checkPointSleep)
	container.StopStatsCollection()
}
示例#2
0
func TestStatsEngineClientErrorListingContainers(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	engine := NewDockerStatsEngine(&cfg)
	mockDockerClient := ecsengine.NewMockDockerClient(mockCtrl)
	// Mock client will return error while listing images.
	mockDockerClient.EXPECT().ListContainers(false).Return(ecsengine.ListContainersResponse{DockerIds: nil, Error: fmt.Errorf("could not list containers")})
	engine.client = mockDockerClient
	mockChannel := make(chan ecsengine.DockerContainerChangeEvent)
	mockDockerClient.EXPECT().ContainerEvents(gomock.Any()).Return(mockChannel, nil)
	engine.client = mockDockerClient
	engine.Init()

	time.Sleep(waitForCleanupSleep)
	// Make sure that the stats engine deregisters the event listener when it fails to
	// list images.
	if engine.ctx.Err() != context.Canceled {
		t.Error("Engine context hasn't been canceled")
	}
}
func TestContainerStatsCollectionStopsIfContainerIsTerminal(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockDockerClient := ecsengine.NewMockDockerClient(ctrl)
	resolver := mock_resolver.NewMockContainerMetadataResolver(ctrl)

	dockerID := "container1"
	ctx, cancel := context.WithCancel(context.TODO())

	closedChan := make(chan *docker.Stats)
	close(closedChan)

	statsErr := fmt.Errorf("test error")
	mockContainer := &api.DockerContainer{
		DockerId: dockerID,
		Container: &api.Container{
			KnownStatus: api.ContainerStopped,
		},
	}
	gomock.InOrder(
		mockDockerClient.EXPECT().Stats(dockerID, ctx).Return(closedChan, nil),
		resolver.EXPECT().ResolveContainer(dockerID).Return(mockContainer, statsErr),
	)

	container := &StatsContainer{
		containerMetadata: &ContainerMetadata{
			DockerID: dockerID,
		},
		ctx:      ctx,
		cancel:   cancel,
		client:   mockDockerClient,
		resolver: resolver,
	}
	container.StartStatsCollection()
	select {
	case <-ctx.Done():
	}
}
func TestStatsEngineAddRemoveContainers(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	resolver := mock_resolver.NewMockContainerMetadataResolver(ctrl)
	mockDockerClient := ecsengine.NewMockDockerClient(ctrl)
	t1 := &api.Task{Arn: "t1", Family: "f1"}
	t2 := &api.Task{Arn: "t2", Family: "f2"}
	t3 := &api.Task{Arn: "t3"}
	resolver.EXPECT().ResolveTask("c1").AnyTimes().Return(t1, nil)
	resolver.EXPECT().ResolveTask("c2").AnyTimes().Return(t1, nil)
	resolver.EXPECT().ResolveTask("c3").AnyTimes().Return(t2, nil)
	resolver.EXPECT().ResolveTask("c4").AnyTimes().Return(nil, fmt.Errorf("unmapped container"))
	resolver.EXPECT().ResolveTask("c5").AnyTimes().Return(t2, nil)
	resolver.EXPECT().ResolveTask("c6").AnyTimes().Return(t3, nil)
	resolver.EXPECT().ResolveContainer(gomock.Any()).AnyTimes().Return(&api.DockerContainer{
		Container: &api.Container{},
	}, nil)
	mockStatsChannel := make(chan *docker.Stats)
	defer close(mockStatsChannel)
	mockDockerClient.EXPECT().Stats(gomock.Any(), gomock.Any()).Return(mockStatsChannel, nil).AnyTimes()

	engine := NewDockerStatsEngine(&cfg, nil, eventStream("TestStatsEngineAddRemoveContainers"))
	engine.resolver = resolver
	engine.client = mockDockerClient
	engine.cluster = defaultCluster
	engine.containerInstanceArn = defaultContainerInstance
	defer engine.removeAll()

	engine.addContainer("c1")
	engine.addContainer("c1")

	if len(engine.tasksToContainers) != 1 {
		t.Errorf("Adding containers failed. Expected num tasks = 1, got: %d", len(engine.tasksToContainers))
	}

	containers, _ := engine.tasksToContainers["t1"]
	if len(containers) != 1 {
		t.Error("Adding duplicate containers failed.")
	}
	_, exists := containers["c1"]
	if !exists {
		t.Error("Container c1 not found in engine")
	}

	engine.addContainer("c2")
	containers, _ = engine.tasksToContainers["t1"]
	_, exists = containers["c2"]
	if !exists {
		t.Error("Container c2 not found in engine")
	}

	for _, statsContainer := range containers {
		for _, fakeContainerStats := range createFakeContainerStats() {
			statsContainer.statsQueue.Add(fakeContainerStats)
		}
	}

	// Ensure task shows up in metrics.
	containerMetrics, err := engine.getContainerMetricsForTask("t1")
	if err != nil {
		t.Errorf("Error getting container metrics: %v", err)
	}
	err = validateContainerMetrics(containerMetrics, 2)
	if err != nil {
		t.Errorf("Error validating container metrics: %v", err)
	}

	metadata, taskMetrics, err := engine.GetInstanceMetrics()
	if err != nil {
		t.Errorf("Error gettting instance metrics: %v", err)
	}

	err = validateMetricsMetadata(metadata)
	if err != nil {
		t.Errorf("Error validating metadata: %v", err)
	}
	if len(taskMetrics) != 1 {
		t.Errorf("Incorrect number of tasks. Expected: 1, got: %d", len(taskMetrics))
	}
	err = validateContainerMetrics(taskMetrics[0].ContainerMetrics, 2)
	if err != nil {
		t.Errorf("Error validating container metrics: %v", err)
	}
	if *taskMetrics[0].TaskArn != "t1" {
		t.Errorf("Incorrect task arn. Expected: t1, got: %s", *taskMetrics[0].TaskArn)
	}

	// Ensure that only valid task shows up in metrics.
	_, err = engine.getContainerMetricsForTask("t2")
	if err == nil {
		t.Error("Expected non-empty error for non existent task")
	}

	engine.removeContainer("c1")
	containers, _ = engine.tasksToContainers["t1"]
	_, exists = containers["c1"]
	if exists {
		t.Error("Container c1 not removed from engine")
	}
	engine.removeContainer("c2")
	containers, _ = engine.tasksToContainers["t1"]
	_, exists = containers["c2"]
	if exists {
		t.Error("Container c2 not removed from engine")
	}
	engine.addContainer("c3")
	containers, _ = engine.tasksToContainers["t2"]
	_, exists = containers["c3"]
	if !exists {
		t.Error("Container c3 not found in engine")
	}

	_, _, err = engine.GetInstanceMetrics()
	if err == nil {
		t.Error("Expected non-empty error for empty stats.")
	}
	engine.removeContainer("c3")

	// Should get an error while adding this container due to unmapped
	// container to task.
	engine.addContainer("c4")
	err = validateIdleContainerMetrics(engine)
	if err != nil {
		t.Fatalf("Error validating metadata: %v", err)
	}

	// Should get an error while adding this container due to unmapped
	// task arn to task definition family.
	engine.addContainer("c6")
	err = validateIdleContainerMetrics(engine)
	if err != nil {
		t.Fatalf("Error validating metadata: %v", err)
	}
}
func TestContainerStatsCollection(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockDockerClient := ecsengine.NewMockDockerClient(ctrl)

	dockerID := "container1"
	ctx, cancel := context.WithCancel(context.TODO())
	statChan := make(chan *docker.Stats)
	mockDockerClient.EXPECT().Stats(dockerID, ctx).Return(statChan, nil)
	go func() {
		for _, stat := range statsData {
			// doing this with json makes me sad, but is the easiest way to
			// deal with the docker.Stats.MemoryStats inner struct
			jsonStat := fmt.Sprintf(`
				{
					"memory_stats": {"usage":%d},
					"cpu_stats":{
						"cpu_usage":{
							"percpu_usage":[%d],
							"total_usage":%d
						}
					}
				}`, stat.memBytes, stat.cpuTime, stat.cpuTime)
			dockerStat := &docker.Stats{}
			json.Unmarshal([]byte(jsonStat), dockerStat)
			dockerStat.Read = stat.timestamp
			statChan <- dockerStat
		}
	}()

	container := &StatsContainer{
		containerMetadata: &ContainerMetadata{
			DockerID: dockerID,
		},
		ctx:    ctx,
		cancel: cancel,
		client: mockDockerClient,
	}
	container.StartStatsCollection()
	time.Sleep(checkPointSleep)
	container.StopStatsCollection()
	cpuStatsSet, err := container.statsQueue.GetCPUStatsSet()
	if err != nil {
		t.Fatal("Error gettting cpu stats set:", err)
	}
	if *cpuStatsSet.Min == math.MaxFloat64 || math.IsNaN(*cpuStatsSet.Min) {
		t.Error("Min value incorrectly set: ", *cpuStatsSet.Min)
	}
	if *cpuStatsSet.Max == -math.MaxFloat64 || math.IsNaN(*cpuStatsSet.Max) {
		t.Error("Max value incorrectly set: ", *cpuStatsSet.Max)
	}
	if *cpuStatsSet.SampleCount == 0 {
		t.Error("Samplecount is 0")
	}
	if *cpuStatsSet.Sum == 0 {
		t.Error("Sum value incorrectly set: ", *cpuStatsSet.Sum)
	}

	memStatsSet, err := container.statsQueue.GetMemoryStatsSet()
	if err != nil {
		t.Error("Error gettting cpu stats set:", err)
	}
	if *memStatsSet.Min == math.MaxFloat64 {
		t.Error("Min value incorrectly set: ", *memStatsSet.Min)
	}
	if *memStatsSet.Max == 0 {
		t.Error("Max value incorrectly set: ", *memStatsSet.Max)
	}
	if *memStatsSet.SampleCount == 0 {
		t.Error("Samplecount is 0")
	}
	if *memStatsSet.Sum == 0 {
		t.Error("Sum value incorrectly set: ", *memStatsSet.Sum)
	}
}