// Copyright © 2019 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 ( "fmt" "strconv" "strings" "testing" "time" "gotest.tools/assert" "gotest.tools/assert/cmp" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "knative.dev/pkg/apis" duckv1 "knative.dev/pkg/apis/duck/v1" "knative.dev/serving/pkg/apis/autoscaling" api_serving "knative.dev/serving/pkg/apis/serving" servingv1 "knative.dev/serving/pkg/apis/serving/v1" client_serving "knative.dev/client/pkg/serving" knclient "knative.dev/client/pkg/serving/v1" "knative.dev/client/pkg/util" "knative.dev/pkg/ptr" ) const ( imageDigest = "sha256:1234567890123456789012345678901234567890123456789012345678901234" ) func TestServiceDescribeBasic(t *testing.T) { // New mock client client := knclient.NewMockKnServiceClient(t) // Recording: r := client.Recorder() // Prepare service expectedService := createTestServiceWithServiceAccount("foo", []string{"rev1"}, "default-sa", goodConditions()) // Get service & revision r.GetService("foo", &expectedService, nil) rev1 := createTestRevision("rev1", 1, goodConditions()) r.GetRevision("rev1", &rev1, nil) // Testing: output, err := executeServiceCommand(client, "describe", "foo") assert.NilError(t, err) validateServiceOutput(t, "foo", output) assert.Assert(t, util.ContainsAll(output, "123456")) assert.Assert(t, util.ContainsAll(output, "Annotations:", "anno1=aval1, anno2=aval2, anno3=")) assert.Assert(t, cmp.Regexp(`(?m)\s*Annotations:.*\.\.\.$`, output)) assert.Assert(t, util.ContainsAll(output, "Labels:", "label1=lval1, label2=lval2\n")) assert.Assert(t, util.ContainsAll(output, "[1]")) assert.Assert(t, cmp.Regexp("Service Account: \\s+default-sa", output)) assert.Equal(t, strings.Count(output, "rev1"), 1) // Validate that all recorded API methods have been called r.Validate() } func TestServiceDescribeWithMultipleNames(t *testing.T) { client := knclient.NewMockKnServiceClient(t) r := client.Recorder() createTestService("foo", []string{"rev1"}, goodConditions()) _, err := executeServiceCommand(client, "describe", "foo", "foo1") assert.Assert(t, util.ContainsAll(err.Error(), "'service describe' requires the service name given as single argument")) r.Validate() } func TestServiceDescribeSad(t *testing.T) { client := knclient.NewMockKnServiceClient(t) r := client.Recorder() expectedService := createTestService("foo", []string{"rev1"}, goodConditions()) expectedService.Status.Conditions[0].Status = v1.ConditionFalse r.GetService("foo", &expectedService, nil) rev1 := createTestRevision("rev1", 1, goodConditions()) r.GetRevision("rev1", &rev1, nil) output, err := executeServiceCommand(client, "describe", "foo") assert.NilError(t, err) validateServiceOutput(t, "foo", output) assert.Assert(t, util.ContainsAll(output, "!!", "Ready")) r.Validate() } func TestServiceDescribeLatest(t *testing.T) { // New mock client client := knclient.NewMockKnServiceClient(t) r := client.Recorder() expectedService := createTestService("foo", []string{"rev1"}, goodConditions()) expectedService.Status.Traffic[0].LatestRevision = ptr.Bool(true) // Get service & revision r.GetService("foo", &expectedService, nil) rev1 := createTestRevision("rev1", 1, goodConditions()) r.GetRevision("rev1", &rev1, nil) output, err := executeServiceCommand(client, "describe", "foo") assert.NilError(t, err) validateServiceOutput(t, "foo", output) assert.Assert(t, util.ContainsAll(output, "@latest (rev1)")) // Validate that all recorded API methods have been called r.Validate() } func TestServiceDescribeLatestNotInTraffic(t *testing.T) { // New mock client client := knclient.NewMockKnServiceClient(t) // Recording: r := client.Recorder() // Prepare service expectedService := createTestService("foo", []string{"rev1", "rev2"}, goodConditions()) expectedService.Status.Traffic = expectedService.Status.Traffic[:1] expectedService.Status.Traffic[0].LatestRevision = ptr.Bool(false) expectedService.Status.Traffic[0].Percent = ptr.Int64(int64(100)) // Get service & revision r.GetService("foo", &expectedService, nil) rev1 := createTestRevision("rev1", 1, goodConditions()) rev2 := createTestRevision("rev2", 2, goodConditions()) r.GetRevision("rev1", &rev1, nil) r.GetRevision("rev2", &rev2, nil) // Testing: output, err := executeServiceCommand(client, "describe", "foo") assert.NilError(t, err) validateServiceOutput(t, "foo", output) assert.Assert(t, util.ContainsAll(output, "rev2 (current @latest)")) // Validate that all recorded API methods have been called r.Validate() } func TestServiceDescribeEachNamedOnce(t *testing.T) { // New mock client client := knclient.NewMockKnServiceClient(t) // Recording: r := client.Recorder() // Prepare service expectedService := createTestService("foo", []string{"rev1", "rev2"}, goodConditions()) expectedService.Status.Traffic = expectedService.Status.Traffic[:1] expectedService.Status.Traffic[0].LatestRevision = ptr.Bool(false) expectedService.Status.Traffic[0].Percent = ptr.Int64(int64(100)) // Get service & revision r.GetService("foo", &expectedService, nil) rev1 := createTestRevision("rev1", 1, goodConditions()) rev2 := createTestRevision("rev2", 2, goodConditions()) r.GetRevision("rev1", &rev1, nil) r.GetRevision("rev2", &rev2, nil) // Testing: output, err := executeServiceCommand(client, "describe", "foo") assert.NilError(t, err) validateServiceOutput(t, "foo", output) assert.Assert(t, util.ContainsAll(output, "rev1", "rev2")) assert.Equal(t, strings.Count(output, "rev2"), 1) assert.Equal(t, strings.Count(output, "rev1"), 1) // Validate that all recorded API methods have been called r.Validate() } func TestServiceDescribeLatestAndCurrentBothHaveTrafficEntries(t *testing.T) { // New mock client client := knclient.NewMockKnServiceClient(t) // Recording: r := client.Recorder() // Prepare service expectedService := createTestService("foo", []string{"rev1", "rev1"}, goodConditions()) expectedService.Status.Traffic[0].LatestRevision = ptr.Bool(true) expectedService.Status.Traffic[0].Tag = "latest" expectedService.Status.Traffic[1].Tag = "current" // Get service & revision r.GetService("foo", &expectedService, nil) rev1 := createTestRevision("rev1", 1, goodConditions()) r.GetRevision("rev1", &rev1, nil) r.GetRevision("rev1", &rev1, nil) // Testing: output, err := executeServiceCommand(client, "describe", "foo") assert.NilError(t, err) validateServiceOutput(t, "foo", output) assert.Assert(t, util.ContainsAll(output, "@latest (rev1) #latest", "rev1 (current @latest) #current", "50%")) // Validate that all recorded API methods have been called r.Validate() } func TestServiceDescribeLatestCreatedIsBroken(t *testing.T) { // New mock client client := knclient.NewMockKnServiceClient(t) // Recording: r := client.Recorder() // Prepare service expectedService := createTestService("foo", []string{"rev1"}, goodConditions()) expectedService.Status.Traffic[0].LatestRevision = ptr.Bool(true) expectedService.Status.LatestCreatedRevisionName = "rev2" // Get service & revision r.GetService("foo", &expectedService, nil) rev1 := createTestRevision("rev1", 1, goodConditions()) rev2 := createTestRevision("rev2", 2, goodConditions()) rev2.Status.Conditions[0].Status = v1.ConditionFalse r.GetRevision("rev1", &rev1, nil) r.GetRevision("rev2", &rev2, nil) // Testing: output, err := executeServiceCommand(client, "describe", "foo") assert.NilError(t, err) validateServiceOutput(t, "foo", output) assert.Assert(t, util.ContainsAll(output, "!", "rev2", "100%", "@latest (rev1)")) // Validate that all recorded API methods have been called r.Validate() } func TestServiceDescribeScaling(t *testing.T) { for _, data := range []struct { minScale, maxScale, limit, target string scaleOut, utilization string }{ {"", "", "", "", "", ""}, {"", "10", "", "", "0 ... 10", ""}, {"10", "", "", "", "10 ... ∞", ""}, {"5", "20", "10", "", "5 ... 20", ""}, {"", "", "20", "30", "", "50"}, } { // New mock client client := knclient.NewMockKnServiceClient(t) // Recording: r := client.Recorder() // Prepare service expectedService := createTestService("foo", []string{"rev1"}, goodConditions()) // Get service & revision r.GetService("foo", &expectedService, nil) rev1 := createTestRevision("rev1", 1, goodConditions()) addScaling(&rev1, data.minScale, data.maxScale, data.target, data.limit, data.utilization) r.GetRevision("rev1", &rev1, nil) revList := servingv1.RevisionList{ TypeMeta: metav1.TypeMeta{ Kind: "RevisionList", APIVersion: "serving.knative.dev/v1", }, Items: []servingv1.Revision{ rev1, }, } // Return the list of all revisions r.ListRevisions(knclient.HasLabelSelector(api_serving.ServiceLabelKey, "foo"), &revList, nil) // Testing: output, err := executeServiceCommand(client, "describe", "foo", "--verbose") assert.NilError(t, err) validateServiceOutput(t, "foo", output) if data.limit != "" || data.target != "" { assert.Assert(t, util.ContainsAll(output, "Concurrency:")) } else { assert.Assert(t, !strings.Contains(output, "Concurrency:")) } assert.Assert(t, cmp.Regexp("Cluster:\\s+https://foo.default.svc.cluster.local", output)) validateOutputLine(t, output, "Scale", data.scaleOut) validateOutputLine(t, output, "Limit", data.limit) validateOutputLine(t, output, "Target", data.target) // Validate that all recorded API methods have been called r.Validate() } } func validateOutputLine(t *testing.T, output string, label string, value string) { if value != "" { assert.Assert(t, cmp.Regexp(fmt.Sprintf("%s:\\s*%s", label, value), output)) } else { assert.Assert(t, !strings.Contains(output, label+":")) } } func TestServiceDescribeResources(t *testing.T) { for _, data := range []struct { reqMem, limitMem, reqCPU, limitCPU string memoryOut, cpuOut string }{ {"", "", "", "", "", ""}, {"10Mi", "100Mi", "100m", "1", "10Mi ... 100Mi", "100m ... 1"}, {"", "100Mi", "", "1", "100Mi", "1"}, {"10Mi", "", "100m", "", "10Mi", "100m"}, } { // New mock client client := knclient.NewMockKnServiceClient(t) // Recording: r := client.Recorder() // Prepare service expectedService := createTestService("foo", []string{"rev1"}, goodConditions()) // Get service & revision r.GetService("foo", &expectedService, nil) rev1 := createTestRevision("rev1", 1, goodConditions()) addResourceLimits(&rev1.Spec.Containers[0].Resources, data.reqMem, data.limitMem, data.reqCPU, data.limitCPU) r.GetRevision("rev1", &rev1, nil) revList := servingv1.RevisionList{ TypeMeta: metav1.TypeMeta{ Kind: "RevisionList", APIVersion: "serving.knative.dev/v1", }, Items: []servingv1.Revision{ rev1, }, } // Return the list of all revisions r.ListRevisions(knclient.HasLabelSelector(api_serving.ServiceLabelKey, "foo"), &revList, nil) // Testing: output, err := executeServiceCommand(client, "describe", "foo", "--verbose") assert.NilError(t, err) validateServiceOutput(t, "foo", output) assert.Assert(t, cmp.Regexp("Cluster:\\s+https://foo.default.svc.cluster.local", output)) validateOutputLine(t, output, "Memory", data.memoryOut) validateOutputLine(t, output, "CPU", data.cpuOut) // Validate that all recorded API methods have been called r.Validate() } } func TestServiceDescribeUserImageVsImage(t *testing.T) { // New mock client client := knclient.NewMockKnServiceClient(t) // Recording: r := client.Recorder() // Prepare service expectedService := createTestService("foo", []string{"rev1", "rev2", "rev3", "rev4"}, goodConditions()) r.GetService("foo", &expectedService, nil) rev1 := createTestRevision("rev1", 1, goodConditions()) rev2 := createTestRevision("rev2", 2, goodConditions()) rev3 := createTestRevision("rev3", 3, goodConditions()) rev4 := createTestRevision("rev4", 4, goodConditions()) // Different combinations of image annotations and not. rev1.Spec.Containers[0].Image = "gcr.io/test/image:latest" rev1.Annotations[client_serving.UserImageAnnotationKey] = "gcr.io/test/image:latest" rev2.Spec.Containers[0].Image = "gcr.io/test/image@" + imageDigest rev2.Annotations[client_serving.UserImageAnnotationKey] = "gcr.io/test/image:latest" // rev3 is as if we changed the image but didn't change the annotation rev3.Annotations[client_serving.UserImageAnnotationKey] = "gcr.io/test/image:latest" rev3.Spec.Containers[0].Image = "gcr.io/a/b" // rev4 is without the annotation at all and no hash rev4.Status.DeprecatedImageDigest = "" rev4.Spec.Containers[0].Image = "gcr.io/x/y" // Fetch the revisions r.GetRevision("rev1", &rev1, nil) r.GetRevision("rev2", &rev2, nil) r.GetRevision("rev3", &rev3, nil) r.GetRevision("rev4", &rev4, nil) // Testing: output, err := executeServiceCommand(client, "describe", "foo") assert.NilError(t, err) validateServiceOutput(t, "foo", output) assert.Assert(t, util.ContainsAll(output, "Image", "Name", "gcr.io/test/image:latest (pinned to 123456)", "gcr.io/a/b (at 123456)", "gcr.io/x/y")) assert.Assert(t, util.ContainsAll(output, "[1]", "[2]")) // Validate that all recorded API methods have been called r.Validate() } func TestServiceDescribeVerbose(t *testing.T) { // New mock client client := knclient.NewMockKnServiceClient(t) // Recording: r := client.Recorder() // Prepare service expectedService := createTestService("foo", []string{"rev1", "rev2", "rev3"}, goodConditions()) expectedService.Status.Traffic = expectedService.Status.Traffic[2:] expectedService.Status.Traffic[0].LatestRevision = ptr.Bool(true) expectedService.Status.Traffic[0].Percent = ptr.Int64(int64(100)) r.GetService("foo", &expectedService, nil) rev1 := createTestRevision("rev1", 1, goodConditions()) rev2 := createTestRevision("rev2", 2, goodConditions()) rev3 := createTestRevision("rev3", 3, goodConditions()) revList := servingv1.RevisionList{ TypeMeta: metav1.TypeMeta{ Kind: "RevisionList", APIVersion: "serving.knative.dev/v1", }, Items: []servingv1.Revision{ rev1, rev2, rev3, }, } // Return the list of all revisions r.ListRevisions(knclient.HasLabelSelector(api_serving.ServiceLabelKey, "foo"), &revList, nil) // Fetch the revisions r.GetRevision("rev3", &rev3, nil) // Testing: output, err := executeServiceCommand(client, "describe", "foo", "--verbose") assert.NilError(t, err) validateServiceOutput(t, "foo", output) assert.Assert(t, cmp.Regexp("Cluster:\\s+https://foo.default.svc.cluster.local", output)) assert.Assert(t, util.ContainsAll(output, "Image", "Name", "gcr.io/test/image (at 123456)", "100%", "(0s)")) assert.Assert(t, util.ContainsAll(output, "Env:", "env1=eval1\n", "env2=eval2\n")) assert.Assert(t, util.ContainsAll(output, "EnvFrom:", "cm:test1\n", "cm:test2\n")) assert.Assert(t, util.ContainsAll(output, "Annotations:", "anno1=aval1\n", "anno2=aval2\n")) assert.Assert(t, util.ContainsAll(output, "Labels:", "label1=lval1\n", "label2=lval2\n")) assert.Assert(t, util.ContainsAll(output, "[1]", "[2]")) assert.Assert(t, util.ContainsAll(output, "rev1", "rev2", "rev3")) assert.Equal(t, strings.Count(output, "rev3"), 1) assert.Equal(t, strings.Count(output, "rev2"), 1) assert.Equal(t, strings.Count(output, "rev1"), 1) // Validate that all recorded API methods have been called r.Validate() } func TestServiceDescribeWithWrongArguments(t *testing.T) { client := knclient.NewMockKnServiceClient(t) _, err := executeServiceCommand(client, "describe") assert.ErrorContains(t, err, "requires", "name", "service", "single", "argument") _, err = executeServiceCommand(client, "describe", "foo", "bar") assert.ErrorContains(t, err, "requires", "name", "service", "single", "argument") } func TestServiceDescribeMachineReadable(t *testing.T) { client := knclient.NewMockKnServiceClient(t) // Recording: r := client.Recorder() // Prepare service expectedService := createTestService("foo", []string{"rev1", "rev2"}, goodConditions()) r.GetService("foo", &expectedService, nil) output, err := executeServiceCommand(client, "describe", "foo", "-o", "yaml") assert.NilError(t, err) assert.Assert(t, util.ContainsAll(output, "kind: Service", "spec:", "status:", "metadata:")) r.Validate() } func TestServiceDescribeURL(t *testing.T) { client := knclient.NewMockKnServiceClient(t) // Recording: r := client.Recorder() // Prepare service expectedService := createTestService("foo", []string{"rev1", "rev2"}, goodConditions()) r.GetService("foo", &expectedService, nil) output, err := executeServiceCommand(client, "describe", "foo", "-o", "url") assert.NilError(t, err) assert.Assert(t, util.ContainsAll(output, "foo.default.example.com")) r.Validate() } func validateServiceOutput(t *testing.T, service string, output string) { assert.Assert(t, cmp.Regexp("Name:\\s+"+service, output)) assert.Assert(t, cmp.Regexp("Namespace:\\s+default", output)) assert.Assert(t, cmp.Regexp("URL:\\s+https://"+service+".default.example.com", output)) assert.Assert(t, util.ContainsAll(output, "Age:", "Revisions:", "Conditions:", "Labels:", "Annotations:")) assert.Assert(t, util.ContainsAll(output, "Ready", "RoutesReady", "OK", "TYPE", "AGE", "REASON")) } func TestServiceDescribeRevisionUnknown(t *testing.T) { client := knclient.NewMockKnServiceClient(t) r := client.Recorder() expectedService := createTestServiceRevisionUnknown("foo", []string{"rev1"}, unknownConditions()) r.GetService("foo", &expectedService, nil) rev1 := createTestRevision("rev1", 1, unknownConditions()) r.GetRevision("rev1", &rev1, nil) output, err := executeServiceCommand(client, "describe", "foo") assert.NilError(t, err) validateServiceOutput(t, "foo", output) assert.Assert(t, util.ContainsAll(output, "123456")) assert.Assert(t, util.ContainsAll(output, "Annotations:", "anno1=aval1, anno2=aval2, anno3=")) assert.Assert(t, cmp.Regexp(`(?m)\s*Annotations:.*\.\.\.$`, output)) assert.Assert(t, util.ContainsAll(output, "Labels:", "label1=lval1, label2=lval2\n")) assert.Assert(t, util.ContainsAll(output, "[1]")) assert.Equal(t, strings.Count(output, "rev1"), 1) r.Validate() } func createTestService(name string, revisionNames []string, conditions duckv1.Conditions) servingv1.Service { labelMap := make(map[string]string) labelMap["label1"] = "lval1" labelMap["label2"] = "lval2" annoMap := make(map[string]string) annoMap["anno1"] = "aval1" annoMap["anno2"] = "aval2" annoMap["anno3"] = "very_long_value_which_should_be_truncated_in_normal_output_if_we_make_it_even_longer" serviceURL, _ := apis.ParseURL(fmt.Sprintf("https://%s.default.svc.cluster.local", name)) addressURL, _ := apis.ParseURL(fmt.Sprintf("https://%s.default.example.com", name)) service := servingv1.Service{ TypeMeta: metav1.TypeMeta{ Kind: "Service", APIVersion: "serving.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: "default", Labels: labelMap, Annotations: annoMap, CreationTimestamp: metav1.Time{Time: time.Now().Add(-30 * time.Second)}, }, Status: servingv1.ServiceStatus{ RouteStatusFields: servingv1.RouteStatusFields{ URL: addressURL, Address: &duckv1.Addressable{URL: serviceURL}, }, Status: duckv1.Status{ Conditions: conditions, }, }, } service.Status.LatestCreatedRevisionName = revisionNames[len(revisionNames)-1] service.Status.LatestReadyRevisionName = revisionNames[len(revisionNames)-1] if len(revisionNames) > 0 { trafficTargets := make([]servingv1.TrafficTarget, 0) for _, rname := range revisionNames { url, _ := apis.ParseURL(fmt.Sprintf("https://%s", rname)) target := servingv1.TrafficTarget{ RevisionName: rname, ConfigurationName: name, Percent: ptr.Int64(int64(100 / len(revisionNames))), URL: url, } trafficTargets = append(trafficTargets, target) } service.Status.Traffic = trafficTargets } return service } func createTestServiceRevisionUnknown(name string, revisionNames []string, conditions duckv1.Conditions) servingv1.Service { labelMap := make(map[string]string) labelMap["label1"] = "lval1" labelMap["label2"] = "lval2" annoMap := make(map[string]string) annoMap["anno1"] = "aval1" annoMap["anno2"] = "aval2" annoMap["anno3"] = "very_long_value_which_should_be_truncated_in_normal_output_if_we_make_it_even_longer" serviceURL, _ := apis.ParseURL(fmt.Sprintf("https://%s.default.svc.cluster.local", name)) addressURL, _ := apis.ParseURL(fmt.Sprintf("https://%s.default.example.com", name)) service := servingv1.Service{ TypeMeta: metav1.TypeMeta{ Kind: "Service", APIVersion: "serving.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: "default", Labels: labelMap, Annotations: annoMap, CreationTimestamp: metav1.Time{Time: time.Now().Add(-30 * time.Second)}, }, Status: servingv1.ServiceStatus{ RouteStatusFields: servingv1.RouteStatusFields{ URL: addressURL, Address: &duckv1.Addressable{URL: serviceURL}, }, Status: duckv1.Status{ Conditions: conditions, }, }, } service.Status.LatestCreatedRevisionName = revisionNames[len(revisionNames)-1] // LatestReadyRevisionName == "" when Revision is unknown service.Status.LatestReadyRevisionName = "" if len(revisionNames) > 0 { trafficTargets := make([]servingv1.TrafficTarget, 0) for _, rname := range revisionNames { url, _ := apis.ParseURL(fmt.Sprintf("https://%s", rname)) target := servingv1.TrafficTarget{ RevisionName: rname, ConfigurationName: name, Percent: ptr.Int64(int64(100 / len(revisionNames))), URL: url, } trafficTargets = append(trafficTargets, target) } service.Status.Traffic = trafficTargets } return service } func createTestServiceWithServiceAccount(name string, revisionNames []string, serviceAccountName string, conditions duckv1.Conditions) servingv1.Service { service := createTestService(name, revisionNames, conditions) if serviceAccountName != "" { template := servingv1.RevisionTemplateSpec{ Spec: servingv1.RevisionSpec{ PodSpec: v1.PodSpec{ ServiceAccountName: serviceAccountName, }, }, } service.Spec.Template = template } return service } func addScaling(revision *servingv1.Revision, minScale, maxScale, concurrencyTarget, concurrencyLimit, utilization string) { annos := make(map[string]string) if minScale != "" { annos[autoscaling.MinScaleAnnotationKey] = minScale } if maxScale != "" { annos[autoscaling.MaxScaleAnnotationKey] = maxScale } if concurrencyTarget != "" { annos[autoscaling.TargetAnnotationKey] = concurrencyTarget } if utilization != "" { annos[autoscaling.TargetUtilizationPercentageKey] = utilization } revision.Annotations = annos if concurrencyLimit != "" { l, _ := strconv.ParseInt(concurrencyLimit, 10, 64) revision.Spec.ContainerConcurrency = ptr.Int64(l) } } func addResourceLimits(resources *v1.ResourceRequirements, reqMem, limitMem, reqCPU, limitCPU string) { (*resources).Requests = getResourceListQuantity(reqMem, reqCPU) (*resources).Limits = getResourceListQuantity(limitMem, limitCPU) } func getResourceListQuantity(mem string, cpu string) v1.ResourceList { list := v1.ResourceList{} if mem != "" { q, _ := resource.ParseQuantity(mem) list[v1.ResourceMemory] = q } if cpu != "" { q, _ := resource.ParseQuantity(cpu) list[v1.ResourceCPU] = q } return list } func createTestRevision(revision string, gen int64, conditions duckv1.Conditions) servingv1.Revision { labels := make(map[string]string) labels[api_serving.ConfigurationGenerationLabelKey] = fmt.Sprintf("%d", gen) return servingv1.Revision{ TypeMeta: metav1.TypeMeta{ Kind: "Revision", APIVersion: "serving.knative.dev/v1", }, ObjectMeta: metav1.ObjectMeta{ Name: revision, Namespace: "default", Generation: 1, Labels: labels, Annotations: make(map[string]string), CreationTimestamp: metav1.Time{Time: time.Now()}, }, Spec: servingv1.RevisionSpec{ PodSpec: v1.PodSpec{ Containers: []v1.Container{ { Image: "gcr.io/test/image", 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}, }, }, }, }, }, Status: servingv1.RevisionStatus{ DeprecatedImageDigest: "gcr.io/test/image@" + imageDigest, Status: duckv1.Status{ Conditions: conditions, }, }, } } func goodConditions() duckv1.Conditions { ret := make(duckv1.Conditions, 0) ret = append(ret, apis.Condition{ Type: apis.ConditionReady, Status: v1.ConditionTrue, LastTransitionTime: apis.VolatileTime{ Inner: metav1.Time{Time: time.Now()}, }, }) ret = append(ret, apis.Condition{ Type: servingv1.ServiceConditionRoutesReady, Status: v1.ConditionTrue, LastTransitionTime: apis.VolatileTime{ Inner: metav1.Time{Time: time.Now()}, }, }) return ret } func unknownConditions() duckv1.Conditions { ret := make(duckv1.Conditions, 0) ret = append(ret, apis.Condition{ Type: apis.ConditionReady, Status: v1.ConditionUnknown, LastTransitionTime: apis.VolatileTime{ Inner: metav1.Time{Time: time.Now()}, }, }) ret = append(ret, apis.Condition{ Type: servingv1.ServiceConditionRoutesReady, Status: v1.ConditionUnknown, LastTransitionTime: apis.VolatileTime{ Inner: metav1.Time{Time: time.Now()}, }, }) return ret }