mirror of https://github.com/knative/client.git
* add kn export (#653) * add kn export (#653) * review comments for #669 * add kn export command * add kn export command * add kn export command * add kn export command * add changelog for pr 679 * add changelog * review comments for pr #669 * review comments for pr #669 * review comments for kn export
This commit is contained in:
parent
ba7e14c807
commit
5c794d3b04
|
@ -81,6 +81,10 @@
|
|||
| 🎁
|
||||
| Add `--user` flag for specifying the user id to run the container
|
||||
| https://github.com/knative/client/pull/679[#679]
|
||||
|
||||
| 🎁
|
||||
| add kn service export command for exporting a service
|
||||
| https://github.com/knative/client/pull/669[#669]
|
||||
|===
|
||||
|
||||
## v0.12.0 (2020-01-29)
|
||||
|
|
|
@ -30,6 +30,7 @@ kn service [flags]
|
|||
* [kn service create](kn_service_create.md) - Create a service.
|
||||
* [kn service delete](kn_service_delete.md) - Delete a service.
|
||||
* [kn service describe](kn_service_describe.md) - Show details of a service
|
||||
* [kn service export](kn_service_export.md) - export a service
|
||||
* [kn service list](kn_service_list.md) - List available services.
|
||||
* [kn service update](kn_service_update.md) - Update a service.
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
## kn service export
|
||||
|
||||
export a service
|
||||
|
||||
### Synopsis
|
||||
|
||||
export a service
|
||||
|
||||
```
|
||||
kn service export NAME [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
|
||||
# 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
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
--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
|
||||
-r, --history Export all active revisions
|
||||
-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].
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--config string kn config file (default is ~/.config/kn/config.yaml)
|
||||
--kubeconfig string kubectl config file (default is ~/.kube/config)
|
||||
--log-http log http traffic
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [kn service](kn_service.md) - Service command group
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
// 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
|
||||
"knative.dev/client/pkg/kn/commands"
|
||||
clientservingv1 "knative.dev/client/pkg/serving/v1"
|
||||
"knative.dev/serving/pkg/apis/serving"
|
||||
servingv1 "knative.dev/serving/pkg/apis/serving/v1"
|
||||
)
|
||||
|
||||
// NewServiceExportCommand returns a new command for exporting a service.
|
||||
func NewServiceExportCommand(p *commands.KnParams) *cobra.Command {
|
||||
|
||||
// For machine readable output
|
||||
machineReadablePrintFlags := genericclioptions.NewPrintFlags("")
|
||||
|
||||
command := &cobra.Command{
|
||||
Use: "export NAME",
|
||||
Short: "export a service",
|
||||
Example: `
|
||||
# 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`,
|
||||
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")
|
||||
}
|
||||
if !machineReadablePrintFlags.OutputFlagSpecified() {
|
||||
return errors.New("'kn service export' requires output format")
|
||||
}
|
||||
serviceName := args[0]
|
||||
|
||||
namespace, err := p.GetNamespace(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := p.NewServingClient(namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service, err := client.GetService(serviceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
history, err := cmd.Flags().GetBool("history")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printer, err := machineReadablePrintFlags.ToPrinter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if history {
|
||||
if svcList, err := exportServicewithActiveRevisions(service, client); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return printer.PrintObj(svcList, cmd.OutOrStdout())
|
||||
}
|
||||
}
|
||||
return printer.PrintObj(exportService(service), cmd.OutOrStdout())
|
||||
},
|
||||
}
|
||||
flags := command.Flags()
|
||||
commands.AddNamespaceFlags(flags, false)
|
||||
flags.BoolP("history", "r", false, "Export all active revisions")
|
||||
machineReadablePrintFlags.AddFlags(command)
|
||||
return command
|
||||
}
|
||||
|
||||
func exportService(latestSvc *servingv1.Service) *servingv1.Service {
|
||||
|
||||
exportedSvc := servingv1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: latestSvc.ObjectMeta.Name,
|
||||
Labels: latestSvc.ObjectMeta.Labels,
|
||||
},
|
||||
TypeMeta: latestSvc.TypeMeta,
|
||||
}
|
||||
|
||||
exportedSvc.Spec.Template = servingv1.RevisionTemplateSpec{
|
||||
Spec: latestSvc.Spec.ConfigurationSpec.Template.Spec,
|
||||
ObjectMeta: latestSvc.Spec.ConfigurationSpec.Template.ObjectMeta,
|
||||
}
|
||||
|
||||
return &exportedSvc
|
||||
}
|
||||
|
||||
func constructServicefromRevision(latestSvc *servingv1.Service, revision servingv1.Revision) servingv1.Service {
|
||||
|
||||
exportedSvc := servingv1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: latestSvc.ObjectMeta.Name,
|
||||
Labels: latestSvc.ObjectMeta.Labels,
|
||||
},
|
||||
TypeMeta: latestSvc.TypeMeta,
|
||||
}
|
||||
|
||||
exportedSvc.Spec.Template = servingv1.RevisionTemplateSpec{
|
||||
Spec: revision.Spec,
|
||||
ObjectMeta: latestSvc.Spec.ConfigurationSpec.Template.ObjectMeta,
|
||||
}
|
||||
|
||||
exportedSvc.Spec.ConfigurationSpec.Template.ObjectMeta.Name = revision.ObjectMeta.Name
|
||||
|
||||
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)
|
||||
|
||||
var params []clientservingv1.ListConfig
|
||||
params = append(params, clientservingv1.WithService(latestSvc.ObjectMeta.Name))
|
||||
|
||||
// Query for list with filters
|
||||
revisionList, err := client.ListRevisions(params...)
|
||||
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)
|
||||
|
||||
for _, revision := range revisionList.Items {
|
||||
//construct service only for active revisions
|
||||
if revsMap[revision.ObjectMeta.Name] {
|
||||
exportedSvcItems = append(exportedSvcItems, constructServicefromRevision(latestSvc, revision))
|
||||
}
|
||||
}
|
||||
|
||||
//set traffic in the latest revision
|
||||
exportedSvcItems[len(exportedSvcItems)-1] = setTrafficSplit(latestSvc, exportedSvcItems[len(exportedSvcItems)-1])
|
||||
|
||||
typeMeta := metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "List",
|
||||
}
|
||||
exportedSvcList := &servingv1.ServiceList{
|
||||
TypeMeta: typeMeta,
|
||||
Items: exportedSvcItems,
|
||||
}
|
||||
|
||||
return exportedSvcList, nil
|
||||
}
|
||||
|
||||
func setTrafficSplit(latestSvc *servingv1.Service, exportedSvc servingv1.Service) servingv1.Service {
|
||||
|
||||
exportedSvc.Spec.RouteSpec = latestSvc.Spec.RouteSpec
|
||||
|
||||
return exportedSvc
|
||||
}
|
||||
|
||||
func getRevisionstoExport(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 {
|
||||
revsMap[traffic.RevisionName] = true
|
||||
}
|
||||
}
|
||||
return revsMap
|
||||
}
|
||||
|
||||
// sortRevisions sorts revisions by generation and name (in this order)
|
||||
func sortRevisions(revisionList *servingv1.RevisionList) {
|
||||
// sort revisionList by configuration generation key
|
||||
sort.SliceStable(revisionList.Items, revisionListSortFunc(revisionList))
|
||||
}
|
||||
|
||||
// revisionListSortFunc sorts by generation and name
|
||||
func revisionListSortFunc(revisionList *servingv1.RevisionList) func(i int, j int) bool {
|
||||
return func(i, j int) bool {
|
||||
a := revisionList.Items[i]
|
||||
b := revisionList.Items[j]
|
||||
|
||||
// By Generation
|
||||
// Convert configuration generation key from string to int for avoiding string comparison.
|
||||
agen, err := strconv.Atoi(a.Labels[serving.ConfigurationGenerationLabelKey])
|
||||
if err != nil {
|
||||
return a.Name > b.Name
|
||||
}
|
||||
bgen, err := strconv.Atoi(b.Labels[serving.ConfigurationGenerationLabelKey])
|
||||
if err != nil {
|
||||
return a.Name > b.Name
|
||||
}
|
||||
|
||||
if agen != bgen {
|
||||
return agen < bgen
|
||||
}
|
||||
return a.Name > b.Name
|
||||
}
|
||||
}
|
|
@ -0,0 +1,322 @@
|
|||
// 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 (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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"
|
||||
apiserving "knative.dev/serving/pkg/apis/serving"
|
||||
servingv1 "knative.dev/serving/pkg/apis/serving/v1"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
func TestServiceExport(t *testing.T) {
|
||||
var svcs []*servingv1.Service
|
||||
typeMeta := metav1.TypeMeta{
|
||||
Kind: "service",
|
||||
APIVersion: "serving.knative.dev/v1",
|
||||
}
|
||||
|
||||
// case 1 - plain svc
|
||||
plainService := getService("foo")
|
||||
svcs = append(svcs, plainService)
|
||||
|
||||
// case 2 - svc with env variables
|
||||
envSvc := getService("foo")
|
||||
envVars := []v1.EnvVar{
|
||||
{Name: "a", Value: "mouse"},
|
||||
{Name: "b", Value: "cookie"},
|
||||
{Name: "empty", Value: ""},
|
||||
}
|
||||
template := &envSvc.Spec.Template
|
||||
template.Spec.Containers[0].Env = envVars
|
||||
template.Spec.Containers[0].Image = "gcr.io/foo/bar:baz"
|
||||
template.Annotations = map[string]string{servinglib.UserImageAnnotationKey: "gcr.io/foo/bar:baz"}
|
||||
svcs = append(svcs, envSvc)
|
||||
|
||||
//case 3 - svc with labels
|
||||
labelService := getService("foo")
|
||||
expected := map[string]string{
|
||||
"a": "mouse",
|
||||
"b": "cookie",
|
||||
"empty": "",
|
||||
}
|
||||
labelService.Labels = expected
|
||||
labelService.Spec.Template.Annotations = map[string]string{
|
||||
servinglib.UserImageAnnotationKey: "gcr.io/foo/bar:baz",
|
||||
}
|
||||
template = &labelService.Spec.Template
|
||||
template.ObjectMeta.Labels = expected
|
||||
template.Spec.Containers[0].Image = "gcr.io/foo/bar:baz"
|
||||
svcs = append(svcs, labelService)
|
||||
|
||||
//case 4 - config map
|
||||
CMservice := getService("foo")
|
||||
template = &CMservice.Spec.Template
|
||||
template.Spec.Containers[0].EnvFrom = []v1.EnvFromSource{
|
||||
{
|
||||
ConfigMapRef: &v1.ConfigMapEnvSource{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: "config-map-name",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
template.Spec.Containers[0].Image = "gcr.io/foo/bar:baz"
|
||||
template.Annotations = map[string]string{servinglib.UserImageAnnotationKey: "gcr.io/foo/bar:baz"}
|
||||
svcs = append(svcs, CMservice)
|
||||
|
||||
//case 5 - volume mount and secrets
|
||||
Volservice := getService("foo")
|
||||
template = &Volservice.Spec.Template
|
||||
template.Spec.Volumes = []v1.Volume{
|
||||
{
|
||||
Name: "volume-name",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
Secret: &v1.SecretVolumeSource{
|
||||
SecretName: "secret-name",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
template.Spec.Containers[0].VolumeMounts = []v1.VolumeMount{
|
||||
{
|
||||
Name: "volume-name",
|
||||
MountPath: "/mount/path",
|
||||
ReadOnly: true,
|
||||
},
|
||||
}
|
||||
svcs = append(svcs, Volservice)
|
||||
|
||||
template.Spec.Containers[0].Image = "gcr.io/foo/bar:baz"
|
||||
template.Annotations = map[string]string{servinglib.UserImageAnnotationKey: "gcr.io/foo/bar:baz"}
|
||||
|
||||
for _, svc := range svcs {
|
||||
svc.TypeMeta = typeMeta
|
||||
callServiceExportTest(t, svc)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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")
|
||||
|
||||
assert.NilError(t, err)
|
||||
|
||||
expectedService.ObjectMeta.Namespace = ""
|
||||
|
||||
expSvcYaml, err := yaml.Marshal(expectedService)
|
||||
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Equal(t, string(expSvcYaml), output)
|
||||
|
||||
// Validate that all recorded API methods have been called
|
||||
r.Validate()
|
||||
}
|
||||
|
||||
func TestServiceExportwithMultipleRevisions(t *testing.T) {
|
||||
//case 1 = 2 revisions with traffic split
|
||||
trafficSplitService := createServiceTwoRevsionsWithTraffic("foo", true)
|
||||
|
||||
multiRevs := createTestRevisionList("rev", "foo")
|
||||
|
||||
callServiceExportHistoryTest(t, trafficSplitService, multiRevs)
|
||||
|
||||
//case 2 - same revisions no traffic split
|
||||
noTrafficSplitService := createServiceTwoRevsionsWithTraffic("foo", false)
|
||||
|
||||
callServiceExportHistoryTest(t, noTrafficSplitService, multiRevs)
|
||||
}
|
||||
|
||||
func callServiceExportHistoryTest(t *testing.T, expectedService *servingv1.Service, revs *servingv1.RevisionList) {
|
||||
// New mock client
|
||||
client := knclient.NewMockKnServiceClient(t)
|
||||
|
||||
// Recording:
|
||||
r := client.Recorder()
|
||||
|
||||
r.GetService(expectedService.ObjectMeta.Name, expectedService, nil)
|
||||
|
||||
r.ListRevisions(mock.Any(), revs, nil)
|
||||
|
||||
output, err := executeServiceCommand(client, "export", expectedService.ObjectMeta.Name, "-r", "-o", "json")
|
||||
|
||||
assert.NilError(t, err)
|
||||
|
||||
actSvcList := servingv1.ServiceList{}
|
||||
|
||||
json.Unmarshal([]byte(output), &actSvcList)
|
||||
|
||||
for i, actSvc := range actSvcList.Items {
|
||||
var checkTraffic bool
|
||||
if i == (len(actSvcList.Items) - 1) {
|
||||
checkTraffic = true
|
||||
}
|
||||
validateServiceWithRevisionHistory(t, expectedService, revs, actSvc, checkTraffic)
|
||||
}
|
||||
|
||||
// Validate that all recorded API methods have been called
|
||||
r.Validate()
|
||||
}
|
||||
|
||||
func validateServiceWithRevisionHistory(t *testing.T, expectedsvc *servingv1.Service, expectedRevList *servingv1.RevisionList, actualSvc servingv1.Service, checkTraffic bool) {
|
||||
var expectedRev servingv1.Revision
|
||||
var routeSpec servingv1.RouteSpec
|
||||
for _, rev := range expectedRevList.Items {
|
||||
if actualSvc.Spec.ConfigurationSpec.Template.ObjectMeta.Name == rev.ObjectMeta.Name {
|
||||
expectedRev = rev
|
||||
break
|
||||
}
|
||||
}
|
||||
expectedsvc.Spec.ConfigurationSpec.Template.ObjectMeta.Name = expectedRev.ObjectMeta.Name
|
||||
expectedsvc.Spec.Template.Spec = expectedRev.Spec
|
||||
|
||||
stripExpectedSvcVariables(expectedsvc)
|
||||
|
||||
if !checkTraffic {
|
||||
routeSpec = expectedsvc.Spec.RouteSpec
|
||||
expectedsvc.Spec.RouteSpec = servingv1.RouteSpec{}
|
||||
}
|
||||
assert.DeepEqual(t, expectedsvc, &actualSvc)
|
||||
|
||||
expectedsvc.Spec.RouteSpec = routeSpec
|
||||
}
|
||||
|
||||
func TestServiceExportError(t *testing.T) {
|
||||
// New mock client
|
||||
client := knclient.NewMockKnServiceClient(t)
|
||||
|
||||
expectedService := getService("foo")
|
||||
|
||||
_, err := executeServiceCommand(client, "export", expectedService.ObjectMeta.Name)
|
||||
|
||||
assert.Error(t, err, "'kn service export' requires output format")
|
||||
}
|
||||
|
||||
func createTestRevisionList(revision string, service string) *servingv1.RevisionList {
|
||||
labels1 := make(map[string]string)
|
||||
labels1[apiserving.ConfigurationGenerationLabelKey] = "1"
|
||||
labels1[apiserving.ServiceLabelKey] = service
|
||||
|
||||
labels2 := make(map[string]string)
|
||||
labels2[apiserving.ConfigurationGenerationLabelKey] = "2"
|
||||
labels2[apiserving.ServiceLabelKey] = service
|
||||
|
||||
rev1 := servingv1.Revision{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Revision",
|
||||
APIVersion: "serving.knative.dev/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("%s-%s-%d", service, revision, 1),
|
||||
Namespace: "default",
|
||||
Generation: int64(1),
|
||||
Labels: labels1,
|
||||
},
|
||||
Spec: servingv1.RevisionSpec{
|
||||
PodSpec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Image: "gcr.io/test/image:v1",
|
||||
Env: []v1.EnvVar{
|
||||
{Name: "env1", Value: "eval1"},
|
||||
{Name: "env2", Value: "eval2"},
|
||||
},
|
||||
EnvFrom: []v1.EnvFromSource{
|
||||
{ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: "test1"}}},
|
||||
{ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: "test2"}}},
|
||||
},
|
||||
Ports: []v1.ContainerPort{
|
||||
{ContainerPort: 8080},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
rev2 := rev1
|
||||
|
||||
rev2.Spec.PodSpec.Containers[0].Image = "gcr.io/test/image:v2"
|
||||
rev2.ObjectMeta.Labels = labels2
|
||||
rev2.ObjectMeta.Generation = int64(2)
|
||||
rev2.ObjectMeta.Name = fmt.Sprintf("%s-%s-%d", service, revision, 2)
|
||||
|
||||
typeMeta := metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "List",
|
||||
}
|
||||
|
||||
return &servingv1.RevisionList{
|
||||
TypeMeta: typeMeta,
|
||||
Items: []servingv1.Revision{rev1, rev2},
|
||||
}
|
||||
}
|
||||
|
||||
func createServiceTwoRevsionsWithTraffic(svc string, trafficSplit bool) *servingv1.Service {
|
||||
expectedService := createTestService(svc, []string{svc + "-rev-1", svc + "-rev-2"}, goodConditions())
|
||||
expectedService.Status.Traffic[0].LatestRevision = ptr.Bool(true)
|
||||
expectedService.Status.Traffic[0].Tag = "latest"
|
||||
expectedService.Status.Traffic[1].Tag = "current"
|
||||
|
||||
if trafficSplit {
|
||||
trafficList := []servingv1.TrafficTarget{
|
||||
{
|
||||
RevisionName: "foo-rev-1",
|
||||
Percent: ptr.Int64(int64(50)),
|
||||
}, {
|
||||
RevisionName: "foo-rev-2",
|
||||
Percent: ptr.Int64(int64(50)),
|
||||
}}
|
||||
expectedService.Spec.RouteSpec = servingv1.RouteSpec{Traffic: trafficList}
|
||||
} else {
|
||||
trafficList := []servingv1.TrafficTarget{
|
||||
{
|
||||
RevisionName: "foo-rev-2",
|
||||
Percent: ptr.Int64(int64(50)),
|
||||
}}
|
||||
expectedService.Spec.RouteSpec = servingv1.RouteSpec{Traffic: trafficList}
|
||||
}
|
||||
|
||||
return &expectedService
|
||||
}
|
||||
|
||||
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{}
|
||||
}
|
|
@ -41,6 +41,7 @@ func NewServiceCommand(p *commands.KnParams) *cobra.Command {
|
|||
serviceCmd.AddCommand(NewServiceCreateCommand(p))
|
||||
serviceCmd.AddCommand(NewServiceDeleteCommand(p))
|
||||
serviceCmd.AddCommand(NewServiceUpdateCommand(p))
|
||||
serviceCmd.AddCommand(NewServiceExportCommand(p))
|
||||
return serviceCmd
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue