2016-09-15 20:08:51 +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 (
2017-02-27 04:50:20 +00:00
"fmt"
2017-05-20 01:07:52 +00:00
"io/ioutil"
"os"
"path/filepath"
2016-09-15 20:08:51 +00:00
"testing"
2017-06-29 20:21:17 +00:00
dockertypes "github.com/docker/docker/api/types"
2017-03-20 08:52:38 +00:00
dockernat "github.com/docker/go-connections/nat"
2016-09-15 20:08:51 +00:00
"github.com/stretchr/testify/assert"
2016-11-12 00:09:23 +00:00
"github.com/stretchr/testify/require"
2016-09-27 22:13:27 +00:00
2018-02-06 22:11:09 +00:00
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
2017-05-03 17:46:35 +00:00
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
2017-09-08 22:50:36 +00:00
"k8s.io/kubernetes/pkg/security/apparmor"
2016-09-15 20:08:51 +00:00
)
func TestLabelsAndAnnotationsRoundTrip ( t * testing . T ) {
expectedLabels := map [ string ] string { "foo.123.abc" : "baz" , "bar.456.xyz" : "qwe" }
expectedAnnotations := map [ string ] string { "uio.ert" : "dfs" , "jkl" : "asd" }
// Merge labels and annotations into docker labels.
dockerLabels := makeLabels ( expectedLabels , expectedAnnotations )
// Extract labels and annotations from docker labels.
actualLabels , actualAnnotations := extractLabels ( dockerLabels )
assert . Equal ( t , expectedLabels , actualLabels )
assert . Equal ( t , expectedAnnotations , actualAnnotations )
}
2016-09-27 22:13:27 +00:00
2017-05-01 12:55:16 +00:00
// TestGetApparmorSecurityOpts tests the logic of generating container apparmor options from sandbox annotations.
func TestGetApparmorSecurityOpts ( t * testing . T ) {
makeConfig := func ( profile string ) * runtimeapi . LinuxContainerSecurityContext {
return & runtimeapi . LinuxContainerSecurityContext {
ApparmorProfile : profile ,
}
2016-09-27 22:13:27 +00:00
}
tests := [ ] struct {
msg string
2017-05-01 12:55:16 +00:00
config * runtimeapi . LinuxContainerSecurityContext
2016-09-27 22:13:27 +00:00
expectedOpts [ ] string
} { {
2017-05-01 12:55:16 +00:00
msg : "No AppArmor options" ,
config : makeConfig ( "" ) ,
2016-09-27 22:13:27 +00:00
expectedOpts : nil ,
} , {
2017-05-01 12:55:16 +00:00
msg : "AppArmor runtime/default" ,
config : makeConfig ( "runtime/default" ) ,
expectedOpts : [ ] string { } ,
2016-09-27 22:13:27 +00:00
} , {
2017-05-01 12:55:16 +00:00
msg : "AppArmor local profile" ,
config : makeConfig ( apparmor . ProfileNamePrefix + "foo" ) ,
expectedOpts : [ ] string { "apparmor=foo" } ,
2016-09-27 22:13:27 +00:00
} }
for i , test := range tests {
2017-05-01 12:55:16 +00:00
opts , err := getApparmorSecurityOpts ( test . config , '=' )
2016-09-27 22:13:27 +00:00
assert . NoError ( t , err , "TestCase[%d]: %s" , i , test . msg )
assert . Len ( t , opts , len ( test . expectedOpts ) , "TestCase[%d]: %s" , i , test . msg )
for _ , opt := range test . expectedOpts {
assert . Contains ( t , opts , opt , "TestCase[%d]: %s" , i , test . msg )
}
}
}
2016-10-14 15:51:24 +00:00
2016-11-14 23:33:22 +00:00
// TestGetUserFromImageUser tests the logic of getting image uid or user name of image user.
func TestGetUserFromImageUser ( t * testing . T ) {
newI64 := func ( i int64 ) * int64 { return & i }
for c , test := range map [ string ] struct {
user string
uid * int64
2017-01-20 01:55:37 +00:00
name string
2016-11-14 23:33:22 +00:00
} {
"no gid" : {
user : "0" ,
uid : newI64 ( 0 ) ,
} ,
"uid/gid" : {
user : "0:1" ,
uid : newI64 ( 0 ) ,
} ,
"empty user" : {
user : "" ,
} ,
"multiple spearators" : {
user : "1:2:3" ,
uid : newI64 ( 1 ) ,
} ,
"root username" : {
user : "root:root" ,
2017-01-20 01:55:37 +00:00
name : "root" ,
2016-11-14 23:33:22 +00:00
} ,
"username" : {
user : "test:test" ,
2017-01-20 01:55:37 +00:00
name : "test" ,
2016-11-14 23:33:22 +00:00
} ,
} {
t . Logf ( "TestCase - %q" , c )
actualUID , actualName := getUserFromImageUser ( test . user )
assert . Equal ( t , test . uid , actualUID )
assert . Equal ( t , test . name , actualName )
}
}
2016-11-12 00:09:23 +00:00
func TestParsingCreationConflictError ( t * testing . T ) {
// Expected error message from docker.
msg := "Conflict. The name \"/k8s_POD_pfpod_e2e-tests-port-forwarding-dlxt2_81a3469e-99e1-11e6-89f2-42010af00002_0\" is already in use by container 24666ab8c814d16f986449e504ea0159468ddf8da01897144a770f66dce0e14e. You have to remove (or rename) that container to be able to reuse that name."
matches := conflictRE . FindStringSubmatch ( msg )
require . Len ( t , matches , 2 )
require . Equal ( t , matches [ 1 ] , "24666ab8c814d16f986449e504ea0159468ddf8da01897144a770f66dce0e14e" )
}
2017-02-03 02:28:19 +00:00
2017-05-20 01:07:52 +00:00
// writeDockerConfig will write a config file into a temporary dir, and return that dir.
// Caller is responsible for deleting the dir and its contents.
func writeDockerConfig ( cfg string ) ( string , error ) {
tmpdir , err := ioutil . TempDir ( "" , "dockershim=helpers_test.go=" )
if err != nil {
return "" , err
}
dir := filepath . Join ( tmpdir , ".docker" )
if err := os . Mkdir ( dir , 0755 ) ; err != nil {
return "" , err
}
return tmpdir , ioutil . WriteFile ( filepath . Join ( dir , "config.json" ) , [ ] byte ( cfg ) , 0644 )
}
2017-02-27 04:50:20 +00:00
func TestEnsureSandboxImageExists ( t * testing . T ) {
2017-03-02 01:18:02 +00:00
sandboxImage := "gcr.io/test/image"
2017-05-20 01:07:52 +00:00
authConfig := dockertypes . AuthConfig { Username : "user" , Password : "pass" }
2017-02-27 04:50:20 +00:00
for desc , test := range map [ string ] struct {
2017-05-20 01:07:52 +00:00
injectImage bool
imgNeedsAuth bool
injectErr error
calls [ ] string
err bool
configJSON string
2017-02-27 04:50:20 +00:00
} {
"should not pull image when it already exists" : {
2017-03-02 01:18:02 +00:00
injectImage : true ,
injectErr : nil ,
calls : [ ] string { "inspect_image" } ,
2017-02-27 04:50:20 +00:00
} ,
"should pull image when it doesn't exist" : {
2017-03-02 01:18:02 +00:00
injectImage : false ,
2017-05-03 17:46:35 +00:00
injectErr : libdocker . ImageNotFoundError { ID : "image_id" } ,
2017-03-02 01:18:02 +00:00
calls : [ ] string { "inspect_image" , "pull" } ,
2017-02-27 04:50:20 +00:00
} ,
"should return error when inspect image fails" : {
2017-03-02 01:18:02 +00:00
injectImage : false ,
injectErr : fmt . Errorf ( "arbitrary error" ) ,
calls : [ ] string { "inspect_image" } ,
err : true ,
2017-02-27 04:50:20 +00:00
} ,
2017-05-20 01:07:52 +00:00
"should return error when image pull needs private auth, but none provided" : {
injectImage : true ,
imgNeedsAuth : true ,
injectErr : libdocker . ImageNotFoundError { ID : "image_id" } ,
calls : [ ] string { "inspect_image" , "pull" } ,
err : true ,
} ,
2017-02-27 04:50:20 +00:00
} {
t . Logf ( "TestCase: %q" , desc )
_ , fakeDocker , _ := newTestDockerService ( )
2017-03-02 01:18:02 +00:00
if test . injectImage {
2017-06-29 20:21:17 +00:00
images := [ ] dockertypes . ImageSummary { { ID : sandboxImage } }
2017-05-20 01:07:52 +00:00
fakeDocker . InjectImages ( images )
if test . imgNeedsAuth {
fakeDocker . MakeImagesPrivate ( images , authConfig )
}
2017-03-02 01:18:02 +00:00
}
fakeDocker . InjectError ( "inspect_image" , test . injectErr )
2017-05-20 01:07:52 +00:00
2017-09-08 22:50:36 +00:00
err := ensureSandboxImageExists ( fakeDocker , sandboxImage )
2017-02-27 04:50:20 +00:00
assert . NoError ( t , fakeDocker . AssertCalls ( test . calls ) )
assert . Equal ( t , test . err , err != nil )
}
}
2017-03-20 08:52:38 +00:00
func TestMakePortsAndBindings ( t * testing . T ) {
for desc , test := range map [ string ] struct {
pm [ ] * runtimeapi . PortMapping
2017-06-29 20:21:17 +00:00
exposedPorts dockernat . PortSet
2017-03-20 08:52:38 +00:00
portmappings map [ dockernat . Port ] [ ] dockernat . PortBinding
} {
"no port mapping" : {
pm : nil ,
exposedPorts : map [ dockernat . Port ] struct { } { } ,
portmappings : map [ dockernat . Port ] [ ] dockernat . PortBinding { } ,
} ,
"tcp port mapping" : {
pm : [ ] * runtimeapi . PortMapping {
{
Protocol : runtimeapi . Protocol_TCP ,
ContainerPort : 80 ,
HostPort : 80 ,
} ,
} ,
exposedPorts : map [ dockernat . Port ] struct { } {
"80/tcp" : { } ,
} ,
portmappings : map [ dockernat . Port ] [ ] dockernat . PortBinding {
"80/tcp" : {
{
HostPort : "80" ,
} ,
} ,
} ,
} ,
"udp port mapping" : {
pm : [ ] * runtimeapi . PortMapping {
{
Protocol : runtimeapi . Protocol_UDP ,
ContainerPort : 80 ,
HostPort : 80 ,
} ,
} ,
exposedPorts : map [ dockernat . Port ] struct { } {
"80/udp" : { } ,
} ,
portmappings : map [ dockernat . Port ] [ ] dockernat . PortBinding {
"80/udp" : {
{
HostPort : "80" ,
} ,
} ,
} ,
} ,
2018-02-09 06:53:53 +00:00
"multiple port mappings" : {
2017-03-20 08:52:38 +00:00
pm : [ ] * runtimeapi . PortMapping {
{
Protocol : runtimeapi . Protocol_TCP ,
ContainerPort : 80 ,
HostPort : 80 ,
} ,
{
Protocol : runtimeapi . Protocol_TCP ,
ContainerPort : 80 ,
HostPort : 81 ,
} ,
} ,
exposedPorts : map [ dockernat . Port ] struct { } {
"80/tcp" : { } ,
} ,
portmappings : map [ dockernat . Port ] [ ] dockernat . PortBinding {
"80/tcp" : {
{
HostPort : "80" ,
} ,
{
HostPort : "81" ,
} ,
} ,
} ,
} ,
} {
t . Logf ( "TestCase: %s" , desc )
actualExposedPorts , actualPortMappings := makePortsAndBindings ( test . pm )
assert . Equal ( t , test . exposedPorts , actualExposedPorts )
assert . Equal ( t , test . portmappings , actualPortMappings )
}
}
2017-08-29 08:01:04 +00:00
func TestGenerateMountBindings ( t * testing . T ) {
mounts := [ ] * runtimeapi . Mount {
// everything default
{
HostPath : "/mnt/1" ,
ContainerPath : "/var/lib/mysql/1" ,
} ,
// readOnly
{
HostPath : "/mnt/2" ,
ContainerPath : "/var/lib/mysql/2" ,
Readonly : true ,
} ,
// SELinux
{
HostPath : "/mnt/3" ,
ContainerPath : "/var/lib/mysql/3" ,
SelinuxRelabel : true ,
} ,
// Propagation private
{
HostPath : "/mnt/4" ,
ContainerPath : "/var/lib/mysql/4" ,
Propagation : runtimeapi . MountPropagation_PROPAGATION_PRIVATE ,
} ,
// Propagation rslave
{
HostPath : "/mnt/5" ,
ContainerPath : "/var/lib/mysql/5" ,
Propagation : runtimeapi . MountPropagation_PROPAGATION_HOST_TO_CONTAINER ,
} ,
// Propagation rshared
{
HostPath : "/mnt/6" ,
ContainerPath : "/var/lib/mysql/6" ,
Propagation : runtimeapi . MountPropagation_PROPAGATION_BIDIRECTIONAL ,
} ,
// Propagation unknown (falls back to private)
{
HostPath : "/mnt/7" ,
ContainerPath : "/var/lib/mysql/7" ,
Propagation : runtimeapi . MountPropagation ( 42 ) ,
} ,
// Everything
{
HostPath : "/mnt/8" ,
ContainerPath : "/var/lib/mysql/8" ,
Readonly : true ,
SelinuxRelabel : true ,
Propagation : runtimeapi . MountPropagation_PROPAGATION_BIDIRECTIONAL ,
} ,
}
expectedResult := [ ] string {
"/mnt/1:/var/lib/mysql/1" ,
"/mnt/2:/var/lib/mysql/2:ro" ,
"/mnt/3:/var/lib/mysql/3:Z" ,
"/mnt/4:/var/lib/mysql/4" ,
"/mnt/5:/var/lib/mysql/5:rslave" ,
"/mnt/6:/var/lib/mysql/6:rshared" ,
"/mnt/7:/var/lib/mysql/7" ,
"/mnt/8:/var/lib/mysql/8:ro,Z,rshared" ,
}
result := generateMountBindings ( mounts )
2018-02-04 07:14:55 +00:00
assert . Equal ( t , expectedResult , result )
2017-08-29 08:01:04 +00:00
}