mirror of https://github.com/knative/client.git
refactor kn service export to export revisions (#819)
* refactor kn service export to export revisions * refactor kn service export to export revisions * refactor kn service export to export revisions * refactor kn service export to export revisions * kn service export refactor * kn service export refactor * kn service export refactor * kn service export refactor * review changes for #819
This commit is contained in:
parent
c41e9fd923
commit
644ecb68e5
|
|
@ -14,10 +14,14 @@ kn service export NAME [flags]
|
|||
|
||||
```
|
||||
|
||||
# Export a service in yaml format
|
||||
# Export a service in YAML format
|
||||
kn service export foo -n bar -o yaml
|
||||
# Export a service in json format
|
||||
# Export a service in JSON format
|
||||
kn service export foo -n bar -o json
|
||||
# Export a service with revisions
|
||||
kn service export foo --with-revisions --mode=resources -n bar -o json
|
||||
# Export services in kubectl friendly format, as a list kind, one service item for each revision
|
||||
kn service export foo --with-revisions --mode=kubernetes -n bar -o json
|
||||
```
|
||||
|
||||
### Options
|
||||
|
|
@ -25,6 +29,7 @@ kn service export NAME [flags]
|
|||
```
|
||||
--allow-missing-template-keys If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats. (default true)
|
||||
-h, --help help for export
|
||||
--mode string Format for exporting all routed revisions. One of kubernetes|resources (experimental)
|
||||
-n, --namespace string Specify the namespace to operate in.
|
||||
-o, --output string Output format. One of: json|yaml|name|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-file.
|
||||
--template string Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/cli-runtime/pkg/printers"
|
||||
|
||||
"knative.dev/client/pkg/kn/commands"
|
||||
clientservingv1 "knative.dev/client/pkg/serving/v1"
|
||||
|
|
@ -32,6 +33,15 @@ import (
|
|||
servingv1 "knative.dev/serving/pkg/apis/serving/v1"
|
||||
)
|
||||
|
||||
var IGNORED_SERVICE_ANNOTATIONS = []string{
|
||||
"serving.knative.dev/creator",
|
||||
"serving.knative.dev/lastModifier",
|
||||
"kubectl.kubernetes.io/last-applied-configuration",
|
||||
}
|
||||
var IGNORED_REVISION_ANNOTATIONS = []string{
|
||||
"serving.knative.dev/lastPinned",
|
||||
}
|
||||
|
||||
// NewServiceExportCommand returns a new command for exporting a service.
|
||||
func NewServiceExportCommand(p *commands.KnParams) *cobra.Command {
|
||||
|
||||
|
|
@ -42,10 +52,14 @@ func NewServiceExportCommand(p *commands.KnParams) *cobra.Command {
|
|||
Use: "export NAME",
|
||||
Short: "Export a service.",
|
||||
Example: `
|
||||
# Export a service in yaml format
|
||||
# Export a service in YAML format
|
||||
kn service export foo -n bar -o yaml
|
||||
# Export a service in json format
|
||||
kn service export foo -n bar -o json`,
|
||||
# Export a service in JSON format
|
||||
kn service export foo -n bar -o json
|
||||
# Export a service with revisions
|
||||
kn service export foo --with-revisions --mode=resources -n bar -o json
|
||||
# Export services in kubectl friendly format, as a list kind, one service item for each revision
|
||||
kn service export foo --with-revisions --mode=kubernetes -n bar -o json`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 {
|
||||
return errors.New("'kn service export' requires name of the service as single argument")
|
||||
|
|
@ -69,105 +83,138 @@ func NewServiceExportCommand(p *commands.KnParams) *cobra.Command {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
withRevisions, err := cmd.Flags().GetBool("with-revisions")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printer, err := machineReadablePrintFlags.ToPrinter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if withRevisions {
|
||||
if svcList, err := exportServiceWithActiveRevisions(service, client); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return printer.PrintObj(svcList, cmd.OutOrStdout())
|
||||
}
|
||||
}
|
||||
return printer.PrintObj(exportService(service), cmd.OutOrStdout())
|
||||
return exportService(cmd, service, client, printer)
|
||||
},
|
||||
}
|
||||
flags := command.Flags()
|
||||
commands.AddNamespaceFlags(flags, false)
|
||||
flags.Bool("with-revisions", false, "Export all routed revisions (experimental)")
|
||||
flags.String("mode", "", "Format for exporting all routed revisions. One of kubernetes|resources (experimental)")
|
||||
machineReadablePrintFlags.AddFlags(command)
|
||||
return command
|
||||
}
|
||||
|
||||
func exportService(latestSvc *servingv1.Service) *servingv1.Service {
|
||||
func exportService(cmd *cobra.Command, service *servingv1.Service, client clientservingv1.KnServingClient, printer printers.ResourcePrinter) error {
|
||||
withRevisions, err := cmd.Flags().GetBool("with-revisions")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !withRevisions {
|
||||
return printer.PrintObj(exportLatestService(service, false), cmd.OutOrStdout())
|
||||
}
|
||||
|
||||
mode, err := cmd.Flags().GetString("mode")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case "kubernetes":
|
||||
svcList, err := exportServiceListWithActiveRevisions(service, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printer.PrintObj(svcList, cmd.OutOrStdout())
|
||||
case "resources":
|
||||
latestSvc, revList, err := exportActiveRevisionList(service, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//print svc
|
||||
if err := printer.PrintObj(latestSvc, cmd.OutOrStdout()); err != nil {
|
||||
return err
|
||||
}
|
||||
// print revisionList if revisions exist
|
||||
if len(revList.Items) > 0 {
|
||||
return printer.PrintObj(revList, cmd.OutOrStdout())
|
||||
}
|
||||
default:
|
||||
return errors.New("'kn service export --with-revisions' requires a mode, please specify one of kubernetes|resources.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func exportLatestService(latestSvc *servingv1.Service, withRoutes bool) *servingv1.Service {
|
||||
exportedSvc := servingv1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: latestSvc.ObjectMeta.Name,
|
||||
Labels: latestSvc.ObjectMeta.Labels,
|
||||
Annotations: latestSvc.ObjectMeta.Annotations,
|
||||
},
|
||||
TypeMeta: latestSvc.TypeMeta,
|
||||
}
|
||||
|
||||
exportedSvc.Spec.Template = servingv1.RevisionTemplateSpec{
|
||||
Spec: latestSvc.Spec.ConfigurationSpec.Template.Spec,
|
||||
ObjectMeta: latestSvc.Spec.ConfigurationSpec.Template.ObjectMeta,
|
||||
Spec: latestSvc.Spec.Template.Spec,
|
||||
ObjectMeta: latestSvc.Spec.Template.ObjectMeta,
|
||||
}
|
||||
|
||||
if withRoutes {
|
||||
exportedSvc.Spec.RouteSpec = latestSvc.Spec.RouteSpec
|
||||
}
|
||||
|
||||
stripIgnoredAnnotationsFromService(&exportedSvc)
|
||||
|
||||
return &exportedSvc
|
||||
}
|
||||
|
||||
func constructServicefromRevision(latestSvc *servingv1.Service, revision servingv1.Revision) servingv1.Service {
|
||||
func exportRevision(revision *servingv1.Revision) servingv1.Revision {
|
||||
exportedRevision := servingv1.Revision{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: revision.ObjectMeta.Name,
|
||||
Labels: revision.ObjectMeta.Labels,
|
||||
Annotations: revision.ObjectMeta.Annotations,
|
||||
},
|
||||
TypeMeta: revision.TypeMeta,
|
||||
}
|
||||
|
||||
exportedRevision.Spec = revision.Spec
|
||||
stripIgnoredAnnotationsFromRevision(&exportedRevision)
|
||||
return exportedRevision
|
||||
}
|
||||
|
||||
func constructServiceFromRevision(latestSvc *servingv1.Service, revision *servingv1.Revision) servingv1.Service {
|
||||
exportedSvc := servingv1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: latestSvc.ObjectMeta.Name,
|
||||
Labels: latestSvc.ObjectMeta.Labels,
|
||||
Annotations: latestSvc.ObjectMeta.Annotations,
|
||||
},
|
||||
TypeMeta: latestSvc.TypeMeta,
|
||||
}
|
||||
|
||||
exportedSvc.Spec.ConfigurationSpec.Template = servingv1.RevisionTemplateSpec{
|
||||
exportedSvc.Spec.Template = servingv1.RevisionTemplateSpec{
|
||||
Spec: revision.Spec,
|
||||
ObjectMeta: latestSvc.Spec.ConfigurationSpec.Template.ObjectMeta,
|
||||
ObjectMeta: latestSvc.Spec.Template.ObjectMeta,
|
||||
}
|
||||
|
||||
exportedSvc.Spec.ConfigurationSpec.Template.ObjectMeta.Name = revision.ObjectMeta.Name
|
||||
|
||||
exportedSvc.Spec.Template.ObjectMeta.Name = revision.ObjectMeta.Name
|
||||
stripIgnoredAnnotationsFromService(&exportedSvc)
|
||||
return exportedSvc
|
||||
}
|
||||
|
||||
func exportServiceWithActiveRevisions(latestSvc *servingv1.Service, client clientservingv1.KnServingClient) (*servingv1.ServiceList, error) {
|
||||
var exportedSvcItems []servingv1.Service
|
||||
|
||||
//get revisions to export from traffic
|
||||
revsMap := getRevisionstoExport(latestSvc)
|
||||
|
||||
// Query for list with filters
|
||||
revisionList, err := client.ListRevisions(clientservingv1.WithService(latestSvc.ObjectMeta.Name))
|
||||
func exportServiceListWithActiveRevisions(latestSvc *servingv1.Service, client clientservingv1.KnServingClient) (*servingv1.ServiceList, error) {
|
||||
revisionList, revsMap, err := getRevisionsToExport(latestSvc, client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(revisionList.Items) == 0 {
|
||||
return nil, fmt.Errorf("no revisions found for the service %s", latestSvc.ObjectMeta.Name)
|
||||
}
|
||||
|
||||
// sort revisions to main the order of generations
|
||||
sortRevisions(revisionList)
|
||||
var exportedSvcItems []servingv1.Service
|
||||
|
||||
for _, revision := range revisionList.Items {
|
||||
//construct service only for active revisions
|
||||
if revsMap[revision.ObjectMeta.Name] {
|
||||
exportedSvcItems = append(exportedSvcItems, constructServicefromRevision(latestSvc, revision))
|
||||
if revsMap[revision.ObjectMeta.Name] && revision.ObjectMeta.Name != latestSvc.Spec.Template.ObjectMeta.Name {
|
||||
exportedSvcItems = append(exportedSvcItems, constructServiceFromRevision(latestSvc, &revision))
|
||||
}
|
||||
}
|
||||
|
||||
if len(exportedSvcItems) == 0 {
|
||||
return nil, fmt.Errorf("no revisions found for service %s", latestSvc.ObjectMeta.Name)
|
||||
}
|
||||
|
||||
//set traffic in the latest revision on if there is traffic split
|
||||
if len(latestSvc.Spec.RouteSpec.Traffic) > 1 {
|
||||
exportedSvcItems[len(exportedSvcItems)-1] = setTrafficSplit(latestSvc, exportedSvcItems[len(exportedSvcItems)-1])
|
||||
}
|
||||
//add latest service, add traffic if more than one revision exist
|
||||
exportedSvcItems = append(exportedSvcItems, *(exportLatestService(latestSvc, len(revisionList.Items) > 1)))
|
||||
|
||||
typeMeta := metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
|
|
@ -181,20 +228,56 @@ func exportServiceWithActiveRevisions(latestSvc *servingv1.Service, client clien
|
|||
return exportedSvcList, nil
|
||||
}
|
||||
|
||||
func setTrafficSplit(latestSvc *servingv1.Service, exportedSvc servingv1.Service) servingv1.Service {
|
||||
|
||||
exportedSvc.Spec.RouteSpec = latestSvc.Spec.RouteSpec
|
||||
return exportedSvc
|
||||
func exportActiveRevisionList(latestSvc *servingv1.Service, client clientservingv1.KnServingClient) (*servingv1.Service, *servingv1.RevisionList, error) {
|
||||
revisionList, revsMap, err := getRevisionsToExport(latestSvc, client)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
func getRevisionstoExport(latestSvc *servingv1.Service) map[string]bool {
|
||||
var exportedRevItems []servingv1.Revision
|
||||
|
||||
for _, revision := range revisionList.Items {
|
||||
//append only active revisions, no latest revision
|
||||
if revsMap[revision.ObjectMeta.Name] && revision.ObjectMeta.Name != latestSvc.Spec.Template.ObjectMeta.Name {
|
||||
exportedRevItems = append(exportedRevItems, exportRevision(&revision))
|
||||
}
|
||||
}
|
||||
|
||||
typeMeta := metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "List",
|
||||
}
|
||||
exportedRevList := &servingv1.RevisionList{
|
||||
TypeMeta: typeMeta,
|
||||
Items: exportedRevItems,
|
||||
}
|
||||
|
||||
return exportLatestService(latestSvc, len(revisionList.Items) > 1), exportedRevList, nil
|
||||
}
|
||||
|
||||
func getRevisionsToExport(latestSvc *servingv1.Service, client clientservingv1.KnServingClient) (*servingv1.RevisionList, map[string]bool, error) {
|
||||
//get revisions to export from traffic
|
||||
revsMap := getRoutedRevisions(latestSvc)
|
||||
|
||||
// Query for list with filters
|
||||
revisionList, err := client.ListRevisions(clientservingv1.WithService(latestSvc.ObjectMeta.Name))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if len(revisionList.Items) == 0 {
|
||||
return nil, nil, fmt.Errorf("no revisions found for the service %s", latestSvc.ObjectMeta.Name)
|
||||
}
|
||||
// sort revisions to maintain the order of generations
|
||||
sortRevisions(revisionList)
|
||||
return revisionList, revsMap, nil
|
||||
}
|
||||
|
||||
func getRoutedRevisions(latestSvc *servingv1.Service) map[string]bool {
|
||||
trafficList := latestSvc.Spec.RouteSpec.Traffic
|
||||
revsMap := make(map[string]bool)
|
||||
|
||||
for _, traffic := range trafficList {
|
||||
if traffic.RevisionName == "" {
|
||||
revsMap[latestSvc.Spec.ConfigurationSpec.Template.ObjectMeta.Name] = true
|
||||
} else {
|
||||
if traffic.RevisionName != "" {
|
||||
revsMap[traffic.RevisionName] = true
|
||||
}
|
||||
}
|
||||
|
|
@ -230,3 +313,15 @@ func revisionListSortFunc(revisionList *servingv1.RevisionList) func(i int, j in
|
|||
return a.Name > b.Name
|
||||
}
|
||||
}
|
||||
|
||||
func stripIgnoredAnnotationsFromService(svc *servingv1.Service) {
|
||||
for _, annotation := range IGNORED_SERVICE_ANNOTATIONS {
|
||||
delete(svc.ObjectMeta.Annotations, annotation)
|
||||
}
|
||||
}
|
||||
|
||||
func stripIgnoredAnnotationsFromRevision(revision *servingv1.Revision) {
|
||||
for _, annotation := range IGNORED_REVISION_ANNOTATIONS {
|
||||
delete(revision.ObjectMeta.Annotations, annotation)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,15 +16,13 @@ package service
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
servinglib "knative.dev/client/pkg/serving"
|
||||
knclient "knative.dev/client/pkg/serving/v1"
|
||||
"knative.dev/client/pkg/util/mock"
|
||||
"knative.dev/pkg/ptr"
|
||||
|
|
@ -35,162 +33,317 @@ import (
|
|||
|
||||
type expectedServiceOption func(*servingv1.Service)
|
||||
type expectedRevisionOption func(*servingv1.Revision)
|
||||
type expectedServiceListOption func(*servingv1.ServiceList)
|
||||
type expectedRevisionListOption func(*servingv1.RevisionList)
|
||||
type podSpecOption func(*v1.PodSpec)
|
||||
|
||||
type testCase struct {
|
||||
name string
|
||||
latestSvc *servingv1.Service
|
||||
expectedSvcList *servingv1.ServiceList
|
||||
revisionList *servingv1.RevisionList
|
||||
expectedRevisionList *servingv1.RevisionList
|
||||
}
|
||||
|
||||
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")
|
||||
|
||||
_, err = executeServiceExportCommand(t, tc, "export", tc.latestSvc.ObjectMeta.Name, "--with-revisions", "-o", "json")
|
||||
assert.Error(t, err, "'kn service export --with-revisions' requires a mode, please specify one of kubernetes|resources.")
|
||||
|
||||
_, err = executeServiceExportCommand(t, tc, "export", tc.latestSvc.ObjectMeta.Name, "--with-revisions", "--mode", "k8s", "-o", "yaml")
|
||||
assert.Error(t, err, "'kn service export --with-revisions' requires a mode, please specify one of kubernetes|resources.")
|
||||
}
|
||||
|
||||
func TestServiceExport(t *testing.T) {
|
||||
|
||||
svcs := []*servingv1.Service{
|
||||
getServiceWithOptions(getService("foo"), withContainer()),
|
||||
getServiceWithOptions(getService("foo"), withContainer(), withEnv([]v1.EnvVar{{Name: "a", Value: "mouse"}})),
|
||||
getServiceWithOptions(getService("foo"), withContainer(), withLabels(map[string]string{"a": "mouse", "b": "cookie", "empty": ""})),
|
||||
getServiceWithOptions(getService("foo"), withContainer(), withEnvFrom([]string{"cm-name"})),
|
||||
getServiceWithOptions(getService("foo"), withContainer(), withVolumeandSecrets("volName", "secretName")),
|
||||
}
|
||||
|
||||
for _, svc := range svcs {
|
||||
callServiceExportTest(t, svc)
|
||||
for _, tc := range []testCase{
|
||||
{latestSvc: getServiceWithOptions(getService("foo"), withServicePodSpecOption(withContainer()))},
|
||||
{latestSvc: getServiceWithOptions(getService("foo"), withServicePodSpecOption(withContainer(), withEnv([]v1.EnvVar{{Name: "a", Value: "mouse"}})))},
|
||||
{latestSvc: getServiceWithOptions(getService("foo"), withConfigurationLabels(map[string]string{"a": "mouse"}), withConfigurationAnnotations(map[string]string{"a": "mouse"}), withServicePodSpecOption(withContainer()))},
|
||||
{latestSvc: getServiceWithOptions(getService("foo"), withLabels(map[string]string{"a": "mouse"}), withAnnotations(map[string]string{"a": "mouse"}), withServicePodSpecOption(withContainer()))},
|
||||
{latestSvc: getServiceWithOptions(getService("foo"), withServicePodSpecOption(withContainer(), withVolumeandSecrets("secretName")))},
|
||||
} {
|
||||
exportServiceTest(t, &tc)
|
||||
}
|
||||
}
|
||||
|
||||
func callServiceExportTest(t *testing.T, expectedService *servingv1.Service) {
|
||||
// New mock client
|
||||
client := knclient.NewMockKnServiceClient(t)
|
||||
// Recording:
|
||||
r := client.Recorder()
|
||||
r.GetService(expectedService.ObjectMeta.Name, expectedService, nil)
|
||||
|
||||
output, err := executeServiceCommand(client, "export", expectedService.ObjectMeta.Name, "-o", "yaml")
|
||||
func exportServiceTest(t *testing.T, tc *testCase) {
|
||||
output, err := executeServiceExportCommand(t, tc, "export", tc.latestSvc.ObjectMeta.Name, "-o", "yaml")
|
||||
assert.NilError(t, err)
|
||||
|
||||
actSvc := servingv1.Service{}
|
||||
err = yaml.Unmarshal([]byte(output), &actSvc)
|
||||
assert.NilError(t, err)
|
||||
stripExpectedSvcVariables(expectedService)
|
||||
assert.DeepEqual(t, expectedService, &actSvc)
|
||||
// Validate that all recorded API methods have been called
|
||||
r.Validate()
|
||||
|
||||
stripUnwantedFields(tc.latestSvc)
|
||||
assert.DeepEqual(t, tc.latestSvc, &actSvc)
|
||||
}
|
||||
|
||||
func TestServiceExportwithMultipleRevisions(t *testing.T) {
|
||||
//case 1 - 2 revisions with traffic split
|
||||
expSvc1 := getServiceWithOptions(getService("foo"), withContainer(), withServiceRevisionName("foo-rev-1"))
|
||||
stripExpectedSvcVariables(expSvc1)
|
||||
expSvc2 := getServiceWithOptions(getService("foo"), withContainer(), withTrafficSplit([]string{"foo-rev-1", "foo-rev-2"}, []int{50, 50}, []string{"latest", "current"}), withServiceRevisionName("foo-rev-2"))
|
||||
stripExpectedSvcVariables(expSvc2)
|
||||
latestSvc := getServiceWithOptions(getService("foo"), withContainer(), withTrafficSplit([]string{"foo-rev-1", "foo-rev-2"}, []int{50, 50}, []string{"latest", "current"}))
|
||||
|
||||
expSvcList := servingv1.ServiceList{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "List",
|
||||
},
|
||||
Items: []servingv1.Service{*expSvc1, *expSvc2},
|
||||
for _, tc := range []testCase{{
|
||||
name: "test 2 revisions with traffic split",
|
||||
latestSvc: getServiceWithOptions(
|
||||
getService("foo"),
|
||||
withAnnotations(map[string]string{"serving.knative.dev/creator": "ut", "serving.knative.dev/lastModifier": "ut"}),
|
||||
withTrafficSplit([]string{"foo-rev-1", ""}, []int{50, 50}, []bool{false, true}),
|
||||
withServicePodSpecOption(withContainer()),
|
||||
),
|
||||
expectedSvcList: getServiceListWithOptions(
|
||||
withServices(
|
||||
getService("foo"),
|
||||
withUnwantedFieldsStripped(),
|
||||
withServicePodSpecOption(withContainer()),
|
||||
withServiceRevisionName("foo-rev-1"),
|
||||
),
|
||||
withServices(
|
||||
getService("foo"),
|
||||
withUnwantedFieldsStripped(),
|
||||
withServicePodSpecOption(withContainer()),
|
||||
withTrafficSplit([]string{"foo-rev-1", ""}, []int{50, 50}, []bool{false, true}),
|
||||
),
|
||||
),
|
||||
revisionList: getRevisionListWithOptions(
|
||||
withRevisions(
|
||||
withRevisionLabels(map[string]string{apiserving.ServiceLabelKey: "foo"}),
|
||||
withRevisionGeneration("1"),
|
||||
withRevisionAnnotations(map[string]string{"serving.knative.dev/lastPinned": "1111132"}),
|
||||
withRevisionName("foo-rev-1"),
|
||||
withRevisionPodSpecOption(withContainer()),
|
||||
),
|
||||
withRevisions(
|
||||
withRevisionLabels(map[string]string{apiserving.ServiceLabelKey: "foo"}),
|
||||
withRevisionGeneration("2"),
|
||||
withRevisionName("foo-rev-2"),
|
||||
withRevisionPodSpecOption(withContainer()),
|
||||
),
|
||||
),
|
||||
expectedRevisionList: getRevisionListWithOptions(
|
||||
withRevisions(
|
||||
withRevisionLabels(map[string]string{apiserving.ServiceLabelKey: "foo"}),
|
||||
withRevisionName("foo-rev-1"),
|
||||
withRevisionGeneration("1"),
|
||||
withRevisionPodSpecOption(withContainer()),
|
||||
),
|
||||
),
|
||||
}, {
|
||||
name: "test 2 revisions no traffic split",
|
||||
latestSvc: getServiceWithOptions(
|
||||
getService("foo"),
|
||||
withTrafficSplit([]string{""}, []int{100}, []bool{true}),
|
||||
withServicePodSpecOption(withContainer()),
|
||||
),
|
||||
expectedSvcList: getServiceListWithOptions(
|
||||
withServices(
|
||||
getService("foo"),
|
||||
withUnwantedFieldsStripped(),
|
||||
withServicePodSpecOption(withContainer()),
|
||||
withTrafficSplit([]string{""}, []int{100}, []bool{true}),
|
||||
),
|
||||
),
|
||||
revisionList: getRevisionListWithOptions(
|
||||
withRevisions(
|
||||
withRevisionLabels(map[string]string{apiserving.ServiceLabelKey: "foo"}),
|
||||
withRevisionGeneration("1"),
|
||||
withRevisionName("foo-rev-1"),
|
||||
withRevisionPodSpecOption(withContainer()),
|
||||
),
|
||||
withRevisions(
|
||||
withRevisionLabels(map[string]string{apiserving.ServiceLabelKey: "foo"}),
|
||||
withRevisionGeneration("2"),
|
||||
withRevisionName("foo-rev-2"),
|
||||
withRevisionPodSpecOption(withContainer()),
|
||||
),
|
||||
),
|
||||
}, {
|
||||
name: "test 3 active revisions with traffic split with no latest revision",
|
||||
latestSvc: getServiceWithOptions(
|
||||
getService("foo"),
|
||||
withTrafficSplit([]string{"foo-rev-1", "foo-rev-2", "foo-rev-3"}, []int{25, 50, 25}, []bool{false, false, false}),
|
||||
withServiceRevisionName("foo-rev-3"),
|
||||
withServicePodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]v1.EnvVar{{Name: "a", Value: "mouse"}}),
|
||||
),
|
||||
),
|
||||
expectedSvcList: getServiceListWithOptions(
|
||||
withServices(
|
||||
getService("foo"),
|
||||
withUnwantedFieldsStripped(),
|
||||
withServicePodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]v1.EnvVar{{Name: "a", Value: "cat"}}),
|
||||
),
|
||||
withServiceRevisionName("foo-rev-1"),
|
||||
),
|
||||
withServices(
|
||||
getService("foo"),
|
||||
withUnwantedFieldsStripped(),
|
||||
withServicePodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]v1.EnvVar{{Name: "a", Value: "dog"}}),
|
||||
),
|
||||
withServiceRevisionName("foo-rev-2"),
|
||||
),
|
||||
withServices(
|
||||
getService("foo"),
|
||||
withUnwantedFieldsStripped(),
|
||||
withServicePodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]v1.EnvVar{{Name: "a", Value: "mouse"}}),
|
||||
),
|
||||
withServiceRevisionName("foo-rev-3"),
|
||||
withTrafficSplit([]string{"foo-rev-1", "foo-rev-2", "foo-rev-3"}, []int{25, 50, 25}, []bool{false, false, false}),
|
||||
),
|
||||
),
|
||||
revisionList: getRevisionListWithOptions(
|
||||
withRevisions(
|
||||
withRevisionLabels(map[string]string{apiserving.ServiceLabelKey: "foo"}),
|
||||
withRevisionGeneration("1"),
|
||||
withRevisionName("foo-rev-1"),
|
||||
withRevisionPodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]v1.EnvVar{{Name: "a", Value: "cat"}}),
|
||||
),
|
||||
),
|
||||
withRevisions(
|
||||
withRevisionLabels(map[string]string{apiserving.ServiceLabelKey: "foo"}),
|
||||
withRevisionGeneration("2"),
|
||||
withRevisionName("foo-rev-2"),
|
||||
withRevisionPodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]v1.EnvVar{{Name: "a", Value: "dog"}}),
|
||||
),
|
||||
),
|
||||
withRevisions(
|
||||
withRevisionLabels(map[string]string{apiserving.ServiceLabelKey: "foo"}),
|
||||
withRevisionGeneration("3"),
|
||||
withRevisionName("foo-rev-3"),
|
||||
withRevisionPodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]v1.EnvVar{{Name: "a", Value: "mouse"}}),
|
||||
),
|
||||
),
|
||||
),
|
||||
expectedRevisionList: getRevisionListWithOptions(
|
||||
withRevisions(
|
||||
withRevisionLabels(map[string]string{apiserving.ServiceLabelKey: "foo"}),
|
||||
withRevisionName("foo-rev-1"),
|
||||
withRevisionGeneration("1"),
|
||||
withRevisionPodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]v1.EnvVar{{Name: "a", Value: "cat"}}),
|
||||
),
|
||||
),
|
||||
withRevisions(
|
||||
withRevisionLabels(map[string]string{apiserving.ServiceLabelKey: "foo"}),
|
||||
withRevisionName("foo-rev-2"),
|
||||
withRevisionGeneration("2"),
|
||||
withRevisionPodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]v1.EnvVar{{Name: "a", Value: "dog"}}),
|
||||
),
|
||||
),
|
||||
),
|
||||
}} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
exportWithRevisionsforKubernetesTest(t, &tc)
|
||||
exportWithRevisionsTest(t, &tc)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
multiRevs := getRevisionList("rev", "foo")
|
||||
|
||||
callServiceExportHistoryTest(t, latestSvc, multiRevs, &expSvcList)
|
||||
|
||||
// case 2 - same revisions no traffic split
|
||||
expSvc2 = getServiceWithOptions(getService("foo"), withContainer(), withServiceRevisionName("foo-rev-2"))
|
||||
stripExpectedSvcVariables(expSvc2)
|
||||
expSvcList = servingv1.ServiceList{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "List",
|
||||
},
|
||||
Items: []servingv1.Service{*expSvc2},
|
||||
}
|
||||
latestSvc = getServiceWithOptions(getService("foo"), withContainer(), withTrafficSplit([]string{"foo-rev-2"}, []int{100}, []string{"latest"}))
|
||||
callServiceExportHistoryTest(t, latestSvc, multiRevs, &expSvcList)
|
||||
}
|
||||
|
||||
func callServiceExportHistoryTest(t *testing.T, latestSvc *servingv1.Service, revs *servingv1.RevisionList, expSvcList *servingv1.ServiceList) {
|
||||
// New mock client
|
||||
client := knclient.NewMockKnServiceClient(t)
|
||||
// Recording:
|
||||
r := client.Recorder()
|
||||
|
||||
r.GetService(latestSvc.ObjectMeta.Name, latestSvc, nil)
|
||||
r.ListRevisions(mock.Any(), revs, nil)
|
||||
|
||||
output, err := executeServiceCommand(client, "export", latestSvc.ObjectMeta.Name, "--with-revisions", "-o", "json")
|
||||
func exportWithRevisionsforKubernetesTest(t *testing.T, tc *testCase) {
|
||||
output, err := executeServiceExportCommand(t, tc, "export", tc.latestSvc.ObjectMeta.Name, "--with-revisions", "--mode", "kubernetes", "-o", "json")
|
||||
assert.NilError(t, err)
|
||||
|
||||
actSvcList := servingv1.ServiceList{}
|
||||
err = json.Unmarshal([]byte(output), &actSvcList)
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, expSvcList, &actSvcList)
|
||||
// Validate that all recorded API methods have been called
|
||||
r.Validate()
|
||||
assert.DeepEqual(t, tc.expectedSvcList, &actSvcList)
|
||||
}
|
||||
|
||||
func TestServiceExportError(t *testing.T) {
|
||||
// New mock client
|
||||
func exportWithRevisionsTest(t *testing.T, tc *testCase) {
|
||||
output, err := executeServiceExportCommand(t, tc, "export", tc.latestSvc.ObjectMeta.Name, "--with-revisions", "--mode", "resources", "-o", "json")
|
||||
assert.NilError(t, err)
|
||||
|
||||
stripUnwantedFields(tc.latestSvc)
|
||||
expOut := strings.Builder{}
|
||||
expSvcJSON, err := json.MarshalIndent(tc.latestSvc, "", " ")
|
||||
assert.NilError(t, err)
|
||||
expOut.Write(expSvcJSON)
|
||||
expOut.WriteString("\n")
|
||||
|
||||
if tc.expectedRevisionList != nil {
|
||||
expRevsJSON, err := json.MarshalIndent(tc.expectedRevisionList, "", " ")
|
||||
assert.NilError(t, err)
|
||||
expOut.Write(expRevsJSON)
|
||||
expOut.WriteString("\n")
|
||||
}
|
||||
|
||||
assert.Equal(t, expOut.String(), output)
|
||||
}
|
||||
|
||||
func executeServiceExportCommand(t *testing.T, tc *testCase, options ...string) (string, error) {
|
||||
client := knclient.NewMockKnServiceClient(t)
|
||||
r := client.Recorder()
|
||||
|
||||
expectedService := getService("foo")
|
||||
r.GetService(tc.latestSvc.ObjectMeta.Name, tc.latestSvc, nil)
|
||||
r.ListRevisions(mock.Any(), tc.revisionList, nil)
|
||||
|
||||
_, err := executeServiceCommand(client, "export", expectedService.ObjectMeta.Name)
|
||||
|
||||
assert.Error(t, err, "'kn service export' requires output format")
|
||||
return executeServiceCommand(client, options...)
|
||||
}
|
||||
|
||||
func getRevisionList(revision string, service string) *servingv1.RevisionList {
|
||||
rev1 := getRevisionWithOptions(
|
||||
service,
|
||||
withRevisionGeneration("1"),
|
||||
withRevisionName(fmt.Sprintf("%s-%s-%d", service, revision, 1)),
|
||||
)
|
||||
func stripUnwantedFields(svc *servingv1.Service) {
|
||||
svc.ObjectMeta.Namespace = ""
|
||||
svc.Spec.Template.Spec.Containers[0].Resources = v1.ResourceRequirements{}
|
||||
svc.Status = servingv1.ServiceStatus{}
|
||||
svc.ObjectMeta.CreationTimestamp = metav1.Time{}
|
||||
}
|
||||
|
||||
rev2 := getRevisionWithOptions(
|
||||
service,
|
||||
withRevisionGeneration("2"),
|
||||
withRevisionName(fmt.Sprintf("%s-%s-%d", service, revision, 2)),
|
||||
)
|
||||
|
||||
return &servingv1.RevisionList{
|
||||
func getServiceListWithOptions(options ...expectedServiceListOption) *servingv1.ServiceList {
|
||||
list := &servingv1.ServiceList{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "List",
|
||||
},
|
||||
Items: []servingv1.Revision{rev1, rev2},
|
||||
}
|
||||
}
|
||||
|
||||
func stripExpectedSvcVariables(expectedsvc *servingv1.Service) {
|
||||
expectedsvc.ObjectMeta.Namespace = ""
|
||||
expectedsvc.Spec.Template.Spec.Containers[0].Resources = v1.ResourceRequirements{}
|
||||
expectedsvc.Status = servingv1.ServiceStatus{}
|
||||
expectedsvc.ObjectMeta.Annotations = nil
|
||||
expectedsvc.ObjectMeta.CreationTimestamp = metav1.Time{}
|
||||
}
|
||||
|
||||
func getRevisionWithOptions(service string, options ...expectedRevisionOption) servingv1.Revision {
|
||||
rev := servingv1.Revision{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Revision",
|
||||
APIVersion: "serving.knative.dev/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{
|
||||
apiserving.ServiceLabelKey: service,
|
||||
},
|
||||
},
|
||||
Spec: servingv1.RevisionSpec{
|
||||
PodSpec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Image: "gcr.io/foo/bar:baz",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, fn := range options {
|
||||
fn(&rev)
|
||||
fn(list)
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
func withServices(svc *servingv1.Service, options ...expectedServiceOption) expectedServiceListOption {
|
||||
return func(list *servingv1.ServiceList) {
|
||||
list.Items = append(list.Items, *(getServiceWithOptions(svc, options...)))
|
||||
}
|
||||
}
|
||||
|
||||
func getRevisionListWithOptions(options ...expectedRevisionListOption) *servingv1.RevisionList {
|
||||
list := &servingv1.RevisionList{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "List",
|
||||
},
|
||||
}
|
||||
|
||||
for _, fn := range options {
|
||||
fn(list)
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
func withRevisions(options ...expectedRevisionOption) expectedRevisionListOption {
|
||||
return func(list *servingv1.RevisionList) {
|
||||
list.Items = append(list.Items, getRevisionWithOptions(options...))
|
||||
}
|
||||
return rev
|
||||
}
|
||||
|
||||
func getServiceWithOptions(svc *servingv1.Service, options ...expectedServiceOption) *servingv1.Service {
|
||||
|
|
@ -205,98 +358,47 @@ func getServiceWithOptions(svc *servingv1.Service, options ...expectedServiceOpt
|
|||
|
||||
return svc
|
||||
}
|
||||
|
||||
func withLabels(labels map[string]string) expectedServiceOption {
|
||||
return func(svc *servingv1.Service) {
|
||||
svc.Spec.ConfigurationSpec.Template.ObjectMeta.Labels = labels
|
||||
svc.ObjectMeta.Labels = labels
|
||||
}
|
||||
}
|
||||
|
||||
func withEnvFrom(cmNames []string) expectedServiceOption {
|
||||
func withConfigurationLabels(labels map[string]string) expectedServiceOption {
|
||||
return func(svc *servingv1.Service) {
|
||||
var list []v1.EnvFromSource
|
||||
for _, cmName := range cmNames {
|
||||
list = append(list, v1.EnvFromSource{
|
||||
ConfigMapRef: &v1.ConfigMapEnvSource{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: cmName,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
svc.Spec.ConfigurationSpec.Template.Spec.PodSpec.Containers[0].EnvFrom = list
|
||||
svc.Spec.Template.ObjectMeta.Labels = labels
|
||||
}
|
||||
}
|
||||
|
||||
func withEnv(env []v1.EnvVar) expectedServiceOption {
|
||||
func withAnnotations(Annotations map[string]string) expectedServiceOption {
|
||||
return func(svc *servingv1.Service) {
|
||||
svc.Spec.ConfigurationSpec.Template.Spec.PodSpec.Containers[0].Env = env
|
||||
svc.ObjectMeta.Annotations = Annotations
|
||||
}
|
||||
}
|
||||
|
||||
func withContainer() expectedServiceOption {
|
||||
func withConfigurationAnnotations(Annotations map[string]string) expectedServiceOption {
|
||||
return func(svc *servingv1.Service) {
|
||||
svc.Spec.ConfigurationSpec.Template.Spec.Containers[0].Image = "gcr.io/foo/bar:baz"
|
||||
svc.Spec.ConfigurationSpec.Template.Annotations = map[string]string{servinglib.UserImageAnnotationKey: "gcr.io/foo/bar:baz"}
|
||||
|
||||
svc.Spec.Template.ObjectMeta.Annotations = Annotations
|
||||
}
|
||||
}
|
||||
|
||||
func withVolumeandSecrets(volName string, secretName string) expectedServiceOption {
|
||||
return func(svc *servingv1.Service) {
|
||||
template := &svc.Spec.Template
|
||||
template.Spec.Volumes = []v1.Volume{
|
||||
{
|
||||
Name: volName,
|
||||
VolumeSource: v1.VolumeSource{
|
||||
Secret: &v1.SecretVolumeSource{
|
||||
SecretName: secretName,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
template.Spec.Containers[0].VolumeMounts = []v1.VolumeMount{
|
||||
{
|
||||
Name: volName,
|
||||
MountPath: "/mount/path",
|
||||
ReadOnly: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func withRevisionGeneration(gen string) expectedRevisionOption {
|
||||
return func(rev *servingv1.Revision) {
|
||||
i, _ := strconv.Atoi(gen)
|
||||
rev.ObjectMeta.Generation = int64(i)
|
||||
rev.ObjectMeta.Labels[apiserving.ConfigurationGenerationLabelKey] = gen
|
||||
}
|
||||
}
|
||||
|
||||
func withRevisionName(name string) expectedRevisionOption {
|
||||
return func(rev *servingv1.Revision) {
|
||||
rev.ObjectMeta.Name = name
|
||||
}
|
||||
}
|
||||
|
||||
func withServiceRevisionName(name string) expectedServiceOption {
|
||||
return func(svc *servingv1.Service) {
|
||||
svc.Spec.ConfigurationSpec.Template.ObjectMeta.Name = name
|
||||
svc.Spec.Template.ObjectMeta.Name = name
|
||||
}
|
||||
}
|
||||
|
||||
func withTrafficSplit(revisions []string, percentages []int, tags []string) expectedServiceOption {
|
||||
func withUnwantedFieldsStripped() expectedServiceOption {
|
||||
return func(svc *servingv1.Service) {
|
||||
svc.ObjectMeta.Namespace = ""
|
||||
svc.Spec.Template.Spec.Containers[0].Resources = v1.ResourceRequirements{}
|
||||
svc.Status = servingv1.ServiceStatus{}
|
||||
svc.ObjectMeta.CreationTimestamp = metav1.Time{}
|
||||
}
|
||||
}
|
||||
func withTrafficSplit(revisions []string, percentages []int, latest []bool) expectedServiceOption {
|
||||
return func(svc *servingv1.Service) {
|
||||
var trafficTargets []servingv1.TrafficTarget
|
||||
for i, rev := range revisions {
|
||||
trafficTargets = append(trafficTargets, servingv1.TrafficTarget{
|
||||
Percent: ptr.Int64(int64(percentages[i])),
|
||||
})
|
||||
if tags[i] != "" {
|
||||
trafficTargets[i].Tag = tags[i]
|
||||
}
|
||||
if rev == "latest" {
|
||||
if latest[i] {
|
||||
trafficTargets[i].LatestRevision = ptr.Bool(true)
|
||||
} else {
|
||||
trafficTargets[i].RevisionName = rev
|
||||
|
|
@ -308,3 +410,86 @@ func withTrafficSplit(revisions []string, percentages []int, tags []string) expe
|
|||
}
|
||||
}
|
||||
}
|
||||
func withServicePodSpecOption(options ...podSpecOption) expectedServiceOption {
|
||||
return func(svc *servingv1.Service) {
|
||||
svc.Spec.Template.Spec.PodSpec = getPodSpecWithOptions(options...)
|
||||
}
|
||||
}
|
||||
|
||||
func getRevisionWithOptions(options ...expectedRevisionOption) servingv1.Revision {
|
||||
rev := servingv1.Revision{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Revision",
|
||||
APIVersion: "serving.knative.dev/v1",
|
||||
},
|
||||
}
|
||||
for _, fn := range options {
|
||||
fn(&rev)
|
||||
}
|
||||
return rev
|
||||
}
|
||||
func withRevisionGeneration(gen string) expectedRevisionOption {
|
||||
return func(rev *servingv1.Revision) {
|
||||
rev.ObjectMeta.Labels[apiserving.ConfigurationGenerationLabelKey] = gen
|
||||
}
|
||||
}
|
||||
func withRevisionName(name string) expectedRevisionOption {
|
||||
return func(rev *servingv1.Revision) {
|
||||
rev.ObjectMeta.Name = name
|
||||
}
|
||||
}
|
||||
func withRevisionLabels(labels map[string]string) expectedRevisionOption {
|
||||
return func(rev *servingv1.Revision) {
|
||||
rev.ObjectMeta.Labels = labels
|
||||
}
|
||||
}
|
||||
func withRevisionAnnotations(Annotations map[string]string) expectedRevisionOption {
|
||||
return func(rev *servingv1.Revision) {
|
||||
rev.ObjectMeta.Annotations = Annotations
|
||||
}
|
||||
}
|
||||
func withRevisionPodSpecOption(options ...podSpecOption) expectedRevisionOption {
|
||||
return func(rev *servingv1.Revision) {
|
||||
rev.Spec.PodSpec = getPodSpecWithOptions(options...)
|
||||
}
|
||||
}
|
||||
|
||||
func getPodSpecWithOptions(options ...podSpecOption) v1.PodSpec {
|
||||
spec := v1.PodSpec{}
|
||||
for _, fn := range options {
|
||||
fn(&spec)
|
||||
}
|
||||
return spec
|
||||
}
|
||||
|
||||
func withEnv(env []v1.EnvVar) podSpecOption {
|
||||
return func(spec *v1.PodSpec) {
|
||||
spec.Containers[0].Env = env
|
||||
}
|
||||
}
|
||||
func withContainer() podSpecOption {
|
||||
return func(spec *v1.PodSpec) {
|
||||
spec.Containers = append(spec.Containers, v1.Container{Image: "gcr.io/foo/bar:baz"})
|
||||
}
|
||||
}
|
||||
func withVolumeandSecrets(secretName string) podSpecOption {
|
||||
return func(spec *v1.PodSpec) {
|
||||
spec.Volumes = []v1.Volume{
|
||||
{
|
||||
Name: secretName,
|
||||
VolumeSource: v1.VolumeSource{
|
||||
Secret: &v1.SecretVolumeSource{
|
||||
SecretName: secretName,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
spec.Containers[0].VolumeMounts = []v1.VolumeMount{
|
||||
{
|
||||
Name: secretName,
|
||||
MountPath: "/mount/path",
|
||||
ReadOnly: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package e2e
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
|
|
@ -35,8 +36,12 @@ import (
|
|||
)
|
||||
|
||||
type expectedServiceOption func(*servingv1.Service)
|
||||
type expectedRevisionOption func(*servingv1.Revision)
|
||||
type expectedServiceListOption func(*servingv1.ServiceList)
|
||||
type expectedRevisionListOption func(*servingv1.RevisionList)
|
||||
type podSpecOption func(*corev1.PodSpec)
|
||||
|
||||
func TestServiceExportImportApply(t *testing.T) {
|
||||
func TestServiceExport(t *testing.T) {
|
||||
t.Parallel()
|
||||
it, err := test.NewKnTest()
|
||||
assert.NilError(t, err)
|
||||
|
|
@ -50,22 +55,235 @@ func TestServiceExportImportApply(t *testing.T) {
|
|||
t.Log("create service with byo revision")
|
||||
serviceCreateWithOptions(r, "hello", "--revision-name", "rev1")
|
||||
|
||||
t.Log("export service and compare")
|
||||
serviceExport(r, "hello", getSvc(withName("hello"), withRevisionName("hello-rev1"), withAnnotations()), "-o", "json")
|
||||
t.Log("export service-revision1 and compare")
|
||||
serviceExport(r, "hello", getServiceWithOptions(
|
||||
withServiceName("hello"),
|
||||
withServiceRevisionName("hello-rev1"),
|
||||
withConfigurationAnnotations(),
|
||||
withServicePodSpecOption(withContainer()),
|
||||
), "-o", "json")
|
||||
|
||||
t.Log("update service - add env variable")
|
||||
serviceUpdateWithOptions(r, "hello", "--env", "key1=val1", "--revision-name", "rev2", "--no-lock-to-digest")
|
||||
serviceExport(r, "hello", getSvc(withName("hello"), withRevisionName("hello-rev2"), withEnv("key1", "val1")), "-o", "json")
|
||||
serviceExportWithRevisions(r, "hello", getSvcListWithOneRevision(), "--with-revisions", "-o", "yaml")
|
||||
serviceUpdateWithOptions(r, "hello", "--env", "a=mouse", "--revision-name", "rev2", "--no-lock-to-digest")
|
||||
serviceExport(r, "hello", getServiceWithOptions(
|
||||
withServiceName("hello"),
|
||||
withServiceRevisionName("hello-rev2"),
|
||||
withServicePodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]corev1.EnvVar{{Name: "a", Value: "mouse"}}),
|
||||
),
|
||||
), "-o", "json")
|
||||
|
||||
t.Log("export service-revision2 with kubernetes-resources")
|
||||
serviceExportWithServiceList(r, "hello", getServiceListWithOptions(
|
||||
withServices(
|
||||
withServiceName("hello"),
|
||||
withServiceRevisionName("hello-rev2"),
|
||||
withTrafficSplit([]string{"latest"}, []int{100}, []string{""}),
|
||||
withServicePodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]corev1.EnvVar{{Name: "a", Value: "mouse"}}),
|
||||
),
|
||||
),
|
||||
), "--with-revisions", "--mode", "kubernetes", "-o", "yaml")
|
||||
|
||||
t.Log("export service-revision2 with revisions-only")
|
||||
serviceExportWithRevisionList(r, "hello", getServiceWithOptions(
|
||||
withServiceName("hello"),
|
||||
withServiceRevisionName("hello-rev2"),
|
||||
withTrafficSplit([]string{"latest"}, []int{100}, []string{""}),
|
||||
withServicePodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]corev1.EnvVar{{Name: "a", Value: "mouse"}}),
|
||||
),
|
||||
), getRevisionListWithOptions(), "--with-revisions", "--mode", "resources", "-o", "yaml")
|
||||
|
||||
t.Log("update service with tag and split traffic")
|
||||
serviceUpdateWithOptions(r, "hello", "--tag", "hello-rev1=candidate", "--traffic", "candidate=2%,@latest=98%")
|
||||
serviceExportWithRevisions(r, "hello", getSvcListWithTags(), "--with-revisions", "-o", "yaml")
|
||||
|
||||
t.Log("update service - untag, add env variable and traffic split")
|
||||
t.Log("export service-revision2 after tagging kubernetes-resources")
|
||||
serviceExportWithServiceList(r, "hello", getServiceListWithOptions(
|
||||
withServices(
|
||||
withServiceName("hello"),
|
||||
withServiceRevisionName("hello-rev1"),
|
||||
withServicePodSpecOption(
|
||||
withContainer(),
|
||||
),
|
||||
),
|
||||
withServices(
|
||||
withServiceName("hello"),
|
||||
withServiceRevisionName("hello-rev2"),
|
||||
withTrafficSplit([]string{"latest", "hello-rev1"}, []int{98, 2}, []string{"", "candidate"}),
|
||||
withServicePodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]corev1.EnvVar{{Name: "a", Value: "mouse"}}),
|
||||
),
|
||||
),
|
||||
), "--with-revisions", "--mode", "kubernetes", "-o", "yaml")
|
||||
|
||||
t.Log("export service-revision2 after tagging with revisions-only")
|
||||
serviceExportWithRevisionList(r, "hello", getServiceWithOptions(
|
||||
withServiceName("hello"),
|
||||
withServiceRevisionName("hello-rev2"),
|
||||
withTrafficSplit([]string{"latest", "hello-rev1"}, []int{98, 2}, []string{"", "candidate"}),
|
||||
withServicePodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]corev1.EnvVar{{Name: "a", Value: "mouse"}}),
|
||||
),
|
||||
), getRevisionListWithOptions(
|
||||
withRevisions(
|
||||
withRevisionName("hello-rev1"),
|
||||
withRevisionAnnotations(
|
||||
map[string]string{
|
||||
"client.knative.dev/user-image": "gcr.io/knative-samples/helloworld-go",
|
||||
"serving.knative.dev/creator": "kubernetes-admin",
|
||||
}),
|
||||
withRevisionLabels(
|
||||
map[string]string{
|
||||
"serving.knative.dev/configuration": "hello",
|
||||
"serving.knative.dev/configurationGeneration": "1",
|
||||
"serving.knative.dev/route": "hello",
|
||||
"serving.knative.dev/service": "hello",
|
||||
}),
|
||||
withRevisionPodSpecOption(
|
||||
withContainer(),
|
||||
),
|
||||
),
|
||||
), "--with-revisions", "--mode", "resources", "-o", "yaml")
|
||||
|
||||
t.Log("update service - untag, add env variable, traffic split and system revision name")
|
||||
serviceUpdateWithOptions(r, "hello", "--untag", "candidate")
|
||||
serviceUpdateWithOptions(r, "hello", "--env", "key2=val2", "--revision-name", "rev3", "--traffic", "hello-rev1=30,hello-rev2=30,hello-rev3=40")
|
||||
serviceExportWithRevisions(r, "hello", getSvcListWOTags(), "--with-revisions", "-o", "yaml")
|
||||
serviceUpdateWithOptions(r, "hello", "--env", "b=cat", "--revision-name", "hello-rev3", "--traffic", "hello-rev1=30,hello-rev2=30,hello-rev3=40")
|
||||
|
||||
t.Log("export service-revision3 with kubernetes-resources")
|
||||
serviceExportWithServiceList(r, "hello", getServiceListWithOptions(
|
||||
withServices(
|
||||
withServiceName("hello"),
|
||||
withServiceRevisionName("hello-rev1"),
|
||||
withServicePodSpecOption(
|
||||
withContainer(),
|
||||
),
|
||||
),
|
||||
withServices(
|
||||
withServiceName("hello"),
|
||||
withServiceRevisionName("hello-rev2"),
|
||||
withServicePodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]corev1.EnvVar{{Name: "a", Value: "mouse"}}),
|
||||
),
|
||||
),
|
||||
withServices(
|
||||
withServiceName("hello"),
|
||||
withServiceRevisionName("hello-rev3"),
|
||||
withTrafficSplit([]string{"hello-rev1", "hello-rev2", "hello-rev3"}, []int{30, 30, 40}, []string{"", "", ""}),
|
||||
withServicePodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]corev1.EnvVar{{Name: "a", Value: "mouse"}, {Name: "b", Value: "cat"}}),
|
||||
),
|
||||
),
|
||||
), "--with-revisions", "--mode", "kubernetes", "-o", "yaml")
|
||||
|
||||
t.Log("export service-revision3 with revisions-only")
|
||||
serviceExportWithRevisionList(r, "hello", getServiceWithOptions(
|
||||
withServiceName("hello"),
|
||||
withServiceRevisionName("hello-rev3"),
|
||||
withTrafficSplit([]string{"hello-rev1", "hello-rev2", "hello-rev3"}, []int{30, 30, 40}, []string{"", "", ""}),
|
||||
withServicePodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]corev1.EnvVar{{Name: "a", Value: "mouse"}, {Name: "b", Value: "cat"}}),
|
||||
),
|
||||
), getRevisionListWithOptions(
|
||||
withRevisions(
|
||||
withRevisionName("hello-rev1"),
|
||||
withRevisionAnnotations(
|
||||
map[string]string{
|
||||
"client.knative.dev/user-image": "gcr.io/knative-samples/helloworld-go",
|
||||
"serving.knative.dev/creator": "kubernetes-admin",
|
||||
}),
|
||||
withRevisionLabels(
|
||||
map[string]string{
|
||||
"serving.knative.dev/configuration": "hello",
|
||||
"serving.knative.dev/configurationGeneration": "1",
|
||||
"serving.knative.dev/route": "hello",
|
||||
"serving.knative.dev/service": "hello",
|
||||
}),
|
||||
withRevisionPodSpecOption(
|
||||
withContainer(),
|
||||
),
|
||||
),
|
||||
withRevisions(
|
||||
withRevisionName("hello-rev2"),
|
||||
withRevisionAnnotations(
|
||||
map[string]string{
|
||||
"serving.knative.dev/creator": "kubernetes-admin",
|
||||
}),
|
||||
withRevisionLabels(
|
||||
map[string]string{
|
||||
"serving.knative.dev/configuration": "hello",
|
||||
"serving.knative.dev/configurationGeneration": "2",
|
||||
"serving.knative.dev/route": "hello",
|
||||
"serving.knative.dev/service": "hello",
|
||||
}),
|
||||
withRevisionPodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]corev1.EnvVar{{Name: "a", Value: "mouse"}}),
|
||||
),
|
||||
),
|
||||
), "--with-revisions", "--mode", "resources", "-o", "yaml")
|
||||
|
||||
t.Log("send all traffic to revision 2")
|
||||
serviceUpdateWithOptions(r, "hello", "--traffic", "hello-rev2=100")
|
||||
|
||||
t.Log("export kubernetes-resources - all traffic to revision 2")
|
||||
serviceExportWithServiceList(r, "hello", getServiceListWithOptions(
|
||||
withServices(
|
||||
withServiceName("hello"),
|
||||
withServiceRevisionName("hello-rev2"),
|
||||
withServicePodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]corev1.EnvVar{{Name: "a", Value: "mouse"}}),
|
||||
),
|
||||
),
|
||||
withServices(
|
||||
withServiceName("hello"),
|
||||
withServiceRevisionName("hello-rev3"),
|
||||
withTrafficSplit([]string{"hello-rev2"}, []int{100}, []string{""}),
|
||||
withServicePodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]corev1.EnvVar{{Name: "a", Value: "mouse"}, {Name: "b", Value: "cat"}}),
|
||||
),
|
||||
),
|
||||
), "--with-revisions", "--mode", "kubernetes", "-o", "yaml")
|
||||
|
||||
t.Log("export revisions-only - all traffic to revision 2")
|
||||
serviceExportWithRevisionList(r, "hello", getServiceWithOptions(
|
||||
withServiceName("hello"),
|
||||
withServiceRevisionName("hello-rev3"),
|
||||
withTrafficSplit([]string{"hello-rev2"}, []int{100}, []string{""}),
|
||||
withServicePodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]corev1.EnvVar{{Name: "a", Value: "mouse"}, {Name: "b", Value: "cat"}}),
|
||||
),
|
||||
), getRevisionListWithOptions(
|
||||
withRevisions(
|
||||
withRevisionName("hello-rev2"),
|
||||
withRevisionAnnotations(
|
||||
map[string]string{
|
||||
"serving.knative.dev/creator": "kubernetes-admin",
|
||||
}),
|
||||
withRevisionLabels(
|
||||
map[string]string{
|
||||
"serving.knative.dev/configuration": "hello",
|
||||
"serving.knative.dev/configurationGeneration": "2",
|
||||
"serving.knative.dev/route": "hello",
|
||||
"serving.knative.dev/service": "hello",
|
||||
}),
|
||||
withRevisionPodSpecOption(
|
||||
withContainer(),
|
||||
withEnv([]corev1.EnvVar{{Name: "a", Value: "mouse"}}),
|
||||
),
|
||||
),
|
||||
), "--with-revisions", "--mode", "resources", "-o", "yaml")
|
||||
}
|
||||
|
||||
// Private methods
|
||||
|
|
@ -78,7 +296,7 @@ func serviceExport(r *test.KnRunResultCollector, serviceName string, expService
|
|||
r.AssertNoError(out)
|
||||
}
|
||||
|
||||
func serviceExportWithRevisions(r *test.KnRunResultCollector, serviceName string, expServiceList servingv1.ServiceList, options ...string) {
|
||||
func serviceExportWithServiceList(r *test.KnRunResultCollector, serviceName string, expServiceList servingv1.ServiceList, options ...string) {
|
||||
command := []string{"service", "export", serviceName}
|
||||
command = append(command, options...)
|
||||
out := r.KnTest().Kn().Run(command...)
|
||||
|
|
@ -86,166 +304,131 @@ func serviceExportWithRevisions(r *test.KnRunResultCollector, serviceName string
|
|||
r.AssertNoError(out)
|
||||
}
|
||||
|
||||
func serviceExportWithRevisionList(r *test.KnRunResultCollector, serviceName string, expService servingv1.Service, expRevisionList servingv1.RevisionList, options ...string) {
|
||||
command := []string{"service", "export", serviceName}
|
||||
command = append(command, options...)
|
||||
out := r.KnTest().Kn().Run(command...)
|
||||
validateExportedServiceandRevisionList(r.T(), r.KnTest(), out.Stdout, expService, expRevisionList)
|
||||
r.AssertNoError(out)
|
||||
}
|
||||
|
||||
// Private functions
|
||||
|
||||
func validateExportedService(t *testing.T, it *test.KnTest, out string, expService servingv1.Service) {
|
||||
actSvcJSON := servingv1.Service{}
|
||||
err := json.Unmarshal([]byte(out), &actSvcJSON)
|
||||
actSvc := servingv1.Service{}
|
||||
err := json.Unmarshal([]byte(out), &actSvc)
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, &expService, &actSvcJSON)
|
||||
assert.DeepEqual(t, &expService, &actSvc)
|
||||
}
|
||||
|
||||
func validateExportedServiceList(t *testing.T, it *test.KnTest, out string, expServiceList servingv1.ServiceList) {
|
||||
actYaml := servingv1.ServiceList{}
|
||||
err := yaml.Unmarshal([]byte(out), &actYaml)
|
||||
actSvcList := servingv1.ServiceList{}
|
||||
err := yaml.Unmarshal([]byte(out), &actSvcList)
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, &expServiceList, &actYaml)
|
||||
assert.DeepEqual(t, &expServiceList, &actSvcList)
|
||||
}
|
||||
|
||||
func getSvc(options ...expectedServiceOption) servingv1.Service {
|
||||
func validateExportedServiceandRevisionList(t *testing.T, it *test.KnTest, out string, expService servingv1.Service, expRevisionList servingv1.RevisionList) {
|
||||
outArray := strings.Split(out, "apiVersion: v1")
|
||||
|
||||
actSvc := servingv1.Service{}
|
||||
err := yaml.Unmarshal([]byte(outArray[0]), &actSvc)
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, &expService, &actSvc)
|
||||
|
||||
if len(outArray) > 1 {
|
||||
revListBuilder := strings.Builder{}
|
||||
revListBuilder.WriteString("apiVersion: v1")
|
||||
revListBuilder.WriteString("\n")
|
||||
revListBuilder.WriteString(outArray[1])
|
||||
actRevList := servingv1.RevisionList{}
|
||||
err := yaml.Unmarshal([]byte(revListBuilder.String()), &actRevList)
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, &actRevList, &actRevList)
|
||||
} else if len(expRevisionList.Items) > 0 {
|
||||
t.Errorf("expecting a revision list and got no list")
|
||||
}
|
||||
}
|
||||
|
||||
func getServiceListWithOptions(options ...expectedServiceListOption) servingv1.ServiceList {
|
||||
list := servingv1.ServiceList{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "List",
|
||||
},
|
||||
}
|
||||
|
||||
for _, fn := range options {
|
||||
fn(&list)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func withServices(options ...expectedServiceOption) expectedServiceListOption {
|
||||
return func(list *servingv1.ServiceList) {
|
||||
list.Items = append(list.Items, getServiceWithOptions(options...))
|
||||
}
|
||||
}
|
||||
|
||||
func getRevisionListWithOptions(options ...expectedRevisionListOption) servingv1.RevisionList {
|
||||
list := servingv1.RevisionList{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "List",
|
||||
},
|
||||
}
|
||||
|
||||
for _, fn := range options {
|
||||
fn(&list)
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
func withRevisions(options ...expectedRevisionOption) expectedRevisionListOption {
|
||||
return func(list *servingv1.RevisionList) {
|
||||
list.Items = append(list.Items, getRevisionWithOptions(options...))
|
||||
}
|
||||
}
|
||||
|
||||
func getServiceWithOptions(options ...expectedServiceOption) servingv1.Service {
|
||||
svc := servingv1.Service{
|
||||
Spec: servingv1.ServiceSpec{
|
||||
ConfigurationSpec: servingv1.ConfigurationSpec{
|
||||
Template: servingv1.RevisionTemplateSpec{
|
||||
Spec: servingv1.RevisionSpec{
|
||||
ContainerConcurrency: ptr.Int64(int64(0)),
|
||||
TimeoutSeconds: ptr.Int64(int64(300)),
|
||||
PodSpec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "user-container",
|
||||
Image: test.KnDefaultTestImage,
|
||||
Resources: corev1.ResourceRequirements{},
|
||||
ReadinessProbe: &corev1.Probe{
|
||||
SuccessThreshold: int32(1),
|
||||
Handler: corev1.Handler{
|
||||
TCPSocket: &corev1.TCPSocketAction{
|
||||
Port: intstr.FromInt(0),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Service",
|
||||
APIVersion: "serving.knative.dev/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, fn := range options {
|
||||
fn(&svc)
|
||||
}
|
||||
svc.Spec.Template.Spec.ContainerConcurrency = ptr.Int64(int64(0))
|
||||
svc.Spec.Template.Spec.TimeoutSeconds = ptr.Int64(int64(300))
|
||||
|
||||
return svc
|
||||
}
|
||||
|
||||
func getSvcListWOTags() servingv1.ServiceList {
|
||||
return servingv1.ServiceList{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "List",
|
||||
},
|
||||
Items: []servingv1.Service{
|
||||
getSvc(
|
||||
withName("hello"),
|
||||
withRevisionName("hello-rev1"),
|
||||
),
|
||||
getSvc(
|
||||
withName("hello"),
|
||||
withRevisionName("hello-rev2"),
|
||||
withEnv("key1", "val1"),
|
||||
),
|
||||
getSvc(
|
||||
withName("hello"),
|
||||
withRevisionName("hello-rev3"),
|
||||
withEnv("key1", "val1"), withEnv("key2", "val2"),
|
||||
withTrafficSplit([]string{"hello-rev1", "hello-rev2", "hello-rev3"}, []int{30, 30, 40}, []string{"", "", ""}),
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getSvcListWithTags() servingv1.ServiceList {
|
||||
return servingv1.ServiceList{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "List",
|
||||
},
|
||||
Items: []servingv1.Service{
|
||||
getSvc(
|
||||
withName("hello"),
|
||||
withRevisionName("hello-rev1"),
|
||||
),
|
||||
getSvc(
|
||||
withName("hello"),
|
||||
withRevisionName("hello-rev2"),
|
||||
withEnv("key1", "val1"),
|
||||
withTrafficSplit([]string{"latest", "hello-rev1"}, []int{98, 2}, []string{"", "candidate"}),
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getSvcListWithOneRevision() servingv1.ServiceList {
|
||||
return servingv1.ServiceList{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "List",
|
||||
},
|
||||
Items: []servingv1.Service{
|
||||
getSvc(
|
||||
withName("hello"),
|
||||
withRevisionName("hello-rev2"),
|
||||
withEnv("key1", "val1"),
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func withRevisionName(revName string) expectedServiceOption {
|
||||
return func(svc *servingv1.Service) {
|
||||
svc.Spec.ConfigurationSpec.Template.ObjectMeta.Name = revName
|
||||
}
|
||||
}
|
||||
|
||||
func withAnnotations() expectedServiceOption {
|
||||
return func(svc *servingv1.Service) {
|
||||
svc.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations = map[string]string{
|
||||
"client.knative.dev/user-image": "gcr.io/knative-samples/helloworld-go",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func withName(name string) expectedServiceOption {
|
||||
func withServiceName(name string) expectedServiceOption {
|
||||
return func(svc *servingv1.Service) {
|
||||
svc.ObjectMeta.Name = name
|
||||
}
|
||||
}
|
||||
|
||||
func withEnv(key string, val string) expectedServiceOption {
|
||||
func withConfigurationLabels(labels map[string]string) expectedServiceOption {
|
||||
return func(svc *servingv1.Service) {
|
||||
env := []corev1.EnvVar{
|
||||
{
|
||||
Name: key,
|
||||
Value: val,
|
||||
},
|
||||
}
|
||||
currentEnv := svc.Spec.ConfigurationSpec.Template.Spec.PodSpec.Containers[0].Env
|
||||
if len(currentEnv) > 0 {
|
||||
svc.Spec.ConfigurationSpec.Template.Spec.PodSpec.Containers[0].Env = append(currentEnv, env...)
|
||||
} else {
|
||||
svc.Spec.ConfigurationSpec.Template.Spec.PodSpec.Containers[0].Env = env
|
||||
}
|
||||
|
||||
svc.Spec.Template.ObjectMeta.Labels = labels
|
||||
}
|
||||
}
|
||||
func withConfigurationAnnotations() expectedServiceOption {
|
||||
return func(svc *servingv1.Service) {
|
||||
svc.Spec.Template.ObjectMeta.Annotations = map[string]string{
|
||||
"client.knative.dev/user-image": "gcr.io/knative-samples/helloworld-go",
|
||||
}
|
||||
}
|
||||
}
|
||||
func withServiceRevisionName(name string) expectedServiceOption {
|
||||
return func(svc *servingv1.Service) {
|
||||
svc.Spec.Template.ObjectMeta.Name = name
|
||||
}
|
||||
}
|
||||
|
||||
func withTrafficSplit(revisions []string, percentages []int, tags []string) expectedServiceOption {
|
||||
return func(svc *servingv1.Service) {
|
||||
var trafficTargets []servingv1.TrafficTarget
|
||||
|
|
@ -268,3 +451,76 @@ func withTrafficSplit(revisions []string, percentages []int, tags []string) expe
|
|||
}
|
||||
}
|
||||
}
|
||||
func withServicePodSpecOption(options ...podSpecOption) expectedServiceOption {
|
||||
return func(svc *servingv1.Service) {
|
||||
svc.Spec.Template.Spec.PodSpec = getPodSpecWithOptions(options...)
|
||||
}
|
||||
}
|
||||
func getRevisionWithOptions(options ...expectedRevisionOption) servingv1.Revision {
|
||||
rev := servingv1.Revision{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Revision",
|
||||
APIVersion: "serving.knative.dev/v1",
|
||||
},
|
||||
}
|
||||
for _, fn := range options {
|
||||
fn(&rev)
|
||||
}
|
||||
rev.Spec.ContainerConcurrency = ptr.Int64(int64(0))
|
||||
rev.Spec.TimeoutSeconds = ptr.Int64(int64(300))
|
||||
return rev
|
||||
}
|
||||
func withRevisionName(name string) expectedRevisionOption {
|
||||
return func(rev *servingv1.Revision) {
|
||||
rev.ObjectMeta.Name = name
|
||||
}
|
||||
}
|
||||
func withRevisionLabels(labels map[string]string) expectedRevisionOption {
|
||||
return func(rev *servingv1.Revision) {
|
||||
rev.ObjectMeta.Labels = labels
|
||||
}
|
||||
}
|
||||
func withRevisionAnnotations(Annotations map[string]string) expectedRevisionOption {
|
||||
return func(rev *servingv1.Revision) {
|
||||
rev.ObjectMeta.Annotations = Annotations
|
||||
}
|
||||
}
|
||||
func withRevisionPodSpecOption(options ...podSpecOption) expectedRevisionOption {
|
||||
return func(rev *servingv1.Revision) {
|
||||
rev.Spec.PodSpec = getPodSpecWithOptions(options...)
|
||||
}
|
||||
}
|
||||
|
||||
func getPodSpecWithOptions(options ...podSpecOption) corev1.PodSpec {
|
||||
spec := corev1.PodSpec{}
|
||||
for _, fn := range options {
|
||||
fn(&spec)
|
||||
}
|
||||
return spec
|
||||
}
|
||||
|
||||
func withEnv(env []corev1.EnvVar) podSpecOption {
|
||||
return func(spec *corev1.PodSpec) {
|
||||
spec.Containers[0].Env = env
|
||||
}
|
||||
}
|
||||
|
||||
func withContainer() podSpecOption {
|
||||
return func(spec *corev1.PodSpec) {
|
||||
spec.Containers = []corev1.Container{
|
||||
{
|
||||
Name: "user-container",
|
||||
Image: test.KnDefaultTestImage,
|
||||
Resources: corev1.ResourceRequirements{},
|
||||
ReadinessProbe: &corev1.Probe{
|
||||
SuccessThreshold: int32(1),
|
||||
Handler: corev1.Handler{
|
||||
TCPSocket: &corev1.TCPSocketAction{
|
||||
Port: intstr.FromInt(0),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue