compose/kube/resources/pod_test.go

1034 lines
22 KiB
Go

// +build kube
/*
Copyright 2020 Docker Compose CLI 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 resources
import (
"fmt"
"os"
"runtime"
"testing"
"github.com/compose-spec/compose-go/loader"
"github.com/compose-spec/compose-go/types"
"github.com/stretchr/testify/assert"
apiv1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
)
func loadYAML(yaml string) (*types.Project, error) {
dict, err := loader.ParseYAML([]byte(yaml))
if err != nil {
return nil, err
}
workingDir, err := os.Getwd()
if err != nil {
panic(err)
}
configs := []types.ConfigFile{
{
Filename: "test-compose.yaml",
Config: dict,
},
}
config := types.ConfigDetails{
WorkingDir: workingDir,
ConfigFiles: configs,
Environment: nil,
}
return loader.Load(config)
}
func podTemplate(t *testing.T, yaml string) apiv1.PodTemplateSpec {
res, err := podTemplateWithError(yaml)
assert.NoError(t, err)
return res
}
func podTemplateWithError(yaml string) (apiv1.PodTemplateSpec, error) {
model, err := loadYAML(yaml)
if err != nil {
return apiv1.PodTemplateSpec{}, err
}
return toPodTemplate(model, model.Services[0], nil)
}
func TestToPodWithDockerSocket(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("on windows, source path validation is broken (and actually, source validation for windows workload is broken too). Skip it for now, as we don't support it yet")
return
}
podTemplate := podTemplate(t, `
version: "3"
services:
redis:
image: "redis:alpine"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
`)
expectedVolume := apiv1.Volume{
Name: "mount-0",
VolumeSource: apiv1.VolumeSource{
HostPath: &apiv1.HostPathVolumeSource{
Path: "/var/run",
},
},
}
expectedMount := apiv1.VolumeMount{
Name: "mount-0",
MountPath: "/var/run/docker.sock",
SubPath: "docker.sock",
}
assert.Len(t, podTemplate.Spec.Volumes, 1)
assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0])
assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
}
func TestToPodWithFunkyCommand(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
redis:
image: basi/node-exporter
command: ["-collector.procfs", "/host/proc", "-collector.sysfs", "/host/sys"]
`)
expectedArgs := []string{
`-collector.procfs`,
`/host/proc`, // ?
`-collector.sysfs`,
`/host/sys`, // ?
}
assert.Equal(t, expectedArgs, podTemplate.Spec.Containers[0].Args)
}
/* FIXME
func TestToPodWithGlobalVolume(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
db:
image: "postgres:9.4"
volumes:
- dbdata:/var/lib/postgresql/data
volumes:
dbdata:
`)
expectedMount := apiv1.VolumeMount{
Name: "dbdata",
MountPath: "/var/lib/postgresql/data",
}
assert.Len(t, podTemplate.Spec.Volumes, 0)
assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
}
*/
func TestToPodWithResources(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
db:
image: "postgres:9.4"
deploy:
resources:
limits:
cpus: "0.001"
memory: 50Mb
reservations:
cpus: "0.0001"
memory: 20Mb
`)
expectedResourceRequirements := apiv1.ResourceRequirements{
Limits: map[apiv1.ResourceName]resource.Quantity{
apiv1.ResourceCPU: resource.MustParse("0.001"),
apiv1.ResourceMemory: resource.MustParse(fmt.Sprintf("%d", 50*1024*1024)),
},
Requests: map[apiv1.ResourceName]resource.Quantity{
apiv1.ResourceCPU: resource.MustParse("0.0001"),
apiv1.ResourceMemory: resource.MustParse(fmt.Sprintf("%d", 20*1024*1024)),
},
}
assert.Equal(t, expectedResourceRequirements, podTemplate.Spec.Containers[0].Resources)
}
func TestToPodWithCapabilities(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
redis:
image: "redis:alpine"
cap_add:
- ALL
cap_drop:
- NET_ADMIN
- SYS_ADMIN
`)
expectedSecurityContext := &apiv1.SecurityContext{
Capabilities: &apiv1.Capabilities{
Add: []apiv1.Capability{"ALL"},
Drop: []apiv1.Capability{"NET_ADMIN", "SYS_ADMIN"},
},
}
assert.Equal(t, expectedSecurityContext, podTemplate.Spec.Containers[0].SecurityContext)
}
func TestToPodWithReadOnly(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
redis:
image: "redis:alpine"
read_only: true
`)
yes := true
expectedSecurityContext := &apiv1.SecurityContext{
ReadOnlyRootFilesystem: &yes,
}
assert.Equal(t, expectedSecurityContext, podTemplate.Spec.Containers[0].SecurityContext)
}
func TestToPodWithPrivileged(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
redis:
image: "redis:alpine"
privileged: true
`)
yes := true
expectedSecurityContext := &apiv1.SecurityContext{
Privileged: &yes,
}
assert.Equal(t, expectedSecurityContext, podTemplate.Spec.Containers[0].SecurityContext)
}
func TestToPodWithEnvNilShouldErrorOut(t *testing.T) {
_, err := podTemplateWithError(`
version: "3"
services:
redis:
image: "redis:alpine"
environment:
- SESSION_SECRET
`)
assert.Error(t, err)
}
func TestToPodWithEnv(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
redis:
image: "redis:alpine"
environment:
- RACK_ENV=development
- SHOW=true
`)
expectedEnv := []apiv1.EnvVar{
{
Name: "RACK_ENV",
Value: "development",
},
{
Name: "SHOW",
Value: "true",
},
}
assert.Equal(t, expectedEnv, podTemplate.Spec.Containers[0].Env)
}
func TestToPodWithVolume(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("on windows, source path validation is broken (and actually, source validation for windows workload is broken too). Skip it for now, as we don't support it yet")
return
}
podTemplate := podTemplate(t, `
version: "3"
services:
nginx:
image: nginx
volumes:
- /ignore:/ignore
- /opt/data:/var/lib/mysql:ro
`)
assert.Len(t, podTemplate.Spec.Volumes, 2)
assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 2)
}
/* FIXME
func TestToPodWithRelativeVolumes(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("on windows, source path validation is broken (and actually, source validation for windows workload is broken too). Skip it for now, as we don't support it yet")
return
}
_, err := podTemplateWithError(`
version: "3"
services:
nginx:
image: nginx
volumes:
- ./fail:/ignore
`)
assert.Error(t, err)
}
*/
func TestToPodWithHealthCheck(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
nginx:
image: nginx
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 90s
timeout: 10s
retries: 3
`)
expectedLivenessProbe := &apiv1.Probe{
TimeoutSeconds: 10,
PeriodSeconds: 90,
FailureThreshold: 3,
Handler: apiv1.Handler{
Exec: &apiv1.ExecAction{
Command: []string{"curl", "-f", "http://localhost"},
},
},
}
assert.Equal(t, expectedLivenessProbe, podTemplate.Spec.Containers[0].LivenessProbe)
}
func TestToPodWithShellHealthCheck(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
nginx:
image: nginx
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost"]
`)
expectedLivenessProbe := &apiv1.Probe{
TimeoutSeconds: 1,
PeriodSeconds: 1,
FailureThreshold: 3,
Handler: apiv1.Handler{
Exec: &apiv1.ExecAction{
Command: []string{"sh", "-c", "curl -f http://localhost"},
},
},
}
assert.Equal(t, expectedLivenessProbe, podTemplate.Spec.Containers[0].LivenessProbe)
}
/* FIXME
func TestToPodWithTargetlessExternalSecret(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
nginx:
image: nginx
secrets:
- my_secret
`)
expectedVolume := apiv1.Volume{
Name: "secret-0",
VolumeSource: apiv1.VolumeSource{
Secret: &apiv1.SecretVolumeSource{
SecretName: "my_secret",
Items: []apiv1.KeyToPath{
{
Key: "file", // TODO: This is the key we assume external secrets use
Path: "secret-0",
},
},
},
},
}
expectedMount := apiv1.VolumeMount{
Name: "secret-0",
ReadOnly: true,
MountPath: "/run/secrets/my_secret",
SubPath: "secret-0",
}
assert.Len(t, podTemplate.Spec.Volumes, 1)
assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0])
assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
}
*/
/* FIXME
func TestToPodWithExternalSecret(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
nginx:
image: nginx
secrets:
- source: my_secret
target: nginx_secret
`)
expectedVolume := apiv1.Volume{
Name: "secret-0",
VolumeSource: apiv1.VolumeSource{
Secret: &apiv1.SecretVolumeSource{
SecretName: "my_secret",
Items: []apiv1.KeyToPath{
{
Key: "file", // TODO: This is the key we assume external secrets use
Path: "secret-0",
},
},
},
},
}
expectedMount := apiv1.VolumeMount{
Name: "secret-0",
ReadOnly: true,
MountPath: "/run/secrets/nginx_secret",
SubPath: "secret-0",
}
assert.Len(t, podTemplate.Spec.Volumes, 1)
assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0])
assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
}
*/
/* FIXME
func TestToPodWithFileBasedSecret(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
nginx:
image: nginx
secrets:
- source: my_secret
secrets:
my_secret:
file: ./secret.txt
`)
expectedVolume := apiv1.Volume{
Name: "secret-0",
VolumeSource: apiv1.VolumeSource{
Secret: &apiv1.SecretVolumeSource{
SecretName: "my_secret",
Items: []apiv1.KeyToPath{
{
Key: "secret.txt",
Path: "secret-0",
},
},
},
},
}
expectedMount := apiv1.VolumeMount{
Name: "secret-0",
ReadOnly: true,
MountPath: "/run/secrets/my_secret",
SubPath: "secret-0",
}
assert.Len(t, podTemplate.Spec.Volumes, 1)
assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0])
assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
}
*/
/* FIXME
func TestToPodWithTwoFileBasedSecrets(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
nginx:
image: nginx
secrets:
- source: my_secret1
- source: my_secret2
target: secret2
secrets:
my_secret1:
file: ./secret1.txt
my_secret2:
file: ./secret2.txt
`)
expectedVolumes := []apiv1.Volume{
{
Name: "secret-0",
VolumeSource: apiv1.VolumeSource{
Secret: &apiv1.SecretVolumeSource{
SecretName: "my_secret1",
Items: []apiv1.KeyToPath{
{
Key: "secret1.txt",
Path: "secret-0",
},
},
},
},
},
{
Name: "secret-1",
VolumeSource: apiv1.VolumeSource{
Secret: &apiv1.SecretVolumeSource{
SecretName: "my_secret2",
Items: []apiv1.KeyToPath{
{
Key: "secret2.txt",
Path: "secret-1",
},
},
},
},
},
}
expectedMounts := []apiv1.VolumeMount{
{
Name: "secret-0",
ReadOnly: true,
MountPath: "/run/secrets/my_secret1",
SubPath: "secret-0",
},
{
Name: "secret-1",
ReadOnly: true,
MountPath: "/run/secrets/secret2",
SubPath: "secret-1",
},
}
assert.Equal(t, expectedVolumes, podTemplate.Spec.Volumes)
assert.Equal(t, expectedMounts, podTemplate.Spec.Containers[0].VolumeMounts)
}
*/
func TestToPodWithTerminationGracePeriod(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
redis:
image: "redis:alpine"
stop_grace_period: 100s
`)
expected := int64(100)
assert.Equal(t, &expected, podTemplate.Spec.TerminationGracePeriodSeconds)
}
func TestToPodWithTmpfs(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
redis:
image: "redis:alpine"
tmpfs:
- /tmp
`)
expectedVolume := apiv1.Volume{
Name: "tmp-0",
VolumeSource: apiv1.VolumeSource{
EmptyDir: &apiv1.EmptyDirVolumeSource{
Medium: "Memory",
},
},
}
expectedMount := apiv1.VolumeMount{
Name: "tmp-0",
MountPath: "/tmp",
}
assert.Len(t, podTemplate.Spec.Volumes, 1)
assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0])
assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
}
func TestToPodWithNumericalUser(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
redis:
image: "redis:alpine"
user: "1000"
`)
userID := int64(1000)
expectedSecurityContext := &apiv1.SecurityContext{
RunAsUser: &userID,
}
assert.Equal(t, expectedSecurityContext, podTemplate.Spec.Containers[0].SecurityContext)
}
func TestToPodWithGitVolume(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
redis:
image: "redis:alpine"
volumes:
- source: "git@github.com:moby/moby.git"
target: /sources
type: git
`)
expectedVolume := apiv1.Volume{
Name: "mount-0",
VolumeSource: apiv1.VolumeSource{
GitRepo: &apiv1.GitRepoVolumeSource{
Repository: "git@github.com:moby/moby.git",
},
},
}
expectedMount := apiv1.VolumeMount{
Name: "mount-0",
ReadOnly: false,
MountPath: "/sources",
}
assert.Len(t, podTemplate.Spec.Volumes, 1)
assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0])
assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
}
/* FIXME
func TestToPodWithFileBasedConfig(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
redis:
image: "redis:alpine"
configs:
- source: my_config
target: /usr/share/nginx/html/index.html
uid: "103"
gid: "103"
mode: 0440
configs:
my_config:
file: ./file.html
`)
mode := int32(0440)
expectedVolume := apiv1.Volume{
Name: "config-0",
VolumeSource: apiv1.VolumeSource{
ConfigMap: &apiv1.ConfigMapVolumeSource{
LocalObjectReference: apiv1.LocalObjectReference{
Name: "my_config",
},
Items: []apiv1.KeyToPath{
{
Key: "file.html",
Path: "config-0",
Mode: &mode,
},
},
},
},
}
expectedMount := apiv1.VolumeMount{
Name: "config-0",
ReadOnly: true,
MountPath: "/usr/share/nginx/html/index.html",
SubPath: "config-0",
}
assert.Len(t, podTemplate.Spec.Volumes, 1)
assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0])
assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
}
*/
/* FIXME
func TestToPodWithTargetlessFileBasedConfig(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
redis:
image: "redis:alpine"
configs:
- my_config
configs:
my_config:
file: ./file.html
`)
expectedVolume := apiv1.Volume{
Name: "config-0",
VolumeSource: apiv1.VolumeSource{
ConfigMap: &apiv1.ConfigMapVolumeSource{
LocalObjectReference: apiv1.LocalObjectReference{
Name: "myconfig",
},
Items: []apiv1.KeyToPath{
{
Key: "file.html",
Path: "config-0",
},
},
},
},
}
expectedMount := apiv1.VolumeMount{
Name: "config-0",
ReadOnly: true,
MountPath: "/myconfig",
SubPath: "config-0",
}
assert.Len(t, podTemplate.Spec.Volumes, 1)
assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0])
assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
}
*/
func TestToPodWithExternalConfig(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
redis:
image: "redis:alpine"
configs:
- source: my_config
target: /usr/share/nginx/html/index.html
uid: "103"
gid: "103"
mode: 0440
configs:
my_config:
external: true
`)
mode := int32(0440)
expectedVolume := apiv1.Volume{
Name: "config-0",
VolumeSource: apiv1.VolumeSource{
ConfigMap: &apiv1.ConfigMapVolumeSource{
LocalObjectReference: apiv1.LocalObjectReference{
Name: "my_config",
},
Items: []apiv1.KeyToPath{
{
Key: "file", // TODO: This is the key we assume external config use
Path: "config-0",
Mode: &mode,
},
},
},
},
}
expectedMount := apiv1.VolumeMount{
Name: "config-0",
ReadOnly: true,
MountPath: "/usr/share/nginx/html/index.html",
SubPath: "config-0",
}
assert.Len(t, podTemplate.Spec.Volumes, 1)
assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1)
assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0])
assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0])
}
/* FIXME
func TestToPodWithTwoConfigsSameMountPoint(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
nginx:
image: nginx
configs:
- source: first
target: /data/first.json
mode: "0440"
- source: second
target: /data/second.json
mode: "0550"
configs:
first:
file: ./file1
secondv:
file: ./file2
`)
mode0440 := int32(0440)
mode0550 := int32(0550)
expectedVolumes := []apiv1.Volume{
{
Name: "config-0",
VolumeSource: apiv1.VolumeSource{
ConfigMap: &apiv1.ConfigMapVolumeSource{
LocalObjectReference: apiv1.LocalObjectReference{
Name: "first",
},
Items: []apiv1.KeyToPath{
{
Key: "file1",
Path: "config-0",
Mode: &mode0440,
},
},
},
},
},
{
Name: "config-1",
VolumeSource: apiv1.VolumeSource{
ConfigMap: &apiv1.ConfigMapVolumeSource{
LocalObjectReference: apiv1.LocalObjectReference{
Name: "second",
},
Items: []apiv1.KeyToPath{
{
Key: "file2",
Path: "config-1",
Mode: &mode0550,
},
},
},
},
},
}
expectedMounts := []apiv1.VolumeMount{
{
Name: "config-0",
ReadOnly: true,
MountPath: "/data/first.json",
SubPath: "config-0",
},
{
Name: "config-1",
ReadOnly: true,
MountPath: "/data/second.json",
SubPath: "config-1",
},
}
assert.Equal(t, expectedVolumes, podTemplate.Spec.Volumes)
assert.Equal(t, expectedMounts, podTemplate.Spec.Containers[0].VolumeMounts)
}
*/
func TestToPodWithTwoExternalConfigsSameMountPoint(t *testing.T) {
podTemplate := podTemplate(t, `
version: "3"
services:
nginx:
image: nginx
configs:
- source: first
target: /data/first.json
- source: second
target: /data/second.json
configs:
first:
file: ./file1
second:
file: ./file2
`)
expectedVolumes := []apiv1.Volume{
{
Name: "config-0",
VolumeSource: apiv1.VolumeSource{
ConfigMap: &apiv1.ConfigMapVolumeSource{
LocalObjectReference: apiv1.LocalObjectReference{
Name: "first",
},
Items: []apiv1.KeyToPath{
{
Key: "file",
Path: "config-0",
},
},
},
},
},
{
Name: "config-1",
VolumeSource: apiv1.VolumeSource{
ConfigMap: &apiv1.ConfigMapVolumeSource{
LocalObjectReference: apiv1.LocalObjectReference{
Name: "second",
},
Items: []apiv1.KeyToPath{
{
Key: "file",
Path: "config-1",
},
},
},
},
},
}
expectedMounts := []apiv1.VolumeMount{
{
Name: "config-0",
ReadOnly: true,
MountPath: "/data/first.json",
SubPath: "config-0",
},
{
Name: "config-1",
ReadOnly: true,
MountPath: "/data/second.json",
SubPath: "config-1",
},
}
assert.Equal(t, expectedVolumes, podTemplate.Spec.Volumes)
assert.Equal(t, expectedMounts, podTemplate.Spec.Containers[0].VolumeMounts)
}
/* FIXME
func TestToPodWithPullSecret(t *testing.T) {
podTemplateWithSecret := podTemplate(t, `
version: "3"
services:
nginx:
image: nginx
x-kubernetes.pull-secret: test-pull-secret
`)
assert.Equal(t, 1, len(podTemplateWithSecret.Spec.ImagePullSecrets))
assert.Equal(t, "test-pull-secret", podTemplateWithSecret.Spec.ImagePullSecrets[0].Name)
podTemplateNoSecret := podTemplate(t, `
version: "3"
services:
nginx:
image: nginx
`)
assert.Nil(t, podTemplateNoSecret.Spec.ImagePullSecrets)
}
*/
/* FIXME
func TestToPodWithPullPolicy(t *testing.T) {
cases := []struct {
name string
stack string
expectedPolicy apiv1.PullPolicy
expectedError string
}{
{
name: "specific tag",
stack: `
version: "3"
services:
nginx:
image: nginx:specific
`,
expectedPolicy: apiv1.PullIfNotPresent,
},
{
name: "latest tag",
stack: `
version: "3"
services:
nginx:
image: nginx:latest
`,
expectedPolicy: apiv1.PullAlways,
},
{
name: "explicit policy",
stack: `
version: "3"
services:
nginx:
image: nginx:specific
x-kubernetes.pull-policy: Never
`,
expectedPolicy: apiv1.PullNever,
},
{
name: "invalid policy",
stack: `
version: "3"
services:
nginx:
image: nginx:specific
x-kubernetes.pull-policy: Invalid
`,
expectedError: `invalid pull policy "Invalid", must be "Always", "IfNotPresent" or "Never"`,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
pod, err := podTemplateWithError(c.stack)
if c.expectedError != "" {
assert.EqualError(t, err, c.expectedError)
} else {
assert.NoError(t, err)
assert.Equal(t, pod.Spec.Containers[0].ImagePullPolicy, c.expectedPolicy)
}
})
}
}
*/