client/pkg/commands/service/export_test.go

290 lines
11 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 service
import (
"context"
"encoding/json"
"testing"
"gotest.tools/v3/assert"
v1 "k8s.io/api/core/v1"
clientv1alpha1 "knative.dev/client/pkg/apis/client/v1alpha1"
"knative.dev/client/pkg/serving"
knclient "knative.dev/client/pkg/serving/v1"
"knative.dev/client/pkg/util/mock"
"knative.dev/client/pkg/util/test"
"knative.dev/pkg/ptr"
apiserving "knative.dev/serving/pkg/apis/serving"
servingv1 "knative.dev/serving/pkg/apis/serving/v1"
servingtest "knative.dev/serving/pkg/testing/v1"
"sigs.k8s.io/yaml"
)
var revisionSpec = servingv1.RevisionSpec{
PodSpec: v1.PodSpec{
Containers: []v1.Container{{
Image: "busybox",
}},
EnableServiceLinks: ptr.Bool(false),
},
TimeoutSeconds: ptr.Int64(300),
}
type testCase struct {
name string
latestSvc *servingv1.Service
expectedSvcList *servingv1.ServiceList
revisionList *servingv1.RevisionList
expectedKNExport *clientv1alpha1.Export
}
func TestServiceExportError(t *testing.T) {
tc := &testCase{latestSvc: getService("foo")}
_, err := executeServiceExportCommand(t, tc, "export", tc.latestSvc.ObjectMeta.Name)
assert.Error(t, err, "'kn service export' requires output format")
}
func TestServiceExport(t *testing.T) {
for _, tc := range []testCase{
{latestSvc: test.BuildServiceWithOptions("foo", servingtest.WithConfigSpec(buildConfiguration()))},
{latestSvc: test.BuildServiceWithOptions("foo", servingtest.WithConfigSpec(buildConfiguration()), servingtest.WithEnv(v1.EnvVar{Name: "a", Value: "mouse"}))},
{latestSvc: test.BuildServiceWithOptions("foo", servingtest.WithConfigSpec(buildConfiguration()), test.WithRevisionAnnotations(map[string]string{"client.knative.dev/user-image": "busybox:v2"}))},
{latestSvc: test.BuildServiceWithOptions("foo", servingtest.WithConfigSpec(buildConfiguration()), servingtest.WithServiceLabel("a", "mouse"), servingtest.WithServiceAnnotation("a", "mouse"))},
{latestSvc: test.BuildServiceWithOptions("foo", servingtest.WithConfigSpec(buildConfiguration()), servingtest.WithVolume("secretName", "/mountpath", volumeSource("secretName")))},
} {
exportServiceTestForReplay(t, &tc)
tc.expectedKNExport = test.BuildKNExportWithOptions()
exportServiceTest(t, &tc, true)
//test default
exportServiceTest(t, &tc, false)
}
}
func exportServiceTestForReplay(t *testing.T, tc *testCase) {
output, err := executeServiceExportCommand(t, tc, "export", tc.latestSvc.ObjectMeta.Name, "--mode", "replay", "-o", "yaml")
assert.NilError(t, err)
actSvc := servingv1.Service{}
err = yaml.Unmarshal([]byte(output), &actSvc)
assert.NilError(t, err)
assert.DeepEqual(t, tc.latestSvc, &actSvc)
}
func exportServiceTest(t *testing.T, tc *testCase, addMode bool) {
args := []string{"export", tc.latestSvc.ObjectMeta.Name, "-o", "json"}
if addMode {
args = append(args, []string{"--mode", "export"}...)
}
output, err := executeServiceExportCommand(t, tc, args...)
assert.NilError(t, err)
tc.expectedKNExport.Spec.Service = *tc.latestSvc
actKNExport := &clientv1alpha1.Export{}
err = json.Unmarshal([]byte(output), actKNExport)
assert.NilError(t, err)
assert.DeepEqual(t, tc.expectedKNExport, actKNExport)
}
func TestServiceExportwithMultipleRevisions(t *testing.T) {
for _, tc := range []testCase{{
name: "test 2 revisions with traffic split",
latestSvc: test.BuildServiceWithOptions(
"foo", servingtest.WithConfigSpec(buildConfiguration()),
test.WithRevisionAnnotations(map[string]string{"client.knative.dev/user-image": "busybox:v2"}),
test.WithTrafficSpec([]string{"foo-rev-1", "latest"}, []int{50, 50}, []string{"", ""}),
),
expectedSvcList: test.BuildServiceListWithOptions(
test.WithService(test.BuildServiceWithOptions("foo", servingtest.WithConfigSpec(buildConfiguration()),
test.WithRevisionAnnotations(map[string]string{"client.knative.dev/user-image": "busybox:v1"}),
servingtest.WithBYORevisionName("foo-rev-1"),
)),
test.WithService(test.BuildServiceWithOptions("foo", servingtest.WithConfigSpec(buildConfiguration()),
test.WithRevisionAnnotations(map[string]string{"client.knative.dev/user-image": "busybox:v2"}),
test.WithTrafficSpec([]string{"foo-rev-1", "latest"}, []int{50, 50}, []string{"", ""}),
)),
),
revisionList: test.BuildRevisionListWithOptions(
test.WithRevision(*(test.BuildRevision("foo-rev-1",
servingtest.WithRevisionLabel(apiserving.ServiceLabelKey, "foo"),
servingtest.WithRevisionLabel(apiserving.ConfigurationGenerationLabelKey, "1"),
servingtest.WithRevisionAnn("client.knative.dev/user-image", "busybox:v1"),
servingtest.WithRevisionAnn("serving.knative.dev/lastPinned", "1111132"),
servingtest.WithRevisionAnn(serving.UpdateTimestampAnnotationKey, "now"),
))),
test.WithRevision(*(test.BuildRevision("foo-rev-2",
servingtest.WithRevisionLabel(apiserving.ServiceLabelKey, "foo"),
servingtest.WithRevisionLabel(apiserving.ConfigurationGenerationLabelKey, "1"),
servingtest.WithRevisionAnn("client.knative.dev/user-image", "busybox:v2"),
servingtest.WithRevisionAnn(serving.UpdateTimestampAnnotationKey, "now"),
))),
),
expectedKNExport: test.BuildKNExportWithOptions(
test.WithKNRevision(*(test.BuildRevision("foo-rev-1",
servingtest.WithRevisionLabel(apiserving.ServiceLabelKey, "foo"),
servingtest.WithRevisionLabel(apiserving.ConfigurationGenerationLabelKey, "1"),
servingtest.WithRevisionAnn("client.knative.dev/user-image", "busybox:v1"),
))),
),
}, {
name: "test 2 revisions no traffic split",
latestSvc: test.BuildServiceWithOptions(
"foo", servingtest.WithConfigSpec(buildConfiguration()),
test.WithTrafficSpec([]string{"latest"}, []int{100}, []string{""}),
),
expectedSvcList: test.BuildServiceListWithOptions(
test.WithService(test.BuildServiceWithOptions(
"foo", servingtest.WithConfigSpec(buildConfiguration()),
test.WithTrafficSpec([]string{"latest"}, []int{100}, []string{""}),
)),
),
revisionList: test.BuildRevisionListWithOptions(
test.WithRevision(*(test.BuildRevision("foo-rev-1",
servingtest.WithRevisionLabel(apiserving.ConfigurationGenerationLabelKey, "1"),
servingtest.WithRevisionLabel(apiserving.ServiceLabelKey, "foo"),
))),
test.WithRevision(*(test.BuildRevision("foo-rev-2",
servingtest.WithRevisionLabel(apiserving.ConfigurationGenerationLabelKey, "2"),
servingtest.WithRevisionLabel(apiserving.ServiceLabelKey, "foo"),
))),
),
expectedKNExport: test.BuildKNExportWithOptions(),
}, {
name: "test 3 active revisions with traffic split with no latest revision",
latestSvc: test.BuildServiceWithOptions(
"foo", servingtest.WithConfigSpec(buildConfiguration()),
servingtest.WithEnv(v1.EnvVar{Name: "a", Value: "mouse"}),
servingtest.WithBYORevisionName("foo-rev-3"),
test.WithTrafficSpec([]string{"foo-rev-1", "foo-rev-2", "foo-rev-3"}, []int{25, 50, 25}, []string{"", "", ""}),
),
expectedSvcList: test.BuildServiceListWithOptions(
test.WithService(
test.BuildServiceWithOptions(
"foo", servingtest.WithConfigSpec(buildConfiguration()),
servingtest.WithEnv(v1.EnvVar{Name: "a", Value: "cat"}),
servingtest.WithBYORevisionName("foo-rev-1"),
),
),
test.WithService(
test.BuildServiceWithOptions(
"foo", servingtest.WithConfigSpec(buildConfiguration()),
servingtest.WithEnv(v1.EnvVar{Name: "a", Value: "dog"}),
servingtest.WithBYORevisionName("foo-rev-2"),
),
),
test.WithService(
test.BuildServiceWithOptions(
"foo", servingtest.WithConfigSpec(buildConfiguration()),
servingtest.WithEnv(v1.EnvVar{Name: "a", Value: "mouse"}),
servingtest.WithBYORevisionName("foo-rev-3"),
test.WithTrafficSpec([]string{"foo-rev-1", "foo-rev-2", "foo-rev-3"}, []int{25, 50, 25}, []string{"", "", ""}),
),
),
),
revisionList: test.BuildRevisionListWithOptions(
test.WithRevision(*(test.BuildRevision("foo-rev-1",
servingtest.WithRevisionLabel(apiserving.ConfigurationGenerationLabelKey, "1"),
servingtest.WithRevisionLabel(apiserving.ServiceLabelKey, "foo"),
test.WithRevisionEnv(v1.EnvVar{Name: "a", Value: "cat"}),
))),
test.WithRevision(*(test.BuildRevision("foo-rev-2",
servingtest.WithRevisionLabel(apiserving.ConfigurationGenerationLabelKey, "2"),
servingtest.WithRevisionLabel(apiserving.ServiceLabelKey, "foo"),
test.WithRevisionEnv(v1.EnvVar{Name: "a", Value: "dog"}),
))),
test.WithRevision(*(test.BuildRevision("foo-rev-3",
servingtest.WithRevisionLabel(apiserving.ConfigurationGenerationLabelKey, "3"),
servingtest.WithRevisionLabel(apiserving.ServiceLabelKey, "foo"),
test.WithRevisionEnv(v1.EnvVar{Name: "a", Value: "mouse"}),
))),
),
expectedKNExport: test.BuildKNExportWithOptions(
test.WithKNRevision(*(test.BuildRevision("foo-rev-1",
servingtest.WithRevisionLabel(apiserving.ConfigurationGenerationLabelKey, "1"),
servingtest.WithRevisionLabel(apiserving.ServiceLabelKey, "foo"),
test.WithRevisionEnv(v1.EnvVar{Name: "a", Value: "cat"}),
))),
test.WithKNRevision(*(test.BuildRevision("foo-rev-2",
servingtest.WithRevisionLabel(apiserving.ConfigurationGenerationLabelKey, "2"),
servingtest.WithRevisionLabel(apiserving.ServiceLabelKey, "foo"),
test.WithRevisionEnv(v1.EnvVar{Name: "a", Value: "dog"}),
))),
),
}} {
t.Run(tc.name, func(t *testing.T) {
exportWithRevisionsforKubernetesTest(t, &tc)
exportWithRevisionsTest(t, &tc)
})
}
}
func exportWithRevisionsforKubernetesTest(t *testing.T, tc *testCase) {
output, err := executeServiceExportCommand(t, tc, "export", tc.latestSvc.ObjectMeta.Name, "--with-revisions", "--mode", "replay", "-o", "json")
assert.NilError(t, err)
actSvcList := servingv1.ServiceList{}
err = json.Unmarshal([]byte(output), &actSvcList)
assert.NilError(t, err)
assert.DeepEqual(t, tc.expectedSvcList, &actSvcList)
}
func exportWithRevisionsTest(t *testing.T, tc *testCase) {
output, err := executeServiceExportCommand(t, tc, "export", tc.latestSvc.ObjectMeta.Name, "--with-revisions", "--mode", "export", "-o", "json")
assert.NilError(t, err)
tc.expectedKNExport.Spec.Service = *tc.latestSvc
actKNExport := &clientv1alpha1.Export{}
err = json.Unmarshal([]byte(output), actKNExport)
assert.NilError(t, err)
assert.DeepEqual(t, tc.expectedKNExport, actKNExport)
}
func executeServiceExportCommand(t *testing.T, tc *testCase, options ...string) (string, error) {
client := knclient.NewMockKnServiceClient(t)
r := client.Recorder()
r.GetService(tc.latestSvc.ObjectMeta.Name, tc.latestSvc, nil)
r.ListRevisions(mock.Any(), tc.revisionList, nil)
return executeServiceCommand(client, options...)
}
func buildConfiguration() *servingv1.ConfigurationSpec {
c := &servingv1.Configuration{
Spec: servingv1.ConfigurationSpec{
Template: servingv1.RevisionTemplateSpec{
Spec: *revisionSpec.DeepCopy(),
},
},
}
c.SetDefaults(context.Background())
return &c.Spec
}
func volumeSource(secretName string) v1.VolumeSource {
return v1.VolumeSource{
Secret: &v1.SecretVolumeSource{
SecretName: secretName,
},
}
}