2016-08-30 00:40:28 +00:00
/ *
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"
2016-10-14 18:52:18 +00:00
"path/filepath"
2017-01-25 19:47:01 +00:00
"strings"
2016-08-30 00:40:28 +00:00
"testing"
"time"
"github.com/stretchr/testify/assert"
2016-11-30 07:27:27 +00:00
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
2016-10-14 18:52:18 +00:00
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
2016-08-30 00:40:28 +00:00
)
// A helper to create a basic config.
2016-11-30 07:27:27 +00:00
func makeContainerConfig ( sConfig * runtimeapi . PodSandboxConfig , name , image string , attempt uint32 , labels , annotations map [ string ] string ) * runtimeapi . ContainerConfig {
return & runtimeapi . ContainerConfig {
Metadata : & runtimeapi . ContainerMetadata {
2017-01-20 01:55:37 +00:00
Name : name ,
Attempt : attempt ,
2016-08-30 00:40:28 +00:00
} ,
2017-01-20 01:55:37 +00:00
Image : & runtimeapi . ImageSpec { Image : image } ,
2016-09-15 20:08:51 +00:00
Labels : labels ,
Annotations : annotations ,
2016-08-30 00:40:28 +00:00
}
}
// TestListContainers creates several containers and then list them to check
// whether the correct metadatas, states, and labels are returned.
func TestListContainers ( t * testing . T ) {
2016-09-27 22:13:27 +00:00
ds , _ , _ := newTestDockerService ( )
2016-08-30 00:40:28 +00:00
podName , namespace := "foo" , "bar"
containerName , image := "sidecar" , "logger"
2016-11-30 07:27:27 +00:00
configs := [ ] * runtimeapi . ContainerConfig { }
sConfigs := [ ] * runtimeapi . PodSandboxConfig { }
2016-08-30 00:40:28 +00:00
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 )
2016-09-15 20:08:51 +00:00
labels := map [ string ] string { "abc.xyz" : fmt . Sprintf ( "label%d" , i ) }
2016-10-13 21:40:41 +00:00
annotations := map [ string ] string { "foo.bar.baz" : fmt . Sprintf ( "annotation%d" , i ) }
2016-08-30 00:40:28 +00:00
c := makeContainerConfig ( s , fmt . Sprintf ( "%s%d" , containerName , i ) ,
2016-09-15 20:08:51 +00:00
fmt . Sprintf ( "%s:v%d" , image , i ) , uint32 ( i ) , labels , annotations )
2016-08-30 00:40:28 +00:00
sConfigs = append ( sConfigs , s )
configs = append ( configs , c )
}
2016-11-30 07:27:27 +00:00
expected := [ ] * runtimeapi . Container { }
state := runtimeapi . ContainerState_CONTAINER_RUNNING
2016-10-06 21:52:40 +00:00
var createdAt int64 = 0
2016-08-30 00:40:28 +00:00
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.
2016-11-30 07:27:27 +00:00
expected = append ( [ ] * runtimeapi . Container { {
2016-10-02 03:55:06 +00:00
Metadata : configs [ i ] . Metadata ,
2017-01-20 01:55:37 +00:00
Id : id ,
PodSandboxId : sandboxID ,
State : state ,
CreatedAt : createdAt ,
2016-10-02 03:55:06 +00:00
Image : configs [ i ] . Image ,
2017-01-20 01:55:37 +00:00
ImageRef : imageRef ,
2016-10-02 03:55:06 +00:00
Labels : configs [ i ] . Labels ,
Annotations : configs [ i ] . Annotations ,
2016-08-30 00:40:28 +00:00
} } , 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 ) {
2016-10-01 00:08:32 +00:00
ds , fDocker , fClock := newTestDockerService ( )
2016-08-30 00:40:28 +00:00
sConfig := makeSandboxConfig ( "foo" , "bar" , "1" , 0 )
2016-09-15 20:08:51 +00:00
labels := map [ string ] string { "abc.xyz" : "foo" }
annotations := map [ string ] string { "foo.bar.baz" : "abc" }
config := makeContainerConfig ( sConfig , "pause" , "iamimage" , 0 , labels , annotations )
2016-08-30 00:40:28 +00:00
var defaultTime time . Time
2016-10-11 21:50:55 +00:00
dt := defaultTime . UnixNano ( )
2016-08-30 00:40:28 +00:00
ct , st , ft := dt , dt , dt
2016-11-30 07:27:27 +00:00
state := runtimeapi . ContainerState_CONTAINER_CREATED
2016-08-30 00:40:28 +00:00
// The following variables are not set in FakeDockerClient.
2016-10-08 04:35:18 +00:00
imageRef := DockerImageIDPrefix + ""
2016-08-30 00:40:28 +00:00
exitCode := int32 ( 0 )
2016-09-23 19:32:21 +00:00
var reason , message string
2016-08-30 00:40:28 +00:00
2016-11-30 07:27:27 +00:00
expected := & runtimeapi . ContainerStatus {
2017-01-20 01:55:37 +00:00
State : state ,
CreatedAt : ct ,
StartedAt : st ,
FinishedAt : ft ,
2016-09-15 20:08:51 +00:00
Metadata : config . Metadata ,
Image : config . Image ,
2017-01-20 01:55:37 +00:00
ImageRef : imageRef ,
ExitCode : exitCode ,
Reason : reason ,
Message : message ,
2016-11-30 07:27:27 +00:00
Mounts : [ ] * runtimeapi . Mount { } ,
2016-09-15 20:08:51 +00:00
Labels : config . Labels ,
Annotations : config . Annotations ,
2016-08-30 00:40:28 +00:00
}
// Create the container.
2016-09-25 05:43:39 +00:00
fClock . SetTime ( time . Now ( ) . Add ( - 1 * time . Hour ) )
2017-01-20 01:55:37 +00:00
expected . CreatedAt = fClock . Now ( ) . UnixNano ( )
2016-10-01 00:08:32 +00:00
const sandboxId = "sandboxid"
id , err := ds . CreateContainer ( sandboxId , config , sConfig )
// 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 )
2016-08-30 00:40:28 +00:00
// Set the id manually since we don't know the id until it's created.
2017-01-20 01:55:37 +00:00
expected . Id = id
2016-08-30 00:40:28 +00:00
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 ( ) )
2017-01-20 01:55:37 +00:00
expected . StartedAt = fClock . Now ( ) . UnixNano ( )
expected . State = runtimeapi . ContainerState_CONTAINER_RUNNING
2016-08-30 00:40:28 +00:00
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.
2016-09-25 05:43:39 +00:00
fClock . SetTime ( time . Now ( ) . Add ( 1 * time . Hour ) )
2017-01-20 01:55:37 +00:00
expected . FinishedAt = fClock . Now ( ) . UnixNano ( )
expected . State = runtimeapi . ContainerState_CONTAINER_EXITED
expected . Reason = "Completed"
2016-08-30 00:40:28 +00:00
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 ) )
}
2016-10-14 18:52:18 +00:00
// 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 )
2017-01-20 01:55:37 +00:00
sConfig . LogDirectory = podLogPath
2016-10-14 18:52:18 +00:00
config := makeContainerConfig ( sConfig , "pause" , "iamimage" , 0 , nil , nil )
2017-01-20 01:55:37 +00:00
config . LogPath = containerLogPath
2016-10-14 18:52:18 +00:00
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 } )
}
2017-01-25 19:47:01 +00:00
// 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" } ,
expectFields : 1 ,
} ,
"conflict create error with successful remove" : {
createError : conflictError ,
expectError : conflictError ,
expectCalls : [ ] string { "create" , "remove" } ,
expectFields : 1 ,
} ,
"conflict create error with random remove error" : {
createError : conflictError ,
removeError : randomError ,
expectError : conflictError ,
expectCalls : [ ] string { "create" , "remove" } ,
expectFields : 1 ,
} ,
"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 )
}
name , err := ds . CreateContainer ( sandboxId , config , sConfig )
assert . Equal ( t , test . expectError , err )
assert . NoError ( t , fDocker . AssertCalls ( test . expectCalls ) )
assert . Len ( t , strings . Split ( name , nameDelimiter ) , test . expectFields )
}
}