client/pkg/kn/flags/podspec_test.go

359 lines
9.5 KiB
Go

// Copyright 2020 The Knative 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 flags
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
"knative.dev/client/lib/test"
"github.com/spf13/cobra"
"gotest.tools/v3/assert"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
"knative.dev/client/pkg/util"
"knative.dev/pkg/ptr"
)
func TestPodSpecFlags(t *testing.T) {
args := []string{"--image", "repo/user/imageID:tag", "--env", "b=c"}
wantedPod := &PodSpecFlags{
Image: "repo/user/imageID:tag",
Env: []string{"b=c"},
EnvFrom: []string{},
EnvValueFrom: []string{},
Mount: []string{},
Volume: []string{},
Arg: []string{},
Command: []string{},
}
flags := &PodSpecFlags{}
testCmd := &cobra.Command{
Use: "test",
Run: func(cmd *cobra.Command, args []string) {
assert.DeepEqual(t, wantedPod, flags)
},
}
testCmd.SetArgs(args)
flags.AddFlags(testCmd.Flags())
flags.AddCreateFlags(testCmd.Flags())
testCmd.Execute()
}
func TestUniqueStringArg(t *testing.T) {
var a uniqueStringArg
a.Set("test")
assert.Equal(t, "test", a.String())
assert.Equal(t, "string", a.Type())
}
func TestPodSpecResolve(t *testing.T) {
inputArgs := []string{"--image", "repo/user/imageID:tag", "--env", "b=c",
"--port", "8080", "--limit", "cpu=1000m", "--limit", "memory=1024Mi",
"--cmd", "/app/start", "--arg", "myArg1", "--service-account", "foo-bar-account",
"--mount", "/mount/path=volume-name", "--volume", "volume-name=cm:config-map-name",
"--env-from", "config-map:config-map-name", "--user", "1001", "--pull-policy", "always"}
expectedPodSpec := corev1.PodSpec{
Containers: []corev1.Container{
{
Image: "repo/user/imageID:tag",
ImagePullPolicy: "Always",
Command: []string{"/app/start"},
Args: []string{"myArg1"},
Ports: []corev1.ContainerPort{
{
ContainerPort: 8080,
},
},
Env: []corev1.EnvVar{{Name: "b", Value: "c"}},
EnvFrom: []corev1.EnvFromSource{
{
ConfigMapRef: &corev1.ConfigMapEnvSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: "config-map-name",
},
},
},
},
Resources: corev1.ResourceRequirements{
Limits: corev1.ResourceList{
corev1.ResourceCPU: parseQuantity("1000m"),
corev1.ResourceMemory: parseQuantity("1024Mi"),
},
Requests: corev1.ResourceList{},
},
VolumeMounts: []corev1.VolumeMount{{Name: "volume-name", ReadOnly: true, MountPath: "/mount/path"}},
SecurityContext: &corev1.SecurityContext{
RunAsUser: ptr.Int64(int64(1001)),
},
},
},
ServiceAccountName: "foo-bar-account",
Volumes: []corev1.Volume{
{
Name: "volume-name",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: "config-map-name",
},
},
},
},
},
}
flags := &PodSpecFlags{}
testCmd := &cobra.Command{
Use: "test",
Run: func(cmd *cobra.Command, args []string) {
podSpec := &corev1.PodSpec{Containers: []corev1.Container{{}}}
err := flags.ResolvePodSpec(podSpec, cmd.Flags(), inputArgs)
assert.NilError(t, err, "PodSpec cannot be resolved.")
assert.DeepEqual(t, expectedPodSpec, *podSpec)
},
}
testCmd.SetArgs(inputArgs)
flags.AddFlags(testCmd.Flags())
flags.AddUpdateFlags(testCmd.Flags())
testCmd.Execute()
}
func TestPodSpecResolveContainers(t *testing.T) {
rawInput := `
containers:
- image: foo:bar
name: foo
env:
- name: a
value: b
resources: {}
- image: bar:bar
name: bar
resources: {}`
rawInvalidFormatInput := `
containers:
image: foo:bar
name: foo
resources: {}`
expectedPodSpec := corev1.PodSpec{
Containers: []corev1.Container{
{
Image: "repo/user/imageID:tag",
Resources: corev1.ResourceRequirements{
Limits: corev1.ResourceList{},
Requests: corev1.ResourceList{},
},
},
{
Name: "foo",
Image: "foo:bar",
Env: []corev1.EnvVar{{Name: "a", Value: "b"}},
},
{
Name: "bar",
Image: "bar:bar",
},
},
}
testCases := []struct {
name string
rawInput string
mockInput func(data string) string
expectedPodSpec corev1.PodSpec
expectedError error
}{
{
"Input:stdin",
rawInput,
func(data string) string {
return mockDataToStdin(t, data)
},
expectedPodSpec,
nil,
},
{
"Input:file",
rawInput,
func(data string) string {
tempDir, err := ioutil.TempDir("", "kn-file")
assert.NilError(t, err)
fileName := filepath.Join(tempDir, "container.yaml")
ioutil.WriteFile(fileName, []byte(data), test.FileModeReadWrite)
return fileName
},
expectedPodSpec,
nil,
},
{
"Input:error",
rawInput,
func(data string) string {
return "-"
},
corev1.PodSpec{Containers: []corev1.Container{{}}},
errors.New("EOF"),
},
{
"Input:invalidFormat",
rawInvalidFormatInput,
func(data string) string {
return mockDataToStdin(t, data)
},
corev1.PodSpec{Containers: []corev1.Container{{}}},
errors.New("cannot unmarshal object into Go struct field PodSpec.containers"),
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
origStdin := os.Stdin
fileName := tc.mockInput(tc.rawInput)
if fileName == "-" {
defer func() { os.Stdin = origStdin }()
} else {
defer os.RemoveAll(fileName)
}
inputArgs := []string{"--image", "repo/user/imageID:tag", "--containers", fileName}
flags := &PodSpecFlags{}
testCmd := &cobra.Command{
Use: "test",
Run: func(cmd *cobra.Command, args []string) {
podSpec := &corev1.PodSpec{Containers: []corev1.Container{{}}}
err := flags.ResolvePodSpec(podSpec, cmd.Flags(), inputArgs)
if tc.expectedError == nil {
assert.NilError(t, err, "PodSpec cannot be resolved.")
assert.DeepEqual(t, tc.expectedPodSpec, *podSpec)
} else {
assert.ErrorContains(t, err, tc.expectedError.Error())
}
},
}
testCmd.SetArgs(inputArgs)
flags.AddFlags(testCmd.Flags())
flags.AddUpdateFlags(testCmd.Flags())
testCmd.Execute()
})
}
}
func mockDataToStdin(t *testing.T, data string) string {
stdinReader, stdinWriter, err := os.Pipe()
assert.NilError(t, err)
_, err = stdinWriter.Write([]byte(data))
assert.NilError(t, err)
stdinWriter.Close()
os.Stdin = stdinReader
return "-"
}
func TestPodSpecResolveReturnError(t *testing.T) {
outBuf := bytes.Buffer{}
flags := &PodSpecFlags{}
inputArgs := []string{"--mount", "123456"}
testCmd := &cobra.Command{
Use: "test",
Run: func(cmd *cobra.Command, args []string) {
podSpec := &corev1.PodSpec{Containers: []corev1.Container{{}}}
err := flags.ResolvePodSpec(podSpec, cmd.Flags(), inputArgs)
fmt.Fprint(cmd.OutOrStdout(), "Return error: ", err)
},
}
testCmd.SetOut(&outBuf)
testCmd.SetArgs(inputArgs)
flags.AddFlags(testCmd.Flags())
flags.AddCreateFlags(testCmd.Flags())
testCmd.Execute()
out := outBuf.String()
assert.Assert(t, util.ContainsAll(out, "Invalid", "mount"))
}
func TestPodSpecResolveReturnErrorPullPolicy(t *testing.T) {
outBuf := bytes.Buffer{}
flags := &PodSpecFlags{}
inputArgs := []string{"--pull-policy", "invalidPolicy"}
testCmd := &cobra.Command{
Use: "test",
Run: func(cmd *cobra.Command, args []string) {
podSpec := &corev1.PodSpec{Containers: []corev1.Container{{}}}
err := flags.ResolvePodSpec(podSpec, cmd.Flags(), inputArgs)
fmt.Fprint(cmd.OutOrStdout(), "Return error: ", err)
},
}
testCmd.SetOut(&outBuf)
testCmd.SetArgs(inputArgs)
flags.AddFlags(testCmd.Flags())
flags.AddCreateFlags(testCmd.Flags())
testCmd.Execute()
out := outBuf.String()
assert.Assert(t, util.ContainsAll(out, "invalid --pull-policy", "Always | Never | IfNotPresent"))
}
func TestPodSpecResolveWithEnvFile(t *testing.T) {
file, err := ioutil.TempFile("", "envfile.env")
assert.NilError(t, err)
file.WriteString("svcOwner=James\nsvcAuthor=James")
defer os.Remove(file.Name())
inputArgs := []string{"--image", "repo/user/imageID:tag", "--env", "svcOwner=David",
"--env-file", file.Name(), "--port", "8080", "--cmd", "/app/start", "--arg", "myArg1"}
expectedPodSpec := corev1.PodSpec{
Containers: []corev1.Container{
{
Image: "repo/user/imageID:tag",
Command: []string{"/app/start"},
Args: []string{"myArg1"},
Ports: []corev1.ContainerPort{
{
ContainerPort: 8080,
},
},
Env: []corev1.EnvVar{{Name: "svcOwner", Value: "James"}, {Name: "svcAuthor", Value: "James"}},
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{},
Requests: v1.ResourceList{},
},
},
},
}
flags := &PodSpecFlags{}
testCmd := &cobra.Command{
Use: "test",
Run: func(cmd *cobra.Command, args []string) {
podSpec := &corev1.PodSpec{Containers: []corev1.Container{{}}}
err := flags.ResolvePodSpec(podSpec, cmd.Flags(), inputArgs)
assert.NilError(t, err, "PodSpec cannot be resolved.")
assert.DeepEqual(t, expectedPodSpec, *podSpec)
},
}
testCmd.SetArgs(inputArgs)
flags.AddFlags(testCmd.Flags())
flags.AddUpdateFlags(testCmd.Flags())
testCmd.Execute()
}