mirror of https://github.com/knative/client.git
359 lines
9.5 KiB
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()
|
|
}
|