mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			292 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			292 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2016 The Kubernetes Authors.
 | 
						|
 | 
						|
Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
you may not use this file except in compliance with the License.
 | 
						|
You may obtain a copy of the License at
 | 
						|
 | 
						|
    http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
Unless required by applicable law or agreed to in writing, software
 | 
						|
distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
See the License for the specific language governing permissions and
 | 
						|
limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package dockershim
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"path/filepath"
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
	"time"
 | 
						|
 | 
						|
	dockertypes "github.com/docker/engine-api/types"
 | 
						|
	"github.com/stretchr/testify/assert"
 | 
						|
	"github.com/stretchr/testify/require"
 | 
						|
 | 
						|
	runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
 | 
						|
	containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
 | 
						|
)
 | 
						|
 | 
						|
// A helper to create a basic config.
 | 
						|
func makeContainerConfig(sConfig *runtimeapi.PodSandboxConfig, name, image string, attempt uint32, labels, annotations map[string]string) *runtimeapi.ContainerConfig {
 | 
						|
	return &runtimeapi.ContainerConfig{
 | 
						|
		Metadata: &runtimeapi.ContainerMetadata{
 | 
						|
			Name:    name,
 | 
						|
			Attempt: attempt,
 | 
						|
		},
 | 
						|
		Image:       &runtimeapi.ImageSpec{Image: image},
 | 
						|
		Labels:      labels,
 | 
						|
		Annotations: annotations,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// TestListContainers creates several containers and then list them to check
 | 
						|
// whether the correct metadatas, states, and labels are returned.
 | 
						|
func TestListContainers(t *testing.T) {
 | 
						|
	ds, _, fakeClock := newTestDockerService()
 | 
						|
	podName, namespace := "foo", "bar"
 | 
						|
	containerName, image := "sidecar", "logger"
 | 
						|
 | 
						|
	configs := []*runtimeapi.ContainerConfig{}
 | 
						|
	sConfigs := []*runtimeapi.PodSandboxConfig{}
 | 
						|
	for i := 0; i < 3; i++ {
 | 
						|
		s := makeSandboxConfig(fmt.Sprintf("%s%d", podName, i),
 | 
						|
			fmt.Sprintf("%s%d", namespace, i), fmt.Sprintf("%d", i), 0)
 | 
						|
		labels := map[string]string{"abc.xyz": fmt.Sprintf("label%d", i)}
 | 
						|
		annotations := map[string]string{"foo.bar.baz": fmt.Sprintf("annotation%d", i)}
 | 
						|
		c := makeContainerConfig(s, fmt.Sprintf("%s%d", containerName, i),
 | 
						|
			fmt.Sprintf("%s:v%d", image, i), uint32(i), labels, annotations)
 | 
						|
		sConfigs = append(sConfigs, s)
 | 
						|
		configs = append(configs, c)
 | 
						|
	}
 | 
						|
 | 
						|
	expected := []*runtimeapi.Container{}
 | 
						|
	state := runtimeapi.ContainerState_CONTAINER_RUNNING
 | 
						|
	var createdAt int64 = fakeClock.Now().UnixNano()
 | 
						|
	for i := range configs {
 | 
						|
		// We don't care about the sandbox id; pass a bogus one.
 | 
						|
		sandboxID := fmt.Sprintf("sandboxid%d", i)
 | 
						|
		id, err := ds.CreateContainer(sandboxID, configs[i], sConfigs[i])
 | 
						|
		assert.NoError(t, err)
 | 
						|
		err = ds.StartContainer(id)
 | 
						|
		assert.NoError(t, err)
 | 
						|
 | 
						|
		imageRef := "" // FakeDockerClient doesn't populate ImageRef yet.
 | 
						|
		// Prepend to the expected list because ListContainers returns
 | 
						|
		// the most recent containers first.
 | 
						|
		expected = append([]*runtimeapi.Container{{
 | 
						|
			Metadata:     configs[i].Metadata,
 | 
						|
			Id:           id,
 | 
						|
			PodSandboxId: sandboxID,
 | 
						|
			State:        state,
 | 
						|
			CreatedAt:    createdAt,
 | 
						|
			Image:        configs[i].Image,
 | 
						|
			ImageRef:     imageRef,
 | 
						|
			Labels:       configs[i].Labels,
 | 
						|
			Annotations:  configs[i].Annotations,
 | 
						|
		}}, expected...)
 | 
						|
	}
 | 
						|
	containers, err := ds.ListContainers(nil)
 | 
						|
	assert.NoError(t, err)
 | 
						|
	assert.Len(t, containers, len(expected))
 | 
						|
	assert.Equal(t, expected, containers)
 | 
						|
}
 | 
						|
 | 
						|
// TestContainerStatus tests the basic lifecycle operations and verify that
 | 
						|
// the status returned reflects the operations performed.
 | 
						|
func TestContainerStatus(t *testing.T) {
 | 
						|
	ds, fDocker, fClock := newTestDockerService()
 | 
						|
	sConfig := makeSandboxConfig("foo", "bar", "1", 0)
 | 
						|
	labels := map[string]string{"abc.xyz": "foo"}
 | 
						|
	annotations := map[string]string{"foo.bar.baz": "abc"}
 | 
						|
	imageName := "iamimage"
 | 
						|
	config := makeContainerConfig(sConfig, "pause", imageName, 0, labels, annotations)
 | 
						|
 | 
						|
	var defaultTime time.Time
 | 
						|
	dt := defaultTime.UnixNano()
 | 
						|
	ct, st, ft := dt, dt, dt
 | 
						|
	state := runtimeapi.ContainerState_CONTAINER_CREATED
 | 
						|
	imageRef := DockerImageIDPrefix + imageName
 | 
						|
	// The following variables are not set in FakeDockerClient.
 | 
						|
	exitCode := int32(0)
 | 
						|
	var reason, message string
 | 
						|
 | 
						|
	expected := &runtimeapi.ContainerStatus{
 | 
						|
		State:       state,
 | 
						|
		CreatedAt:   ct,
 | 
						|
		StartedAt:   st,
 | 
						|
		FinishedAt:  ft,
 | 
						|
		Metadata:    config.Metadata,
 | 
						|
		Image:       config.Image,
 | 
						|
		ImageRef:    imageRef,
 | 
						|
		ExitCode:    exitCode,
 | 
						|
		Reason:      reason,
 | 
						|
		Message:     message,
 | 
						|
		Mounts:      []*runtimeapi.Mount{},
 | 
						|
		Labels:      config.Labels,
 | 
						|
		Annotations: config.Annotations,
 | 
						|
	}
 | 
						|
 | 
						|
	fDocker.InjectImages([]dockertypes.Image{{ID: imageName}})
 | 
						|
 | 
						|
	// Create the container.
 | 
						|
	fClock.SetTime(time.Now().Add(-1 * time.Hour))
 | 
						|
	expected.CreatedAt = fClock.Now().UnixNano()
 | 
						|
	const sandboxId = "sandboxid"
 | 
						|
	id, err := ds.CreateContainer(sandboxId, config, sConfig)
 | 
						|
	assert.NoError(t, err)
 | 
						|
 | 
						|
	// Check internal labels
 | 
						|
	c, err := fDocker.InspectContainer(id)
 | 
						|
	assert.NoError(t, err)
 | 
						|
	assert.Equal(t, c.Config.Labels[containerTypeLabelKey], containerTypeLabelContainer)
 | 
						|
	assert.Equal(t, c.Config.Labels[sandboxIDLabelKey], sandboxId)
 | 
						|
 | 
						|
	// Set the id manually since we don't know the id until it's created.
 | 
						|
	expected.Id = id
 | 
						|
	assert.NoError(t, err)
 | 
						|
	status, err := ds.ContainerStatus(id)
 | 
						|
	assert.NoError(t, err)
 | 
						|
	assert.Equal(t, expected, status)
 | 
						|
 | 
						|
	// Advance the clock and start the container.
 | 
						|
	fClock.SetTime(time.Now())
 | 
						|
	expected.StartedAt = fClock.Now().UnixNano()
 | 
						|
	expected.State = runtimeapi.ContainerState_CONTAINER_RUNNING
 | 
						|
 | 
						|
	err = ds.StartContainer(id)
 | 
						|
	assert.NoError(t, err)
 | 
						|
	status, err = ds.ContainerStatus(id)
 | 
						|
	assert.Equal(t, expected, status)
 | 
						|
 | 
						|
	// Advance the clock and stop the container.
 | 
						|
	fClock.SetTime(time.Now().Add(1 * time.Hour))
 | 
						|
	expected.FinishedAt = fClock.Now().UnixNano()
 | 
						|
	expected.State = runtimeapi.ContainerState_CONTAINER_EXITED
 | 
						|
	expected.Reason = "Completed"
 | 
						|
 | 
						|
	err = ds.StopContainer(id, 0)
 | 
						|
	assert.NoError(t, err)
 | 
						|
	status, err = ds.ContainerStatus(id)
 | 
						|
	assert.Equal(t, expected, status)
 | 
						|
 | 
						|
	// Remove the container.
 | 
						|
	err = ds.RemoveContainer(id)
 | 
						|
	assert.NoError(t, err)
 | 
						|
	status, err = ds.ContainerStatus(id)
 | 
						|
	assert.Error(t, err, fmt.Sprintf("status of container: %+v", status))
 | 
						|
}
 | 
						|
 | 
						|
// TestContainerLogPath tests the container log creation logic.
 | 
						|
func TestContainerLogPath(t *testing.T) {
 | 
						|
	ds, fDocker, _ := newTestDockerService()
 | 
						|
	podLogPath := "/pod/1"
 | 
						|
	containerLogPath := "0"
 | 
						|
	kubeletContainerLogPath := filepath.Join(podLogPath, containerLogPath)
 | 
						|
	sConfig := makeSandboxConfig("foo", "bar", "1", 0)
 | 
						|
	sConfig.LogDirectory = podLogPath
 | 
						|
	config := makeContainerConfig(sConfig, "pause", "iamimage", 0, nil, nil)
 | 
						|
	config.LogPath = containerLogPath
 | 
						|
 | 
						|
	const sandboxId = "sandboxid"
 | 
						|
	id, err := ds.CreateContainer(sandboxId, config, sConfig)
 | 
						|
 | 
						|
	// Check internal container log label
 | 
						|
	c, err := fDocker.InspectContainer(id)
 | 
						|
	assert.NoError(t, err)
 | 
						|
	assert.Equal(t, c.Config.Labels[containerLogPathLabelKey], kubeletContainerLogPath)
 | 
						|
 | 
						|
	// Set docker container log path
 | 
						|
	dockerContainerLogPath := "/docker/container/log"
 | 
						|
	c.LogPath = dockerContainerLogPath
 | 
						|
 | 
						|
	// Verify container log symlink creation
 | 
						|
	fakeOS := ds.os.(*containertest.FakeOS)
 | 
						|
	fakeOS.SymlinkFn = func(oldname, newname string) error {
 | 
						|
		assert.Equal(t, dockerContainerLogPath, oldname)
 | 
						|
		assert.Equal(t, kubeletContainerLogPath, newname)
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	err = ds.StartContainer(id)
 | 
						|
	assert.NoError(t, err)
 | 
						|
 | 
						|
	err = ds.StopContainer(id, 0)
 | 
						|
	assert.NoError(t, err)
 | 
						|
 | 
						|
	// Verify container log symlink deletion
 | 
						|
	err = ds.RemoveContainer(id)
 | 
						|
	assert.NoError(t, err)
 | 
						|
	assert.Equal(t, fakeOS.Removes, []string{kubeletContainerLogPath})
 | 
						|
}
 | 
						|
 | 
						|
// TestContainerCreationConflict tests the logic to work around docker container
 | 
						|
// creation naming conflict bug.
 | 
						|
func TestContainerCreationConflict(t *testing.T) {
 | 
						|
	sConfig := makeSandboxConfig("foo", "bar", "1", 0)
 | 
						|
	config := makeContainerConfig(sConfig, "pause", "iamimage", 0, map[string]string{}, map[string]string{})
 | 
						|
	containerName := makeContainerName(sConfig, config)
 | 
						|
	const sandboxId = "sandboxid"
 | 
						|
	const containerId = "containerid"
 | 
						|
	conflictError := fmt.Errorf("Error response from daemon: Conflict. The name \"/%s\" is already in use by container %s. You have to remove (or rename) that container to be able to reuse that name.",
 | 
						|
		containerName, containerId)
 | 
						|
	noContainerError := fmt.Errorf("Error response from daemon: No such container: %s", containerId)
 | 
						|
	randomError := fmt.Errorf("random error")
 | 
						|
 | 
						|
	for desc, test := range map[string]struct {
 | 
						|
		createError  error
 | 
						|
		removeError  error
 | 
						|
		expectError  error
 | 
						|
		expectCalls  []string
 | 
						|
		expectFields int
 | 
						|
	}{
 | 
						|
		"no create error": {
 | 
						|
			expectCalls:  []string{"create"},
 | 
						|
			expectFields: 6,
 | 
						|
		},
 | 
						|
		"random create error": {
 | 
						|
			createError: randomError,
 | 
						|
			expectError: randomError,
 | 
						|
			expectCalls: []string{"create"},
 | 
						|
		},
 | 
						|
		"conflict create error with successful remove": {
 | 
						|
			createError: conflictError,
 | 
						|
			expectError: conflictError,
 | 
						|
			expectCalls: []string{"create", "remove"},
 | 
						|
		},
 | 
						|
		"conflict create error with random remove error": {
 | 
						|
			createError: conflictError,
 | 
						|
			removeError: randomError,
 | 
						|
			expectError: conflictError,
 | 
						|
			expectCalls: []string{"create", "remove"},
 | 
						|
		},
 | 
						|
		"conflict create error with no such container remove error": {
 | 
						|
			createError:  conflictError,
 | 
						|
			removeError:  noContainerError,
 | 
						|
			expectCalls:  []string{"create", "remove", "create"},
 | 
						|
			expectFields: 7,
 | 
						|
		},
 | 
						|
	} {
 | 
						|
		t.Logf("TestCase: %s", desc)
 | 
						|
		ds, fDocker, _ := newTestDockerService()
 | 
						|
 | 
						|
		if test.createError != nil {
 | 
						|
			fDocker.InjectError("create", test.createError)
 | 
						|
		}
 | 
						|
		if test.removeError != nil {
 | 
						|
			fDocker.InjectError("remove", test.removeError)
 | 
						|
		}
 | 
						|
		id, err := ds.CreateContainer(sandboxId, config, sConfig)
 | 
						|
		require.Equal(t, test.expectError, err)
 | 
						|
		assert.NoError(t, fDocker.AssertCalls(test.expectCalls))
 | 
						|
		if err == nil {
 | 
						|
			c, err := fDocker.InspectContainer(id)
 | 
						|
			assert.NoError(t, err)
 | 
						|
			assert.Len(t, strings.Split(c.Name, nameDelimiter), test.expectFields)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |