From 96beb948ef8083f5e675849d05b61776e3f82512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Suszy=C5=84ski?= Date: Mon, 15 Apr 2024 18:20:49 +0200 Subject: [PATCH] Update sink flag code to use latest from knative/client --- pkg/commands/completion_helper.go | 458 +++++ pkg/commands/completion_helper_test.go | 1617 +++++++++++++++++ .../flags/{listprint.go => list/print.go} | 55 +- .../{listprint_test.go => list/print_test.go} | 45 +- pkg/commands/flags/{sink.go => sink/flag.go} | 111 +- .../flags/{sink_test.go => sink/flag_test.go} | 75 +- pkg/commands/testing_helper_test.go | 66 + pkg/commands/types.go | 37 +- pkg/commands/types_test.go | 12 +- pkg/commands/wait_flags.go | 59 + pkg/commands/wait_flags_test.go | 152 ++ pkg/errors/factory_test.go | 9 +- pkg/eventing/v1/client_mock.go | 183 ++ pkg/eventing/v1/client_mock_test.go | 70 + pkg/eventing/{v1beta1 => v1beta2}/client.go | 54 +- pkg/eventing/v1beta2/client_mock.go | 106 ++ pkg/eventing/v1beta2/client_mock_test.go | 41 + .../{v1beta1 => v1beta2}/client_test.go | 48 +- pkg/flags/podspec_helper.go | 33 + pkg/flags/podspec_helper_test.go | 5 +- .../pkg/factories/kn_source_factory.go | 4 +- .../pkg/factories/kn_source_factory_test.go | 4 +- pkg/kn-source-pkg/pkg/types/structs.go | 4 +- pkg/kn-source-pkg/pkg/types/structs_test.go | 4 +- pkg/printers/tablegenerator.go | 5 +- pkg/serving/service.go | 74 + pkg/serving/service_test.go | 55 + pkg/serving/v1/client_mock.go | 282 +++ pkg/serving/v1/client_mock_test.go | 111 ++ .../eventing/{v1beta1 => v1beta2}/doc.go | 2 +- .../{v1beta1 => v1beta2}/eventing_client.go | 36 +- .../{v1beta1 => v1beta2}/eventtype.go | 42 +- .../eventing/{v1beta1 => v1beta2}/fake/doc.go | 0 .../fake/fake_eventing_client.go | 8 +- .../fake/fake_eventtype.go | 50 +- .../generated_expansion.go | 2 +- vendor/modules.txt | 4 +- 37 files changed, 3666 insertions(+), 257 deletions(-) create mode 100644 pkg/commands/completion_helper.go create mode 100644 pkg/commands/completion_helper_test.go rename pkg/commands/flags/{listprint.go => list/print.go} (60%) rename pkg/commands/flags/{listprint_test.go => list/print_test.go} (76%) rename pkg/commands/flags/{sink.go => sink/flag.go} (55%) rename pkg/commands/flags/{sink_test.go => sink/flag_test.go} (79%) create mode 100644 pkg/commands/testing_helper_test.go create mode 100644 pkg/commands/wait_flags.go create mode 100644 pkg/commands/wait_flags_test.go create mode 100644 pkg/eventing/v1/client_mock.go create mode 100644 pkg/eventing/v1/client_mock_test.go rename pkg/eventing/{v1beta1 => v1beta2}/client.go (74%) create mode 100644 pkg/eventing/v1beta2/client_mock.go create mode 100644 pkg/eventing/v1beta2/client_mock_test.go rename pkg/eventing/{v1beta1 => v1beta2}/client_test.go (80%) create mode 100644 pkg/serving/service.go create mode 100644 pkg/serving/service_test.go create mode 100644 pkg/serving/v1/client_mock.go create mode 100644 pkg/serving/v1/client_mock_test.go rename vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/{v1beta1 => v1beta2}/doc.go (97%) rename vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/{v1beta1 => v1beta2}/eventing_client.go (71%) rename vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/{v1beta1 => v1beta2}/eventtype.go (83%) rename vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/{v1beta1 => v1beta2}/fake/doc.go (100%) rename vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/{v1beta1 => v1beta2}/fake/fake_eventing_client.go (77%) rename vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/{v1beta1 => v1beta2}/fake/fake_eventtype.go (76%) rename vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/{v1beta1 => v1beta2}/generated_expansion.go (97%) diff --git a/pkg/commands/completion_helper.go b/pkg/commands/completion_helper.go new file mode 100644 index 00000000..398ff242 --- /dev/null +++ b/pkg/commands/completion_helper.go @@ -0,0 +1,458 @@ +// Copyright © 2021 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 commands + +import ( + "strings" + + "github.com/spf13/cobra" +) + +type completionConfig struct { + params *KnParams + command *cobra.Command + args []string + toComplete string +} + +var ( + resourceToFuncMap = map[string]func(config *completionConfig) []string{ + "apiserver": completeApiserverSource, + "binding": completeBindingSource, + "broker": completeBroker, + "channel": completeChannel, + "container": completeContainerSource, + "domain": completeDomain, + "ping": completePingSource, + "revision": completeRevision, + "route": completeRoute, + "service": completeService, + "subscription": completeSubscription, + "trigger": completeTrigger, + "eventtype": completeEventtype, + } +) + +// ResourceNameCompletionFunc will return a function that will autocomplete the name of +// the resource based on the subcommand +func ResourceNameCompletionFunc(p *KnParams) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + + var use string + if cmd.Parent() != nil { + use = cmd.Parent().Name() + } + config := completionConfig{ + p, + cmd, + args, + toComplete, + } + return config.getCompletion(use), cobra.ShellCompDirectiveNoFileComp + } +} + +func (config *completionConfig) getCompletion(parent string) []string { + completionFunc := resourceToFuncMap[parent] + if completionFunc == nil { + return []string{} + } + return completionFunc(config) +} + +func getTargetFlagValue(cmd *cobra.Command) string { + flag := cmd.Flag("target") + if flag == nil { + return "" + } + return flag.Value.String() +} + +func completeGitOps(config *completionConfig) (suggestions []string) { + suggestions = make([]string, 0) + if len(config.args) != 0 { + return + } + namespace, err := config.params.GetNamespace(config.command) + if err != nil { + return + } + client, err := config.params.NewGitopsServingClient(namespace, getTargetFlagValue(config.command)) + if err != nil { + return + } + serviceList, err := client.ListServices(config.command.Context()) + if err != nil { + return + } + for _, sug := range serviceList.Items { + if !strings.HasPrefix(sug.Name, config.toComplete) { + continue + } + suggestions = append(suggestions, sug.Name) + } + return +} + +func completeService(config *completionConfig) (suggestions []string) { + if getTargetFlagValue(config.command) != "" { + return completeGitOps(config) + } + + suggestions = make([]string, 0) + if len(config.args) != 0 { + return + } + namespace, err := config.params.GetNamespace(config.command) + if err != nil { + return + } + client, err := config.params.NewServingClient(namespace) + if err != nil { + return + } + serviceList, err := client.ListServices(config.command.Context()) + if err != nil { + return + } + for _, sug := range serviceList.Items { + if !strings.HasPrefix(sug.Name, config.toComplete) { + continue + } + suggestions = append(suggestions, sug.Name) + } + return +} + +func completeBroker(config *completionConfig) (suggestions []string) { + suggestions = make([]string, 0) + if len(config.args) != 0 { + return + } + namespace, err := config.params.GetNamespace(config.command) + if err != nil { + return + } + client, err := config.params.NewEventingClient(namespace) + if err != nil { + return + } + brokerList, err := client.ListBrokers(config.command.Context()) + if err != nil { + return + } + for _, sug := range brokerList.Items { + if !strings.HasPrefix(sug.Name, config.toComplete) { + continue + } + suggestions = append(suggestions, sug.Name) + } + return +} + +func completeRevision(config *completionConfig) (suggestions []string) { + suggestions = make([]string, 0) + if len(config.args) != 0 { + return + } + namespace, err := config.params.GetNamespace(config.command) + if err != nil { + return + } + client, err := config.params.NewServingClient(namespace) + if err != nil { + return + } + revisionList, err := client.ListRevisions(config.command.Context()) + if err != nil { + return + } + for _, sug := range revisionList.Items { + if !strings.HasPrefix(sug.Name, config.toComplete) { + continue + } + suggestions = append(suggestions, sug.Name) + } + return +} + +func completeRoute(config *completionConfig) (suggestions []string) { + suggestions = make([]string, 0) + if len(config.args) != 0 { + return + } + namespace, err := config.params.GetNamespace(config.command) + if err != nil { + return + } + client, err := config.params.NewServingClient(namespace) + if err != nil { + return + } + routeList, err := client.ListRoutes(config.command.Context()) + if err != nil { + return + } + for _, sug := range routeList.Items { + if !strings.HasPrefix(sug.Name, config.toComplete) { + continue + } + suggestions = append(suggestions, sug.Name) + } + return +} + +func completeDomain(config *completionConfig) (suggestions []string) { + suggestions = make([]string, 0) + if len(config.args) != 0 { + return + } + namespace, err := config.params.GetNamespace(config.command) + if err != nil { + return + } + client, err := config.params.NewServingV1beta1Client(namespace) + if err != nil { + return + } + domainMappingList, err := client.ListDomainMappings(config.command.Context()) + if err != nil { + return + } + for _, sug := range domainMappingList.Items { + if !strings.HasPrefix(sug.Name, config.toComplete) { + continue + } + suggestions = append(suggestions, sug.Name) + } + return +} + +func completeTrigger(config *completionConfig) (suggestions []string) { + suggestions = make([]string, 0) + if len(config.args) != 0 { + return + } + namespace, err := config.params.GetNamespace(config.command) + if err != nil { + return + } + client, err := config.params.NewEventingClient(namespace) + if err != nil { + return + } + triggerList, err := client.ListTriggers(config.command.Context()) + if err != nil { + return + } + for _, sug := range triggerList.Items { + if !strings.HasPrefix(sug.Name, config.toComplete) { + continue + } + suggestions = append(suggestions, sug.Name) + } + return +} + +func completeContainerSource(config *completionConfig) (suggestions []string) { + suggestions = make([]string, 0) + if len(config.args) != 0 { + return + } + namespace, err := config.params.GetNamespace(config.command) + if err != nil { + return + } + client, err := config.params.NewSourcesClient(namespace) + if err != nil { + return + } + containerSourceList, err := client.ContainerSourcesClient().ListContainerSources(config.command.Context()) + if err != nil { + return + } + for _, sug := range containerSourceList.Items { + if !strings.HasPrefix(sug.Name, config.toComplete) { + continue + } + suggestions = append(suggestions, sug.Name) + } + return +} + +func completeApiserverSource(config *completionConfig) (suggestions []string) { + suggestions = make([]string, 0) + if len(config.args) != 0 { + return + } + namespace, err := config.params.GetNamespace(config.command) + if err != nil { + return + } + client, err := config.params.NewSourcesClient(namespace) + if err != nil { + return + } + apiServerSourceList, err := client.APIServerSourcesClient().ListAPIServerSource(config.command.Context()) + if err != nil { + return + } + for _, sug := range apiServerSourceList.Items { + if !strings.HasPrefix(sug.Name, config.toComplete) { + continue + } + suggestions = append(suggestions, sug.Name) + } + return +} + +func completeBindingSource(config *completionConfig) (suggestions []string) { + suggestions = make([]string, 0) + if len(config.args) != 0 { + return + } + namespace, err := config.params.GetNamespace(config.command) + if err != nil { + return + } + client, err := config.params.NewSourcesClient(namespace) + if err != nil { + return + } + bindingList, err := client.SinkBindingClient().ListSinkBindings(config.command.Context()) + if err != nil { + return + } + for _, sug := range bindingList.Items { + if !strings.HasPrefix(sug.Name, config.toComplete) { + continue + } + suggestions = append(suggestions, sug.Name) + } + return +} + +func completePingSource(config *completionConfig) (suggestions []string) { + suggestions = make([]string, 0) + if len(config.args) != 0 { + return + } + namespace, err := config.params.GetNamespace(config.command) + if err != nil { + return + } + + client, err := config.params.NewSourcesV1beta2Client(namespace) + if err != nil { + return + } + pingSourcesClient := client.PingSourcesClient() + + pingSourceList, err := pingSourcesClient.ListPingSource(config.command.Context()) + if err != nil { + return + } + for _, sug := range pingSourceList.Items { + if !strings.HasPrefix(sug.Name, config.toComplete) { + continue + } + suggestions = append(suggestions, sug.Name) + } + return +} + +func completeChannel(config *completionConfig) (suggestions []string) { + suggestions = make([]string, 0) + if len(config.args) != 0 { + return + } + namespace, err := config.params.GetNamespace(config.command) + if err != nil { + return + } + + client, err := config.params.NewMessagingClient(namespace) + if err != nil { + return + } + + channelList, err := client.ChannelsClient().ListChannel(config.command.Context()) + if err != nil { + return + } + for _, sug := range channelList.Items { + if !strings.HasPrefix(sug.Name, config.toComplete) { + continue + } + suggestions = append(suggestions, sug.Name) + } + return +} + +func completeSubscription(config *completionConfig) (suggestions []string) { + suggestions = make([]string, 0) + if len(config.args) != 0 { + return + } + namespace, err := config.params.GetNamespace(config.command) + if err != nil { + return + } + + client, err := config.params.NewMessagingClient(namespace) + if err != nil { + return + } + + subscriptionList, err := client.SubscriptionsClient().ListSubscription(config.command.Context()) + if err != nil { + return + } + for _, sug := range subscriptionList.Items { + if !strings.HasPrefix(sug.Name, config.toComplete) { + continue + } + suggestions = append(suggestions, sug.Name) + } + return +} + +func completeEventtype(config *completionConfig) (suggestions []string) { + suggestions = make([]string, 0) + if len(config.args) != 0 { + return + } + namespace, err := config.params.GetNamespace(config.command) + if err != nil { + return + } + + client, err := config.params.NewEventingV1beta2Client(namespace) + if err != nil { + return + } + + eventTypeList, err := client.ListEventtypes(config.command.Context()) + if err != nil { + return + } + for _, sug := range eventTypeList.Items { + if !strings.HasPrefix(sug.Name, config.toComplete) { + continue + } + suggestions = append(suggestions, sug.Name) + } + return +} diff --git a/pkg/commands/completion_helper_test.go b/pkg/commands/completion_helper_test.go new file mode 100644 index 00000000..5d7bbe3d --- /dev/null +++ b/pkg/commands/completion_helper_test.go @@ -0,0 +1,1617 @@ +// Copyright © 2021 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 commands + +import ( + "fmt" + "os" + "path" + "testing" + + "github.com/spf13/cobra" + "gotest.tools/v3/assert" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/clientcmd" + clienteventingv1beta2 "knative.dev/client-pkg/pkg/eventing/v1beta2" + v1beta1 "knative.dev/client-pkg/pkg/messaging/v1" + clientv1beta1 "knative.dev/client-pkg/pkg/serving/v1beta1" + clientsourcesv1 "knative.dev/client-pkg/pkg/sources/v1" + "knative.dev/client-pkg/pkg/sources/v1beta2" + eventingv1beta2 "knative.dev/eventing/pkg/apis/eventing/v1beta2" + messagingv1 "knative.dev/eventing/pkg/apis/messaging/v1" + sourcesv1 "knative.dev/eventing/pkg/apis/sources/v1" + sourcesv1beta2 "knative.dev/eventing/pkg/apis/sources/v1beta2" + sourcesv1fake "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1/fake" + sourcesv1beta2fake "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1beta2/fake" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/cli-runtime/pkg/genericclioptions" + clienttesting "k8s.io/client-go/testing" + clienteventingv1 "knative.dev/client-pkg/pkg/eventing/v1" + v1 "knative.dev/client-pkg/pkg/serving/v1" + + eventingv1 "knative.dev/eventing/pkg/apis/eventing/v1" + "knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1/fake" + beta2fake "knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/fake" + servingv1 "knative.dev/serving/pkg/apis/serving/v1" + servingv1beta1 "knative.dev/serving/pkg/apis/serving/v1beta1" + servingv1fake "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1/fake" + servingv1beta1fake "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1beta1/fake" +) + +type testType struct { + name string + namespace string + p *KnParams + args []string + toComplete string + resource string +} + +type mockMessagingClient struct { + channelsClient v1beta1.KnChannelsClient + subscriptionsClient v1beta1.KnSubscriptionsClient +} + +func (m *mockMessagingClient) ChannelsClient() v1beta1.KnChannelsClient { + return m.channelsClient +} + +func (m *mockMessagingClient) SubscriptionsClient() v1beta1.KnSubscriptionsClient { + return m.subscriptionsClient +} + +const ( + testNs = "test-ns" + errorNs = "error-ns" +) + +var ( + testSvc1 = servingv1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + APIVersion: "serving.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-svc-1", Namespace: testNs}, + } + testSvc2 = servingv1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + APIVersion: "serving.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-svc-2", Namespace: testNs}, + } + testSvc3 = servingv1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + APIVersion: "serving.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-svc-3", Namespace: testNs}, + } + testNsServices = []servingv1.Service{testSvc1, testSvc2, testSvc3} + + fakeServing = &servingv1fake.FakeServingV1{Fake: &clienttesting.Fake{}} + fakeServingAlpha = &servingv1beta1fake.FakeServingV1beta1{Fake: &clienttesting.Fake{}} +) + +var ( + testBroker1 = eventingv1.Broker{ + TypeMeta: metav1.TypeMeta{ + Kind: "Broker", + APIVersion: "eventing.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-broker-1", Namespace: testNs}, + } + testBroker2 = eventingv1.Broker{ + TypeMeta: metav1.TypeMeta{ + Kind: "Broker", + APIVersion: "eventing.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-broker-2", Namespace: testNs}, + } + testBroker3 = eventingv1.Broker{ + TypeMeta: metav1.TypeMeta{ + Kind: "Broker", + APIVersion: "eventing.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-broker-3", Namespace: testNs}, + } + testNsBrokers = []eventingv1.Broker{testBroker1, testBroker2, testBroker3} + + fakeEventing = &fake.FakeEventingV1{Fake: &clienttesting.Fake{}} +) + +var ( + testRev1 = servingv1.Revision{ + TypeMeta: metav1.TypeMeta{ + Kind: "Revision", + APIVersion: "serving.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-rev-1", Namespace: testNs}, + } + testRev2 = servingv1.Revision{ + TypeMeta: metav1.TypeMeta{ + Kind: "Revision", + APIVersion: "serving.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-rev-2", Namespace: testNs}, + } + testRev3 = servingv1.Revision{ + TypeMeta: metav1.TypeMeta{ + Kind: "Revision", + APIVersion: "serving.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-rev-3", Namespace: testNs}, + } + testNsRevs = []servingv1.Revision{testRev1, testRev2, testRev3} +) + +var ( + testRoute1 = servingv1.Route{ + TypeMeta: metav1.TypeMeta{ + Kind: "Route", + APIVersion: "serving.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-route-1", Namespace: testNs}, + } + testRoute2 = servingv1.Route{ + TypeMeta: metav1.TypeMeta{ + Kind: "Route", + APIVersion: "serving.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-route-2", Namespace: testNs}, + } + testRoute3 = servingv1.Route{ + TypeMeta: metav1.TypeMeta{ + Kind: "Route", + APIVersion: "serving.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-route-3", Namespace: testNs}, + } + testNsRoutes = []servingv1.Route{testRoute1, testRoute2, testRoute3} +) + +var ( + testDomain1 = servingv1beta1.DomainMapping{ + TypeMeta: metav1.TypeMeta{ + Kind: "DomainMapping", + APIVersion: "serving.knative.dev/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-domain-1", Namespace: testNs}, + } + testDomain2 = servingv1beta1.DomainMapping{ + TypeMeta: metav1.TypeMeta{ + Kind: "DomainMapping", + APIVersion: "serving.knative.dev/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-domain-2", Namespace: testNs}, + } + testDomain3 = servingv1beta1.DomainMapping{ + TypeMeta: metav1.TypeMeta{ + Kind: "DomainMapping", + APIVersion: "serving.knative.dev/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-domain-3", Namespace: testNs}, + } + testNsDomains = []servingv1beta1.DomainMapping{testDomain1, testDomain2, testDomain3} +) + +var ( + testTrigger1 = eventingv1.Trigger{ + TypeMeta: metav1.TypeMeta{ + Kind: "Trigger", + APIVersion: "eventing.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-trigger-1", Namespace: testNs}, + } + testTrigger2 = eventingv1.Trigger{ + TypeMeta: metav1.TypeMeta{ + Kind: "Trigger", + APIVersion: "eventing.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-trigger-2", Namespace: testNs}, + } + testTrigger3 = eventingv1.Trigger{ + TypeMeta: metav1.TypeMeta{ + Kind: "Trigger", + APIVersion: "eventing.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-trigger-3", Namespace: testNs}, + } + testNsTriggers = []eventingv1.Trigger{testTrigger1, testTrigger2, testTrigger3} +) + +var ( + testContainerSource1 = sourcesv1.ContainerSource{ + TypeMeta: metav1.TypeMeta{ + Kind: "ContainerSource", + APIVersion: "sources.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-container-source-1", Namespace: testNs}, + } + testContainerSource2 = sourcesv1.ContainerSource{ + TypeMeta: metav1.TypeMeta{ + Kind: "ContainerSource", + APIVersion: "sources.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-container-source-2", Namespace: testNs}, + } + testContainerSource3 = sourcesv1.ContainerSource{ + TypeMeta: metav1.TypeMeta{ + Kind: "ContainerSource", + APIVersion: "sources.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-container-source-3", Namespace: testNs}, + } + testNsContainerSources = []sourcesv1.ContainerSource{testContainerSource1, testContainerSource2, testContainerSource3} + fakeSources = &sourcesv1fake.FakeSourcesV1{Fake: &clienttesting.Fake{}} +) + +var ( + testApiServerSource1 = sourcesv1.ApiServerSource{ + TypeMeta: metav1.TypeMeta{ + Kind: "ApiServerSource", + APIVersion: "sources.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-ApiServer-source-1", Namespace: testNs}, + } + testApiServerSource2 = sourcesv1.ApiServerSource{ + TypeMeta: metav1.TypeMeta{ + Kind: "ApiServerSource", + APIVersion: "sources.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-ApiServer-source-2", Namespace: testNs}, + } + testApiServerSource3 = sourcesv1.ApiServerSource{ + TypeMeta: metav1.TypeMeta{ + Kind: "ApiServerSource", + APIVersion: "sources.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-ApiServer-source-3", Namespace: testNs}, + } + testNsApiServerSources = []sourcesv1.ApiServerSource{testApiServerSource1, testApiServerSource2, testApiServerSource3} +) + +var ( + testSinkBinding1 = sourcesv1.SinkBinding{ + TypeMeta: metav1.TypeMeta{ + Kind: "SinkBinding", + APIVersion: "sources.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-sink-binding-1", Namespace: testNs}, + } + testSinkBinding2 = sourcesv1.SinkBinding{ + TypeMeta: metav1.TypeMeta{ + Kind: "SinkBinding", + APIVersion: "sources.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-sink-binding-2", Namespace: testNs}, + } + testSinkBinding3 = sourcesv1.SinkBinding{ + TypeMeta: metav1.TypeMeta{ + Kind: "SinkBinding", + APIVersion: "sources.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-sink-binding-3", Namespace: testNs}, + } + testNsSinkBindings = []sourcesv1.SinkBinding{testSinkBinding1, testSinkBinding2, testSinkBinding3} +) + +var ( + testPingSource1 = sourcesv1beta2.PingSource{ + TypeMeta: metav1.TypeMeta{ + Kind: "PingSource", + APIVersion: "sources.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-ping-source-1", Namespace: testNs}, + } + testPingSource2 = sourcesv1beta2.PingSource{ + TypeMeta: metav1.TypeMeta{ + Kind: "PingSource", + APIVersion: "sources.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-ping-source-2", Namespace: testNs}, + } + testPingSource3 = sourcesv1beta2.PingSource{ + TypeMeta: metav1.TypeMeta{ + Kind: "PingSource", + APIVersion: "sources.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-ping-source-3", Namespace: testNs}, + } + testNsPingSources = []sourcesv1beta2.PingSource{testPingSource1, testPingSource2, testPingSource3} + fakeSourcesV1Beta2 = &sourcesv1beta2fake.FakeSourcesV1beta2{Fake: &clienttesting.Fake{}} +) + +var ( + testChannel1 = messagingv1.Channel{ + TypeMeta: metav1.TypeMeta{ + Kind: "Channel", + APIVersion: "messaging.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-channel-1", Namespace: testNs}, + } + testChannel2 = messagingv1.Channel{ + TypeMeta: metav1.TypeMeta{ + Kind: "Channel", + APIVersion: "messaging.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-channel-2", Namespace: testNs}, + } + testChannel3 = messagingv1.Channel{ + TypeMeta: metav1.TypeMeta{ + Kind: "Channel", + APIVersion: "messaging.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-channel-3", Namespace: testNs}, + } + testNsChannels = []messagingv1.Channel{testChannel1, testChannel2, testChannel3} +) + +var ( + testSubscription1 = messagingv1.Subscription{ + TypeMeta: metav1.TypeMeta{ + Kind: "Subscription", + APIVersion: "messaging.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-subscription-1", Namespace: testNs}, + } + testSubscription2 = messagingv1.Subscription{ + TypeMeta: metav1.TypeMeta{ + Kind: "Subscription", + APIVersion: "messaging.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-subscription-2", Namespace: testNs}, + } + testSubscription3 = messagingv1.Subscription{ + TypeMeta: metav1.TypeMeta{ + Kind: "Subscription", + APIVersion: "messaging.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-subscription-3", Namespace: testNs}, + } + testNsSubscriptions = []messagingv1.Subscription{testSubscription1, testSubscription2, testSubscription3} +) + +var ( + testEventtype1 = eventingv1beta2.EventType{ + TypeMeta: metav1.TypeMeta{ + Kind: "EventType", + APIVersion: "eventing.knative.dev/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-eventtype-1", Namespace: testNs}, + } + testEventtype2 = eventingv1beta2.EventType{ + TypeMeta: metav1.TypeMeta{ + Kind: "EventType", + APIVersion: "eventing.knative.dev/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-eventtype-2", Namespace: testNs}, + } + testEventtype3 = eventingv1beta2.EventType{ + TypeMeta: metav1.TypeMeta{ + Kind: "EventType", + APIVersion: "eventing.knative.dev/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-eventtype-3", Namespace: testNs}, + } + testEventtypes = []eventingv1beta2.EventType{testEventtype1, testEventtype2, testEventtype3} + fakeEventingBeta2Client = &beta2fake.FakeEventingV1beta2{Fake: &clienttesting.Fake{}} +) + +var knParams = initialiseKnParams() + +func initialiseKnParams() *KnParams { + blankConfig, err := clientcmd.NewClientConfigFromBytes([]byte(`kind: Config +version: v1beta2 +users: +- name: u +clusters: +- name: c + cluster: + server: example.com +contexts: +- name: x + context: + user: u + cluster: c +current-context: x +`)) + if err != nil { + panic(err) + } + return &KnParams{ + NewServingClient: func(namespace string) (v1.KnServingClient, error) { + return v1.NewKnServingClient(fakeServing, namespace), nil + }, + NewGitopsServingClient: func(namespace string, dir string) (v1.KnServingClient, error) { + return v1.NewKnServingGitOpsClient(namespace, dir), nil + }, + NewEventingClient: func(namespace string) (clienteventingv1.KnEventingClient, error) { + return clienteventingv1.NewKnEventingClient(fakeEventing, namespace), nil + }, + NewServingV1beta1Client: func(namespace string) (clientv1beta1.KnServingClient, error) { + return clientv1beta1.NewKnServingClient(fakeServingAlpha, namespace), nil + }, + NewSourcesClient: func(namespace string) (clientsourcesv1.KnSourcesClient, error) { + return clientsourcesv1.NewKnSourcesClient(fakeSources, namespace), nil + }, + NewSourcesV1beta2Client: func(namespace string) (v1beta2.KnSourcesClient, error) { + return v1beta2.NewKnSourcesClient(fakeSourcesV1Beta2, namespace), nil + }, + NewEventingV1beta2Client: func(namespace string) (clienteventingv1beta2.KnEventingV1Beta2Client, error) { + return clienteventingv1beta2.NewKnEventingV1Beta2Client(fakeEventingBeta2Client, namespace), nil + }, + ClientConfig: blankConfig, + } +} + +func TestResourceNameCompletionFuncService(t *testing.T) { + completionFunc := ResourceNameCompletionFunc(knParams) + + fakeServing.AddReactor("list", "services", + func(a clienttesting.Action) (bool, runtime.Object, error) { + if a.GetNamespace() == errorNs { + return true, nil, errors.NewInternalError(fmt.Errorf("unable to list services")) + } + return true, &servingv1.ServiceList{Items: testNsServices}, nil + }) + + tests := []testType{ + { + "Empty suggestions when no parent command found", + testNs, + knParams, + nil, + "", + "no-parent", + }, + { + "Empty suggestions when non-zero args", + testNs, + knParams, + []string{"xyz"}, + "", + "service", + }, + { + "Empty suggestions when no namespace flag", + "", + knParams, + nil, + "", + "service", + }, + { + "Suggestions when test-ns namespace set", + testNs, + knParams, + nil, + "", + "service", + }, + { + "Empty suggestions when toComplete is not a prefix", + testNs, + knParams, + nil, + "xyz", + "service", + }, + { + "Empty suggestions when error during list operation", + errorNs, + knParams, + nil, + "", + "service", + }, + } + + for _, tt := range tests { + cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") + t.Run(tt.name, func(t *testing.T) { + config := &completionConfig{ + params: tt.p, + command: cmd, + args: tt.args, + toComplete: tt.toComplete, + } + expectedFunc := resourceToFuncMap[tt.resource] + if expectedFunc == nil { + expectedFunc = func(config *completionConfig) []string { + return []string{} + } + } + cmd.Flags().Set("namespace", tt.namespace) + actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) + expectedSuggestions := expectedFunc(config) + expectedDirective := cobra.ShellCompDirectiveNoFileComp + assert.DeepEqual(t, actualSuggestions, expectedSuggestions) + assert.Equal(t, actualDirective, expectedDirective) + }) + } +} + +func TestResourceNameCompletionFuncBroker(t *testing.T) { + completionFunc := ResourceNameCompletionFunc(knParams) + + fakeEventing.AddReactor("list", "brokers", func(action clienttesting.Action) (bool, runtime.Object, error) { + if action.GetNamespace() == errorNs { + return true, nil, errors.NewInternalError(fmt.Errorf("unable to list brokers")) + } + return true, &eventingv1.BrokerList{Items: testNsBrokers}, nil + }) + tests := []testType{ + { + "Empty suggestions when non-zero args", + testNs, + knParams, + []string{"xyz"}, + "", + "broker", + }, + { + "Empty suggestions when no namespace flag", + "", + knParams, + nil, + "", + "broker", + }, + { + "Suggestions when test-ns namespace set", + testNs, + knParams, + nil, + "", + "broker", + }, + { + "Empty suggestions when toComplete is not a prefix", + testNs, + knParams, + nil, + "xyz", + "broker", + }, + { + "Empty suggestions when error during list operation", + errorNs, + knParams, + nil, + "", + "broker", + }, + } + for _, tt := range tests { + cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") + t.Run(tt.name, func(t *testing.T) { + config := &completionConfig{ + params: tt.p, + command: cmd, + args: tt.args, + toComplete: tt.toComplete, + } + expectedFunc := resourceToFuncMap[tt.resource] + if expectedFunc == nil { + expectedFunc = func(config *completionConfig) []string { + return []string{} + } + } + cmd.Flags().Set("namespace", tt.namespace) + actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) + expectedSuggestions := expectedFunc(config) + expectedDirective := cobra.ShellCompDirectiveNoFileComp + assert.DeepEqual(t, actualSuggestions, expectedSuggestions) + assert.Equal(t, actualDirective, expectedDirective) + }) + } +} + +func TestResourceNameCompletionFuncRevision(t *testing.T) { + completionFunc := ResourceNameCompletionFunc(knParams) + + fakeServing.AddReactor("list", "revisions", + func(a clienttesting.Action) (bool, runtime.Object, error) { + if a.GetNamespace() == errorNs { + return true, nil, errors.NewInternalError(fmt.Errorf("unable to list revisions")) + } + return true, &servingv1.RevisionList{Items: testNsRevs}, nil + }) + + tests := []testType{ + { + "Empty suggestions when non-zero args", + testNs, + knParams, + []string{"xyz"}, + "", + "revision", + }, + { + "Empty suggestions when no namespace flag", + "", + knParams, + nil, + "", + "revision", + }, + { + "Suggestions when test-ns namespace set", + testNs, + knParams, + nil, + "", + "revision", + }, + { + "Empty suggestions when toComplete is not a prefix", + testNs, + knParams, + nil, + "xyz", + "revision", + }, + { + "Empty suggestions when error during list operation", + errorNs, + knParams, + nil, + "", + "revision", + }, + } + for _, tt := range tests { + cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") + t.Run(tt.name, func(t *testing.T) { + config := &completionConfig{ + params: tt.p, + command: cmd, + args: tt.args, + toComplete: tt.toComplete, + } + expectedFunc := resourceToFuncMap[tt.resource] + if expectedFunc == nil { + expectedFunc = func(config *completionConfig) []string { + return []string{} + } + } + cmd.Flags().Set("namespace", tt.namespace) + actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) + expectedSuggestions := expectedFunc(config) + expectedDirective := cobra.ShellCompDirectiveNoFileComp + assert.DeepEqual(t, actualSuggestions, expectedSuggestions) + assert.Equal(t, actualDirective, expectedDirective) + }) + } +} + +func TestResourceNameCompletionFuncGitOps(t *testing.T) { + tempDir := setupTempDir(t) + + completionFunc := ResourceNameCompletionFunc(knParams) + + tests := []testType{ + { + "Empty suggestions when no parent command found", + testNs, + knParams, + nil, + "", + "service", + }, + { + "Empty suggestions when non-zero args", + testNs, + knParams, + []string{"xyz"}, + "", + "service", + }, + { + "Empty suggestions when no namespace flag", + "", + knParams, + nil, + "", + "service", + }, + { + "Suggestions when test-ns namespace set", + testNs, + knParams, + nil, + "", + "service", + }, + { + "Empty suggestions when toComplete is not a prefix", + testNs, + knParams, + nil, + "xyz", + "service", + }, + { + "Empty suggestions when error during list operation", + errorNs, + knParams, + nil, + "", + "service", + }, + } + + for _, tt := range tests { + cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") + t.Run(tt.name, func(t *testing.T) { + config := &completionConfig{ + params: tt.p, + command: cmd, + args: tt.args, + toComplete: tt.toComplete, + } + expectedFunc := resourceToFuncMap[tt.resource] + cmd.Flags().String("target", tempDir, "target directory") + cmd.Flags().Set("namespace", tt.namespace) + + expectedSuggestions := expectedFunc(config) + expectedDirective := cobra.ShellCompDirectiveNoFileComp + actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) + assert.DeepEqual(t, actualSuggestions, expectedSuggestions) + assert.Equal(t, actualDirective, expectedDirective) + }) + } +} + +func TestResourceNameCompletionFuncRoute(t *testing.T) { + completionFunc := ResourceNameCompletionFunc(knParams) + + fakeServing.AddReactor("list", "routes", + func(a clienttesting.Action) (bool, runtime.Object, error) { + if a.GetNamespace() == errorNs { + return true, nil, errors.NewInternalError(fmt.Errorf("unable to list routes")) + } + return true, &servingv1.RouteList{Items: testNsRoutes}, nil + }) + + tests := []testType{ + { + "Empty suggestions when non-zero args", + testNs, + knParams, + []string{"xyz"}, + "", + "route", + }, + { + "Empty suggestions when no namespace flag", + "", + knParams, + nil, + "", + "route", + }, + { + "Suggestions when test-ns namespace set", + testNs, + knParams, + nil, + "", + "route", + }, + { + "Empty suggestions when toComplete is not a prefix", + testNs, + knParams, + nil, + "xyz", + "route", + }, + { + "Empty suggestions when error during list operation", + errorNs, + knParams, + nil, + "", + "route", + }, + } + for _, tt := range tests { + cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") + t.Run(tt.name, func(t *testing.T) { + config := &completionConfig{ + params: tt.p, + command: cmd, + args: tt.args, + toComplete: tt.toComplete, + } + expectedFunc := resourceToFuncMap[tt.resource] + if expectedFunc == nil { + expectedFunc = func(config *completionConfig) []string { + return []string{} + } + } + cmd.Flags().Set("namespace", tt.namespace) + actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) + expectedSuggestions := expectedFunc(config) + expectedDirective := cobra.ShellCompDirectiveNoFileComp + assert.DeepEqual(t, actualSuggestions, expectedSuggestions) + assert.Equal(t, actualDirective, expectedDirective) + }) + } +} + +func TestResourceNameCompletionFuncDomain(t *testing.T) { + completionFunc := ResourceNameCompletionFunc(knParams) + + fakeServingAlpha.AddReactor("list", "domainmappings", + func(a clienttesting.Action) (bool, runtime.Object, error) { + if a.GetNamespace() == errorNs { + return true, nil, errors.NewInternalError(fmt.Errorf("unable to list domains")) + } + return true, &servingv1beta1.DomainMappingList{Items: testNsDomains}, nil + }) + + tests := []testType{ + { + "Empty suggestions when non-zero args", + testNs, + knParams, + []string{"xyz"}, + "", + "domain", + }, + { + "Empty suggestions when no namespace flag", + "", + knParams, + nil, + "", + "domain", + }, + { + "Suggestions when test-ns namespace set", + testNs, + knParams, + nil, + "", + "domain", + }, + { + "Empty suggestions when toComplete is not a prefix", + testNs, + knParams, + nil, + "xyz", + "domain", + }, + { + "Empty suggestions when error during list operation", + errorNs, + knParams, + nil, + "", + "domain", + }, + } + for _, tt := range tests { + cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") + t.Run(tt.name, func(t *testing.T) { + config := &completionConfig{ + params: tt.p, + command: cmd, + args: tt.args, + toComplete: tt.toComplete, + } + expectedFunc := resourceToFuncMap[tt.resource] + if expectedFunc == nil { + expectedFunc = func(config *completionConfig) []string { + return []string{} + } + } + cmd.Flags().Set("namespace", tt.namespace) + actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) + expectedSuggestions := expectedFunc(config) + expectedDirective := cobra.ShellCompDirectiveNoFileComp + assert.DeepEqual(t, actualSuggestions, expectedSuggestions) + assert.Equal(t, actualDirective, expectedDirective) + }) + } +} + +func TestResourceNameCompletionFuncTrigger(t *testing.T) { + completionFunc := ResourceNameCompletionFunc(knParams) + + fakeServing.AddReactor("list", "triggers", + func(a clienttesting.Action) (bool, runtime.Object, error) { + if a.GetNamespace() == errorNs { + return true, nil, errors.NewInternalError(fmt.Errorf("unable to list triggers")) + } + return true, &eventingv1.TriggerList{Items: testNsTriggers}, nil + }) + + tests := []testType{ + { + "Empty suggestions when non-zero args", + testNs, + knParams, + []string{"xyz"}, + "", + "trigger", + }, + { + "Empty suggestions when no namespace flag", + "", + knParams, + nil, + "", + "trigger", + }, + { + "Suggestions when test-ns namespace set", + testNs, + knParams, + nil, + "", + "trigger", + }, + { + "Empty suggestions when toComplete is not a prefix", + testNs, + knParams, + nil, + "xyz", + "trigger", + }, + { + "Empty suggestions when error during list operation", + errorNs, + knParams, + nil, + "", + "trigger", + }, + } + for _, tt := range tests { + cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") + t.Run(tt.name, func(t *testing.T) { + config := &completionConfig{ + params: tt.p, + command: cmd, + args: tt.args, + toComplete: tt.toComplete, + } + expectedFunc := resourceToFuncMap[tt.resource] + if expectedFunc == nil { + expectedFunc = func(config *completionConfig) []string { + return []string{} + } + } + cmd.Flags().Set("namespace", tt.namespace) + actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) + expectedSuggestions := expectedFunc(config) + expectedDirective := cobra.ShellCompDirectiveNoFileComp + assert.DeepEqual(t, actualSuggestions, expectedSuggestions) + assert.Equal(t, actualDirective, expectedDirective) + }) + } +} + +func TestResourceNameCompletionFuncContainerSource(t *testing.T) { + completionFunc := ResourceNameCompletionFunc(knParams) + + fakeSources.AddReactor("list", "containersources", + func(a clienttesting.Action) (bool, runtime.Object, error) { + if a.GetNamespace() == errorNs { + return true, nil, errors.NewInternalError(fmt.Errorf("unable to list container sources")) + } + return true, &sourcesv1.ContainerSourceList{Items: testNsContainerSources}, nil + }) + + tests := []testType{ + { + "Empty suggestions when non-zero args", + testNs, + knParams, + []string{"xyz"}, + "", + "container", + }, + { + "Empty suggestions when no namespace flag", + "", + knParams, + nil, + "", + "container", + }, + { + "Suggestions when test-ns namespace set", + testNs, + knParams, + nil, + "", + "container", + }, + { + "Empty suggestions when toComplete is not a prefix", + testNs, + knParams, + nil, + "xyz", + "container", + }, + { + "Empty suggestions when error during list operation", + errorNs, + knParams, + nil, + "", + "container", + }, + } + for _, tt := range tests { + cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") + t.Run(tt.name, func(t *testing.T) { + config := &completionConfig{ + params: tt.p, + command: cmd, + args: tt.args, + toComplete: tt.toComplete, + } + expectedFunc := resourceToFuncMap[tt.resource] + if expectedFunc == nil { + expectedFunc = func(config *completionConfig) []string { + return []string{} + } + } + cmd.Flags().Set("namespace", tt.namespace) + actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) + expectedSuggestions := expectedFunc(config) + expectedDirective := cobra.ShellCompDirectiveNoFileComp + assert.DeepEqual(t, actualSuggestions, expectedSuggestions) + assert.Equal(t, actualDirective, expectedDirective) + }) + } +} + +func TestResourceNameCompletionFuncApiserverSource(t *testing.T) { + completionFunc := ResourceNameCompletionFunc(knParams) + + fakeSources.AddReactor("list", "apiserversources", + func(a clienttesting.Action) (bool, runtime.Object, error) { + if a.GetNamespace() == errorNs { + return true, nil, errors.NewInternalError(fmt.Errorf("unable to list apiserver sources")) + } + return true, &sourcesv1.ApiServerSourceList{Items: testNsApiServerSources}, nil + }) + + tests := []testType{ + { + "Empty suggestions when non-zero args", + testNs, + knParams, + []string{"xyz"}, + "", + "apiserver", + }, + { + "Empty suggestions when no namespace flag", + "", + knParams, + nil, + "", + "apiserver", + }, + { + "Suggestions when test-ns namespace set", + testNs, + knParams, + nil, + "", + "apiserver", + }, + { + "Empty suggestions when toComplete is not a prefix", + testNs, + knParams, + nil, + "xyz", + "apiserver", + }, + { + "Empty suggestions when error during list operation", + errorNs, + knParams, + nil, + "", + "apiserver", + }, + } + for _, tt := range tests { + cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") + t.Run(tt.name, func(t *testing.T) { + config := &completionConfig{ + params: tt.p, + command: cmd, + args: tt.args, + toComplete: tt.toComplete, + } + expectedFunc := resourceToFuncMap[tt.resource] + if expectedFunc == nil { + expectedFunc = func(config *completionConfig) []string { + return []string{} + } + } + cmd.Flags().Set("namespace", tt.namespace) + actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) + expectedSuggestions := expectedFunc(config) + expectedDirective := cobra.ShellCompDirectiveNoFileComp + assert.DeepEqual(t, actualSuggestions, expectedSuggestions) + assert.Equal(t, actualDirective, expectedDirective) + }) + } +} + +func TestResourceNameCompletionFuncBindingSource(t *testing.T) { + completionFunc := ResourceNameCompletionFunc(knParams) + + fakeSources.AddReactor("list", "sinkbindings", + func(a clienttesting.Action) (bool, runtime.Object, error) { + if a.GetNamespace() == errorNs { + return true, nil, errors.NewInternalError(fmt.Errorf("unable to list binding sources")) + } + return true, &sourcesv1.SinkBindingList{Items: testNsSinkBindings}, nil + }) + + tests := []testType{ + { + "Empty suggestions when non-zero args", + testNs, + knParams, + []string{"xyz"}, + "", + "binding", + }, + { + "Empty suggestions when no namespace flag", + "", + knParams, + nil, + "", + "binding", + }, + { + "Suggestions when test-ns namespace set", + testNs, + knParams, + nil, + "", + "binding", + }, + { + "Empty suggestions when toComplete is not a prefix", + testNs, + knParams, + nil, + "xyz", + "binding", + }, + { + "Empty suggestions when error during list operation", + errorNs, + knParams, + nil, + "", + "binding", + }, + } + for _, tt := range tests { + cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") + t.Run(tt.name, func(t *testing.T) { + config := &completionConfig{ + params: tt.p, + command: cmd, + args: tt.args, + toComplete: tt.toComplete, + } + expectedFunc := resourceToFuncMap[tt.resource] + if expectedFunc == nil { + expectedFunc = func(config *completionConfig) []string { + return []string{} + } + } + cmd.Flags().Set("namespace", tt.namespace) + actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) + expectedSuggestions := expectedFunc(config) + expectedDirective := cobra.ShellCompDirectiveNoFileComp + assert.DeepEqual(t, actualSuggestions, expectedSuggestions) + assert.Equal(t, actualDirective, expectedDirective) + }) + } +} + +func TestResourceNameCompletionFuncPingSource(t *testing.T) { + completionFunc := ResourceNameCompletionFunc(knParams) + + fakeSourcesV1Beta2.AddReactor("list", "pingsources", + func(a clienttesting.Action) (bool, runtime.Object, error) { + if a.GetNamespace() == errorNs { + return true, nil, errors.NewInternalError(fmt.Errorf("unable to list ping sources")) + } + return true, &sourcesv1beta2.PingSourceList{Items: testNsPingSources}, nil + }) + + tests := []testType{ + { + "Empty suggestions when non-zero args", + testNs, + knParams, + []string{"xyz"}, + "", + "ping", + }, + { + "Empty suggestions when no namespace flag", + "", + knParams, + nil, + "", + "ping", + }, + { + "Suggestions when test-ns namespace set", + testNs, + knParams, + nil, + "", + "ping", + }, + { + "Empty suggestions when toComplete is not a prefix", + testNs, + knParams, + nil, + "xyz", + "ping", + }, + { + "Empty suggestions when error during list operation", + errorNs, + knParams, + nil, + "", + "ping", + }, + } + for _, tt := range tests { + cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") + t.Run(tt.name, func(t *testing.T) { + config := &completionConfig{ + params: tt.p, + command: cmd, + args: tt.args, + toComplete: tt.toComplete, + } + expectedFunc := resourceToFuncMap[tt.resource] + if expectedFunc == nil { + expectedFunc = func(config *completionConfig) []string { + return []string{} + } + } + cmd.Flags().Set("namespace", tt.namespace) + actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) + expectedSuggestions := expectedFunc(config) + expectedDirective := cobra.ShellCompDirectiveNoFileComp + assert.DeepEqual(t, actualSuggestions, expectedSuggestions) + assert.Equal(t, actualDirective, expectedDirective) + }) + } +} + +func TestResourceNameCompletionFuncChannel(t *testing.T) { + completionFunc := ResourceNameCompletionFunc(knParams) + + channelClient := v1beta1.NewMockKnChannelsClient(t) + channelClient.Recorder().ListChannel(&messagingv1.ChannelList{Items: testNsChannels}, nil) + channelClient.Recorder().ListChannel(&messagingv1.ChannelList{Items: testNsChannels}, nil) + + channelClient.Recorder().ListChannel(&messagingv1.ChannelList{Items: testNsChannels}, nil) + channelClient.Recorder().ListChannel(&messagingv1.ChannelList{Items: testNsChannels}, nil) + + channelClient.Recorder().ListChannel(&messagingv1.ChannelList{}, fmt.Errorf("error listing channels")) + channelClient.Recorder().ListChannel(&messagingv1.ChannelList{}, fmt.Errorf("error listing channels")) + + messagingClient := &mockMessagingClient{channelClient, nil} + + knParams.NewMessagingClient = func(namespace string) (v1beta1.KnMessagingClient, error) { + return messagingClient, nil + } + tests := []testType{ + { + "Empty suggestions when non-zero args", + testNs, + knParams, + []string{"xyz"}, + "", + "channel", + }, + { + "Empty suggestions when no namespace flag", + "", + knParams, + nil, + "", + "channel", + }, + { + "Suggestions when test-ns namespace set", + testNs, + knParams, + nil, + "", + "channel", + }, + { + "Empty suggestions when toComplete is not a prefix", + testNs, + knParams, + nil, + "xyz", + "channel", + }, + { + "Empty suggestions when error during list operation", + errorNs, + knParams, + nil, + "", + "channel", + }, + } + for _, tt := range tests { + cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") + t.Run(tt.name, func(t *testing.T) { + config := &completionConfig{ + params: tt.p, + command: cmd, + args: tt.args, + toComplete: tt.toComplete, + } + expectedFunc := resourceToFuncMap[tt.resource] + if expectedFunc == nil { + expectedFunc = func(config *completionConfig) []string { + return []string{} + } + } + cmd.Flags().Set("namespace", tt.namespace) + actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) + expectedSuggestions := expectedFunc(config) + expectedDirective := cobra.ShellCompDirectiveNoFileComp + assert.DeepEqual(t, actualSuggestions, expectedSuggestions) + assert.Equal(t, actualDirective, expectedDirective) + }) + } + channelClient.Recorder().Validate() +} + +func TestResourceNameCompletionFuncSubscription(t *testing.T) { + completionFunc := ResourceNameCompletionFunc(knParams) + + subscriptionsClient := v1beta1.NewMockKnSubscriptionsClient(t) + subscriptionsClient.Recorder().ListSubscription(&messagingv1.SubscriptionList{Items: testNsSubscriptions}, nil) + subscriptionsClient.Recorder().ListSubscription(&messagingv1.SubscriptionList{Items: testNsSubscriptions}, nil) + + subscriptionsClient.Recorder().ListSubscription(&messagingv1.SubscriptionList{Items: testNsSubscriptions}, nil) + subscriptionsClient.Recorder().ListSubscription(&messagingv1.SubscriptionList{Items: testNsSubscriptions}, nil) + + subscriptionsClient.Recorder().ListSubscription(&messagingv1.SubscriptionList{}, fmt.Errorf("error listing channels")) + subscriptionsClient.Recorder().ListSubscription(&messagingv1.SubscriptionList{}, fmt.Errorf("error listing channels")) + + messagingClient := &mockMessagingClient{nil, subscriptionsClient} + + knParams.NewMessagingClient = func(namespace string) (v1beta1.KnMessagingClient, error) { + return messagingClient, nil + } + tests := []testType{ + { + "Empty suggestions when non-zero args", + testNs, + knParams, + []string{"xyz"}, + "", + "subscription", + }, + { + "Empty suggestions when no namespace flag", + "", + knParams, + nil, + "", + "subscription", + }, + { + "Suggestions when test-ns namespace set", + testNs, + knParams, + nil, + "", + "subscription", + }, + { + "Empty suggestions when toComplete is not a prefix", + testNs, + knParams, + nil, + "xyz", + "subscription", + }, + { + "Empty suggestions when error during list operation", + errorNs, + knParams, + nil, + "", + "subscription", + }, + } + for _, tt := range tests { + cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") + t.Run(tt.name, func(t *testing.T) { + config := &completionConfig{ + params: tt.p, + command: cmd, + args: tt.args, + toComplete: tt.toComplete, + } + expectedFunc := resourceToFuncMap[tt.resource] + if expectedFunc == nil { + expectedFunc = func(config *completionConfig) []string { + return []string{} + } + } + cmd.Flags().Set("namespace", tt.namespace) + actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) + expectedSuggestions := expectedFunc(config) + expectedDirective := cobra.ShellCompDirectiveNoFileComp + assert.DeepEqual(t, actualSuggestions, expectedSuggestions) + assert.Equal(t, actualDirective, expectedDirective) + }) + } + subscriptionsClient.Recorder().Validate() +} + +func TestResourceNameCompletionFuncEventtype(t *testing.T) { + completionFunc := ResourceNameCompletionFunc(knParams) + + fakeEventingBeta2Client.AddReactor("list", "eventtypes", func(a clienttesting.Action) (bool, runtime.Object, error) { + if a.GetNamespace() == errorNs { + return true, nil, errors.NewInternalError(fmt.Errorf("unable to list eventtypes")) + } + return true, &eventingv1beta2.EventTypeList{Items: testEventtypes}, nil + }) + + tests := []testType{ + { + "Empty suggestions when non-zero args", + testNs, + knParams, + []string{"xyz"}, + "", + "eventtype", + }, + { + "Empty suggestions when no namespace flag", + "", + knParams, + nil, + "", + "eventtype", + }, + { + "Suggestions when test-ns namespace set", + testNs, + knParams, + nil, + "", + "eventtype", + }, + { + "Empty suggestions when toComplete is not a prefix", + testNs, + knParams, + nil, + "xyz", + "eventtype", + }, + { + "Empty suggestions when error during list operation", + errorNs, + knParams, + nil, + "", + "eventtype", + }, + } + for _, tt := range tests { + cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") + t.Run(tt.name, func(t *testing.T) { + config := &completionConfig{ + params: tt.p, + command: cmd, + args: tt.args, + toComplete: tt.toComplete, + } + expectedFunc := resourceToFuncMap[tt.resource] + if expectedFunc == nil { + expectedFunc = func(config *completionConfig) []string { + return []string{} + } + } + cmd.Flags().Set("namespace", tt.namespace) + actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) + expectedSuggestions := expectedFunc(config) + expectedDirective := cobra.ShellCompDirectiveNoFileComp + assert.DeepEqual(t, actualSuggestions, expectedSuggestions) + assert.Equal(t, actualDirective, expectedDirective) + }) + } +} + +func getResourceCommandWithTestSubcommand(resource string, addNamespace, addSubcommand bool) *cobra.Command { + testCommand := &cobra.Command{ + Use: resource, + } + testSubCommand := &cobra.Command{ + Use: "test", + } + if addSubcommand { + testCommand.AddCommand(testSubCommand) + } + if addNamespace { + AddNamespaceFlags(testCommand.Flags(), true) + AddNamespaceFlags(testSubCommand.Flags(), true) + } + return testSubCommand +} + +func setupTempDir(t *testing.T) string { + tempDir := t.TempDir() + + svcPath := path.Join(tempDir, "test-ns", "ksvc") + err := os.MkdirAll(svcPath, 0700) + assert.NilError(t, err) + + for i, testSvc := range []servingv1.Service{testSvc1, testSvc2, testSvc3} { + tempFile, err := os.Create(path.Join(svcPath, fmt.Sprintf("test-svc-%d.yaml", i+1))) + assert.NilError(t, err) + writeToFile(t, testSvc, tempFile) + } + + return tempDir +} + +func writeToFile(t *testing.T, testSvc servingv1.Service, tempFile *os.File) { + yamlPrinter, err := genericclioptions.NewJSONYamlPrintFlags().ToPrinter("yaml") + assert.NilError(t, err) + + err = yamlPrinter.PrintObj(&testSvc, tempFile) + assert.NilError(t, err) + + defer tempFile.Close() +} diff --git a/pkg/commands/flags/listprint.go b/pkg/commands/flags/list/print.go similarity index 60% rename from pkg/commands/flags/listprint.go rename to pkg/commands/flags/list/print.go index 098674f9..6f59b26a 100644 --- a/pkg/commands/flags/listprint.go +++ b/pkg/commands/flags/list/print.go @@ -1,18 +1,20 @@ -// 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 flags +/* + Copyright 2024 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 list import ( "io" @@ -26,16 +28,15 @@ import ( "knative.dev/client-pkg/pkg/util" ) -// ListFlags composes common printer flag structs -// used in the list command. -type ListPrintFlags struct { +// PrintFlags composes common printer flag structs used in the list command. +type PrintFlags struct { GenericPrintFlags *genericclioptions.PrintFlags HumanReadableFlags *commands.HumanPrintFlags PrinterHandler func(h hprinters.PrintHandler) } // AllowedFormats is the list of formats in which data can be displayed -func (f *ListPrintFlags) AllowedFormats() []string { +func (f *PrintFlags) AllowedFormats() []string { formats := f.GenericPrintFlags.AllowedFormats() formats = append(formats, f.HumanReadableFlags.AllowedFormats()...) return formats @@ -43,7 +44,7 @@ func (f *ListPrintFlags) AllowedFormats() []string { // ToPrinter attempts to find a composed set of ListTypesFlags suitable for // returning a printer based on current flag values. -func (f *ListPrintFlags) ToPrinter() (hprinters.ResourcePrinter, error) { +func (f *PrintFlags) ToPrinter() (hprinters.ResourcePrinter, error) { // if there are flags specified for generic printing if f.GenericPrintFlags.OutputFlagSpecified() { p, err := f.GenericPrintFlags.ToPrinter() @@ -61,7 +62,7 @@ func (f *ListPrintFlags) ToPrinter() (hprinters.ResourcePrinter, error) { } // Print is to print an Object to a Writer -func (f *ListPrintFlags) Print(obj runtime.Object, w io.Writer) error { +func (f *PrintFlags) Print(obj runtime.Object, w io.Writer) error { printer, err := f.ToPrinter() if err != nil { return err @@ -80,15 +81,15 @@ func (f *ListPrintFlags) Print(obj runtime.Object, w io.Writer) error { // AddFlags receives a *cobra.Command reference and binds // flags related to humanreadable and template printing. -func (f *ListPrintFlags) AddFlags(cmd *cobra.Command) { +func (f *PrintFlags) AddFlags(cmd *cobra.Command) { f.GenericPrintFlags.AddFlags(cmd) f.HumanReadableFlags.AddFlags(cmd) } -// NewListFlags returns flags associated with humanreadable, -// template, and "name" printing, with default values set. -func NewListPrintFlags(printer func(h hprinters.PrintHandler)) *ListPrintFlags { - return &ListPrintFlags{ +// NewPrintFlags returns flags associated with humanreadable, template, and +// "name" printing, with default values set. +func NewPrintFlags(printer func(h hprinters.PrintHandler)) *PrintFlags { + return &PrintFlags{ GenericPrintFlags: genericclioptions.NewPrintFlags(""), HumanReadableFlags: commands.NewHumanPrintFlags(), PrinterHandler: printer, @@ -97,6 +98,6 @@ func NewListPrintFlags(printer func(h hprinters.PrintHandler)) *ListPrintFlags { // EnsureWithNamespace ensures that humanreadable flags return // a printer capable of printing with a "namespace" column. -func (f *ListPrintFlags) EnsureWithNamespace() { +func (f *PrintFlags) EnsureWithNamespace() { f.HumanReadableFlags.EnsureWithNamespace() } diff --git a/pkg/commands/flags/listprint_test.go b/pkg/commands/flags/list/print_test.go similarity index 76% rename from pkg/commands/flags/listprint_test.go rename to pkg/commands/flags/list/print_test.go index 972f15f4..30e1ba54 100644 --- a/pkg/commands/flags/listprint_test.go +++ b/pkg/commands/flags/list/print_test.go @@ -1,18 +1,20 @@ -// 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 flags +/* + Copyright 2024 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 list_test import ( "bytes" @@ -25,6 +27,7 @@ import ( metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/duration" + "knative.dev/client-pkg/pkg/commands/flags/list" "k8s.io/cli-runtime/pkg/genericclioptions" "knative.dev/client-pkg/pkg/printers" @@ -56,7 +59,7 @@ var ( ) func TestListPrintFlagsFormats(t *testing.T) { - flags := NewListPrintFlags(nil) + flags := list.NewPrintFlags(nil) formats := flags.AllowedFormats() expected := []string{"json", "yaml", "name", "go-template", "go-template-file", "template", "templatefile", "jsonpath", "jsonpath-as-json", "jsonpath-file", "no-headers"} assert.DeepEqual(t, formats, expected) @@ -64,7 +67,7 @@ func TestListPrintFlagsFormats(t *testing.T) { func TestListPrintFlags(t *testing.T) { var cmd *cobra.Command - flags := NewListPrintFlags(func(h hprinters.PrintHandler) {}) + flags := list.NewPrintFlags(func(h hprinters.PrintHandler) {}) cmd = &cobra.Command{} flags.AddFlags(cmd) @@ -82,8 +85,8 @@ func TestListPrintFlags(t *testing.T) { func TestListPrintFlagsPrint(t *testing.T) { var cmd *cobra.Command - flags := NewListPrintFlags(func(h hprinters.PrintHandler) { - h.TableHandler(columnDefs, validPrintFunc) + flags := list.NewPrintFlags(func(h hprinters.PrintHandler) { + assert.NilError(t, h.TableHandler(columnDefs, validPrintFunc)) }) cmd = &cobra.Command{} @@ -108,8 +111,8 @@ func TestListPrintFlagsPrint(t *testing.T) { func TestEnsureNamespaces(t *testing.T) { var cmd *cobra.Command - flags := NewListPrintFlags(func(h hprinters.PrintHandler) { - h.TableHandler(columnDefs, validPrintFunc) + flags := list.NewPrintFlags(func(h hprinters.PrintHandler) { + assert.NilError(t, h.TableHandler(columnDefs, validPrintFunc)) }) cmd = &cobra.Command{} diff --git a/pkg/commands/flags/sink.go b/pkg/commands/flags/sink/flag.go similarity index 55% rename from pkg/commands/flags/sink.go rename to pkg/commands/flags/sink/flag.go index 5a1c0281..ba03e586 100644 --- a/pkg/commands/flags/sink.go +++ b/pkg/commands/flags/sink/flag.go @@ -1,18 +1,20 @@ -// 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 flags +/* + Copyright 2024 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 sink import ( "context" @@ -29,32 +31,44 @@ import ( clientdynamic "knative.dev/client-pkg/pkg/dynamic" ) -type SinkFlags struct { - sink string +// Flag holds the sink flag and its mappings +type Flag struct { + Sink string + SinkMappings map[string]schema.GroupVersionResource +} + +// NewFlag is a constructor function to create Flag from provided map +func NewFlag(mapping map[string]schema.GroupVersionResource) *Flag { + return &Flag{ + SinkMappings: mapping, + } } -// AddWithFlagName configures sink flag with given flag name and a short flag name -// pass empty short flag name if you dont want to set one -func (i *SinkFlags) AddWithFlagName(cmd *cobra.Command, fname, short string) { +// AddWithFlagName configures Sink flag with given flag name and a short flag name +// pass empty short flag name if you don't want to set one +func (i *Flag) AddWithFlagName(cmd *cobra.Command, fname, short string) { flag := "--" + fname if short == "" { - cmd.Flags().StringVar(&i.sink, fname, "", "") + cmd.Flags().StringVar(&i.Sink, fname, "", "") } else { - cmd.Flags().StringVarP(&i.sink, fname, short, "", "") + cmd.Flags().StringVarP(&i.Sink, fname, short, "", "") } cmd.Flag(fname).Usage = "Addressable sink for events. " + "You can specify a broker, channel, Knative service or URI. " + "Examples: '" + flag + " broker:nest' for a broker 'nest', " + "'" + flag + " channel:pipe' for a channel 'pipe', " + "'" + flag + " ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', " + - "'" + flag + " https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, " + + "'" + flag + " https://event.receiver.uri' for an HTTP URI, " + "'" + flag + " ksvc:receiver' or simply '" + flag + " receiver' for a Knative service 'receiver' in the current namespace. " + - "If a prefix is not provided, it is considered as a Knative service in the current namespace. " + - "If referring to a Knative service in another namespace, 'ksvc:name:namespace' combination must be provided explicitly." - + "'" + flag + " special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. " + + "If a prefix is not provided, it is considered as a Knative service in the current namespace." + // Use default mapping if empty + if i.SinkMappings == nil { + i.SinkMappings = defaultSinkMappings + } for _, p := range config.GlobalConfig.SinkMappings() { //user configuration might override the default configuration - sinkMappings[p.Prefix] = schema.GroupVersionResource{ + i.SinkMappings[p.Prefix] = schema.GroupVersionResource{ Resource: p.Resource, Group: p.Group, Version: p.Version, @@ -62,13 +76,13 @@ func (i *SinkFlags) AddWithFlagName(cmd *cobra.Command, fname, short string) { } } -// Add configures sink flag with name 'sink' amd short name 's' -func (i *SinkFlags) Add(cmd *cobra.Command) { +// Add configures Sink flag with name 'Sink' amd short name 's' +func (i *Flag) Add(cmd *cobra.Command) { i.AddWithFlagName(cmd, "sink", "s") } -// sinkPrefixes maps prefixes used for sinks to their GroupVersionResources. -var sinkMappings = map[string]schema.GroupVersionResource{ +// SinkPrefixes maps prefixes used for sinks to their GroupVersionResources. +var defaultSinkMappings = map[string]schema.GroupVersionResource{ "broker": { Resource: "brokers", Group: "eventing.knative.dev", @@ -89,12 +103,16 @@ var sinkMappings = map[string]schema.GroupVersionResource{ // ResolveSink returns the Destination referred to by the flags in the acceptor. // It validates that any object the user is referring to exists. -func (i *SinkFlags) ResolveSink(ctx context.Context, knclient clientdynamic.KnDynamicClient, namespace string) (*duckv1.Destination, error) { +func (i *Flag) ResolveSink(ctx context.Context, knclient clientdynamic.KnDynamicClient, namespace string) (*duckv1.Destination, error) { client := knclient.RawClient() - if i.sink == "" { + if i.Sink == "" { return nil, nil } - prefix, name, ns := parseSink(i.sink) + // Use default mapping if empty + if i.SinkMappings == nil { + i.SinkMappings = defaultSinkMappings + } + prefix, name, ns := parseSink(i.Sink) if prefix == "" { // URI target uri, err := apis.ParseURL(name) @@ -103,10 +121,10 @@ func (i *SinkFlags) ResolveSink(ctx context.Context, knclient clientdynamic.KnDy } return &duckv1.Destination{URI: uri}, nil } - typ, ok := sinkMappings[prefix] + gvr, ok := i.SinkMappings[prefix] if !ok { if prefix == "svc" || prefix == "service" { - return nil, fmt.Errorf("unsupported sink prefix: '%s', please use prefix 'ksvc' for knative service", prefix) + return nil, fmt.Errorf("unsupported Sink prefix: '%s', please use prefix 'ksvc' for knative service", prefix) } idx := strings.LastIndex(prefix, "/") var groupVersion string @@ -116,21 +134,24 @@ func (i *SinkFlags) ResolveSink(ctx context.Context, knclient clientdynamic.KnDy } else { kind = prefix } - parsedVersion, _ := schema.ParseGroupVersion(groupVersion) + parsedVersion, err := schema.ParseGroupVersion(groupVersion) + if err != nil { + return nil, err + } + // For the RAWclient the resource name must be in lower case plural form. + // This is the best effort to sanitize the inputs, but the safest way is to provide + // the appropriate form in user's input. if !strings.HasSuffix(kind, "s") { kind = kind + "s" } - typ = schema.GroupVersionResource{ - Group: parsedVersion.Group, - Version: parsedVersion.Version, - Resource: kind, - } + kind = strings.ToLower(kind) + gvr = parsedVersion.WithResource(kind) } if ns != "" { namespace = ns } - obj, err := client.Resource(typ).Namespace(namespace).Get(ctx, name, metav1.GetOptions{}) + obj, err := client.Resource(gvr).Namespace(namespace).Get(ctx, name, metav1.GetOptions{}) if err != nil { return nil, err } @@ -163,10 +184,10 @@ func parseSink(sink string) (string, string, string) { } } -// SinkToString prepares a sink for list output +// SinkToString prepares a Sink for list output func SinkToString(sink duckv1.Destination) string { if sink.Ref != nil { - if sink.Ref.Kind == "Service" && strings.HasPrefix(sink.Ref.APIVersion, sinkMappings["ksvc"].Group) { + if sink.Ref.Kind == "Service" && strings.HasPrefix(sink.Ref.APIVersion, defaultSinkMappings["ksvc"].Group) { return fmt.Sprintf("ksvc:%s", sink.Ref.Name) } else { return fmt.Sprintf("%s:%s", strings.ToLower(sink.Ref.Kind), sink.Ref.Name) diff --git a/pkg/commands/flags/sink_test.go b/pkg/commands/flags/sink/flag_test.go similarity index 79% rename from pkg/commands/flags/sink_test.go rename to pkg/commands/flags/sink/flag_test.go index 622bd0c2..66026746 100644 --- a/pkg/commands/flags/sink_test.go +++ b/pkg/commands/flags/sink/flag_test.go @@ -1,18 +1,20 @@ -// 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 flags +/* + Copyright 2024 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 sink_test import ( "context" @@ -21,6 +23,7 @@ import ( "github.com/spf13/cobra" "gotest.tools/v3/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/client-pkg/pkg/commands/flags/sink" eventingv1 "knative.dev/eventing/pkg/apis/eventing/v1" messagingv1 "knative.dev/eventing/pkg/apis/messaging/v1" "knative.dev/eventing/pkg/apis/sources/v1beta2" @@ -58,7 +61,7 @@ func TestSinkFlagAdd(t *testing.T) { } for _, tc := range cases { c := &cobra.Command{Use: "sinktest"} - sinkFlags := new(SinkFlags) + sinkFlags := sink.Flag{} if tc.flagName == "" { sinkFlags.Add(c) assert.Equal(t, tc.expectedFlagName, c.Flag("sink").Name) @@ -72,7 +75,7 @@ func TestSinkFlagAdd(t *testing.T) { } func TestResolve(t *testing.T) { - targetExampleCom, err := apis.ParseURL("http://target.example.com") + targetExampleCom, err := apis.ParseURL("https://target.example.com") assert.NilError(t, err) mysvc := &servingv1.Service{ @@ -130,7 +133,19 @@ func TestResolve(t *testing.T) { Namespace: "default", Name: "foo", }}, ""}, - {"http://target.example.com", &duckv1.Destination{ + {"sources.knative.dev/v1/Pingsource:foo", &duckv1.Destination{Ref: &duckv1.KReference{ + APIVersion: "sources.knative.dev/v1", + Kind: "PingSource", + Namespace: "default", + Name: "foo", + }}, ""}, + {"sources.knative.dev/v1/PingSources:foo", &duckv1.Destination{Ref: &duckv1.KReference{ + APIVersion: "sources.knative.dev/v1", + Kind: "PingSource", + Namespace: "default", + Name: "foo", + }}, ""}, + {"https://target.example.com", &duckv1.Destination{ URI: targetExampleCom, }, ""}, {"k8ssvc:foo", nil, "k8ssvcs \"foo\" not found"}, @@ -141,7 +156,7 @@ func TestResolve(t *testing.T) { dynamicClient := dynamicfake.CreateFakeKnDynamicClient("default", mysvc, defaultBroker, pipeChannel, pingSource) for _, c := range cases { - i := &SinkFlags{c.sink} + i := &sink.Flag{Sink: c.sink} result, err := i.ResolveSink(context.Background(), dynamicClient, "default") if c.destination != nil { assert.DeepEqual(t, result, c.destination) @@ -185,7 +200,7 @@ func TestResolveWithNamespace(t *testing.T) { } dynamicClient := dynamicfake.CreateFakeKnDynamicClient("my-namespace", mysvc, defaultBroker, pipeChannel) for _, c := range cases { - i := &SinkFlags{c.sink} + i := &sink.Flag{Sink: c.sink} result, err := i.ResolveSink(context.Background(), dynamicClient, "default") if c.destination != nil { assert.DeepEqual(t, result, c.destination) @@ -198,34 +213,34 @@ func TestResolveWithNamespace(t *testing.T) { } func TestSinkToString(t *testing.T) { - sink := duckv1.Destination{ + dest := duckv1.Destination{ Ref: &duckv1.KReference{Kind: "Service", APIVersion: "serving.knative.dev/v1", Namespace: "my-namespace", Name: "mysvc"}} expected := "ksvc:mysvc" - assert.Equal(t, expected, SinkToString(sink)) - sink = duckv1.Destination{ + assert.Equal(t, expected, sink.SinkToString(dest)) + dest = duckv1.Destination{ Ref: &duckv1.KReference{Kind: "Broker", APIVersion: "eventing.knative.dev/v1", Namespace: "my-namespace", Name: "default"}} expected = "broker:default" - assert.Equal(t, expected, SinkToString(sink)) - sink = duckv1.Destination{ + assert.Equal(t, expected, sink.SinkToString(dest)) + dest = duckv1.Destination{ Ref: &duckv1.KReference{Kind: "Service", APIVersion: "v1", Namespace: "my-namespace", Name: "mysvc"}} expected = "service:mysvc" - assert.Equal(t, expected, SinkToString(sink)) + assert.Equal(t, expected, sink.SinkToString(dest)) - uri := "http://target.example.com" + uri := "https://target.example.com" targetExampleCom, err := apis.ParseURL(uri) assert.NilError(t, err) - sink = duckv1.Destination{ + dest = duckv1.Destination{ URI: targetExampleCom, } - assert.Equal(t, uri, SinkToString(sink)) - assert.Equal(t, "", SinkToString(duckv1.Destination{})) + assert.Equal(t, uri, sink.SinkToString(dest)) + assert.Equal(t, "", sink.SinkToString(duckv1.Destination{})) } diff --git a/pkg/commands/testing_helper_test.go b/pkg/commands/testing_helper_test.go new file mode 100644 index 00000000..f78ae967 --- /dev/null +++ b/pkg/commands/testing_helper_test.go @@ -0,0 +1,66 @@ +// Copyright © 2018 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 commands + +import ( + "fmt" + "testing" + + "github.com/spf13/cobra" + "gotest.tools/v3/assert" + + "knative.dev/client-pkg/pkg/util/test" +) + +func TestCreateTestKnCommand(t *testing.T) { + knParams := &KnParams{} + knCmd, serving, buffer := CreateTestKnCommand(&cobra.Command{Use: "fake"}, knParams) + assert.Assert(t, knCmd != nil) + assert.Assert(t, serving != nil) + assert.Assert(t, buffer != nil) + assert.Assert(t, len(knCmd.Commands()) == 1) + assert.Assert(t, knCmd.Commands()[0].Use == "fake") +} + +func TestCreateSourcesTestKnCommand(t *testing.T) { + knParams := &KnParams{} + knCmd, sources, buffer := CreateSourcesTestKnCommand(&cobra.Command{Use: "fake"}, knParams) + assert.Assert(t, knCmd != nil) + assert.Assert(t, sources != nil) + assert.Assert(t, buffer != nil) + assert.Assert(t, len(knCmd.Commands()) == 1) + assert.Assert(t, knCmd.Commands()[0].Use == "fake") +} + +func TestCreateDynamicTestKnCommand(t *testing.T) { + knParams := &KnParams{} + knCmd, dynamic, buffer := CreateDynamicTestKnCommand(&cobra.Command{Use: "fake"}, knParams) + assert.Assert(t, knCmd != nil) + assert.Assert(t, dynamic != nil) + assert.Assert(t, buffer != nil) + assert.Assert(t, len(knCmd.Commands()) == 1) + assert.Assert(t, knCmd.Commands()[0].Use == "fake") + client, err := knParams.NewDynamicClient("foo") + assert.NilError(t, err) + assert.Assert(t, client != nil) +} + +func TestCaptureStdout(t *testing.T) { + c := test.CaptureOutput(t) + fmt.Print("Hello World !") + stdOut, stdErr := c.Close() + assert.Equal(t, stdErr, "") + assert.Equal(t, stdOut, "Hello World !") +} diff --git a/pkg/commands/types.go b/pkg/commands/types.go index f1004e4b..c792b8b6 100644 --- a/pkg/commands/types.go +++ b/pkg/commands/types.go @@ -20,11 +20,13 @@ import ( "os" "path/filepath" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" eventingv1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1" - eventingv1beta1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1" + eventingv1beta2 "knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2" messagingv1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/messaging/v1" sourcesv1client "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1" sourcesv1beta2client "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1beta2" @@ -36,7 +38,7 @@ import ( clientdynamic "knative.dev/client-pkg/pkg/dynamic" knerrors "knative.dev/client-pkg/pkg/errors" clienteventingv1 "knative.dev/client-pkg/pkg/eventing/v1" - clienteventingv1beta1 "knative.dev/client-pkg/pkg/eventing/v1beta1" + clienteventingv1beta2 "knative.dev/client-pkg/pkg/eventing/v1beta2" clientmessagingv1 "knative.dev/client-pkg/pkg/messaging/v1" clientservingv1 "knative.dev/client-pkg/pkg/serving/v1" clientservingv1beta1 "knative.dev/client-pkg/pkg/serving/v1beta1" @@ -54,6 +56,7 @@ type KnParams struct { KubeAsUID string KubeAsGroup []string ClientConfig clientcmd.ClientConfig + NewKubeClient func() (kubernetes.Interface, error) NewServingClient func(namespace string) (clientservingv1.KnServingClient, error) NewServingV1beta1Client func(namespace string) (clientservingv1beta1.KnServingClient, error) NewGitopsServingClient func(namespace string, dir string) (clientservingv1.KnServingClient, error) @@ -62,7 +65,7 @@ type KnParams struct { NewEventingClient func(namespace string) (clienteventingv1.KnEventingClient, error) NewMessagingClient func(namespace string) (clientmessagingv1.KnMessagingClient, error) NewDynamicClient func(namespace string) (clientdynamic.KnDynamicClient, error) - NewEventingV1beta1Client func(namespace string) (clienteventingv1beta1.KnEventingV1Beta1Client, error) + NewEventingV1beta2Client func(namespace string) (clienteventingv1beta2.KnEventingV1Beta2Client, error) // General global options LogHTTP bool @@ -72,6 +75,10 @@ type KnParams struct { } func (params *KnParams) Initialize() { + if params.NewKubeClient == nil { + params.NewKubeClient = params.newKubeClient + } + if params.NewServingClient == nil { params.NewServingClient = params.newServingClient } @@ -104,9 +111,23 @@ func (params *KnParams) Initialize() { params.NewSourcesV1beta2Client = params.newSourcesClientV1beta2 } - if params.NewEventingV1beta1Client == nil { - params.NewEventingV1beta1Client = params.newEventingV1Beta1Client + if params.NewEventingV1beta2Client == nil { + params.NewEventingV1beta2Client = params.newEventingV1Beta2Client + } +} + +func (params *KnParams) newKubeClient() (kubernetes.Interface, error) { + restConfig, err := params.RestConfig() + if err != nil { + return nil, err + } + + client, err := kubernetes.NewForConfig(restConfig) + if err != nil { + return nil, err } + + return client, nil } func (params *KnParams) newServingClient(namespace string) (clientservingv1.KnServingClient, error) { @@ -169,14 +190,14 @@ func (params *KnParams) newEventingClient(namespace string) (clienteventingv1.Kn return clienteventingv1.NewKnEventingClient(client, namespace), nil } -func (params *KnParams) newEventingV1Beta1Client(namespace string) (clienteventingv1beta1.KnEventingV1Beta1Client, error) { +func (params *KnParams) newEventingV1Beta2Client(namespace string) (clienteventingv1beta2.KnEventingV1Beta2Client, error) { restConfig, err := params.RestConfig() if err != nil { return nil, err } - client, _ := eventingv1beta1.NewForConfig(restConfig) - return clienteventingv1beta1.NewKnEventingV1Beta1Client(client, namespace), nil + client, _ := eventingv1beta2.NewForConfig(restConfig) + return clienteventingv1beta2.NewKnEventingV1Beta2Client(client, namespace), nil } func (params *KnParams) newMessagingClient(namespace string) (clientmessagingv1.KnMessagingClient, error) { diff --git a/pkg/commands/types_test.go b/pkg/commands/types_test.go index 3ef621f0..e76aedf4 100644 --- a/pkg/commands/types_test.go +++ b/pkg/commands/types_test.go @@ -353,7 +353,7 @@ func TestNewSourcesV1beta2Client(t *testing.T) { } } -func TestNewServingV1alpha1Clients(t *testing.T) { +func TestNewServingV1beta1Clients(t *testing.T) { basic, err := clientcmd.NewClientConfigFromBytes([]byte(BASIC_KUBECONFIG)) namespace := "test" if err != nil { @@ -381,7 +381,7 @@ func TestNewServingV1alpha1Clients(t *testing.T) { LogHTTP: tc.logHttp, } - servingV1alpha1Client, err := p.newServingClientV1beta1(namespace) + servingV1beta1Client, err := p.newServingClientV1beta1(namespace) switch len(tc.expectedErrString) { case 0: @@ -397,8 +397,8 @@ func TestNewServingV1alpha1Clients(t *testing.T) { } } - if servingV1alpha1Client != nil { - assert.Assert(t, servingV1alpha1Client.Namespace() == namespace) + if servingV1beta1Client != nil { + assert.Assert(t, servingV1beta1Client.Namespace() == namespace) } } } @@ -513,7 +513,7 @@ func TestInitialize(t *testing.T) { assert.Assert(t, params.NewEventingClient != nil) assert.Assert(t, params.NewMessagingClient != nil) assert.Assert(t, params.NewDynamicClient != nil) - assert.Assert(t, params.NewEventingV1beta1Client != nil) + assert.Assert(t, params.NewEventingV1beta2Client != nil) basic, err := clientcmd.NewClientConfigFromBytes([]byte(BASIC_KUBECONFIG)) if err != nil { @@ -542,7 +542,7 @@ func TestInitialize(t *testing.T) { assert.NilError(t, err) assert.Assert(t, sourcesClient != nil) - eventingBeta1Client, err := params.NewEventingV1beta1Client("mockNamespace") + eventingBeta1Client, err := params.NewEventingV1beta2Client("mockNamespace") assert.NilError(t, err) assert.Assert(t, eventingBeta1Client != nil) } diff --git a/pkg/commands/wait_flags.go b/pkg/commands/wait_flags.go new file mode 100644 index 00000000..3ae3e4de --- /dev/null +++ b/pkg/commands/wait_flags.go @@ -0,0 +1,59 @@ +// 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 commands + +import ( + "fmt" + + "github.com/spf13/cobra" + + knflags "knative.dev/client-pkg/pkg/flags" +) + +// Default time out to use when waiting for reconciliation. It is deliberately very long as it is expected that +// the service doesn't stay in `Unknown` status very long and eventually ends up as `False` or `True` in a timely +// manner +const WaitDefaultTimeout = 600 + +// Flags for tuning wait behaviour +type WaitFlags struct { + // Timeout in seconds for how long to wait for a command to return + TimeoutInSeconds int + // If set then apply resources and wait for completion + Wait bool + // Duration in seconds for waiting between intermediate false ready conditions + ErrorWindowInSeconds int +} + +// Add flags which influence the wait/no-wait behaviour when creating, updating, waiting for +// resources. If the action is not `wait`, set `waitDefault` argument if the default behaviour is synchronous. +// Use `what` for describing what is waited for. +func (p *WaitFlags) AddConditionWaitFlags(command *cobra.Command, waitTimeoutDefault int, action, what, until string) { + if action != "wait" { + waitUsage := fmt.Sprintf("Wait for '%s %s' operation to be completed.", what, action) + waitDefault := true + // Special-case 'delete' command so it comes back to the user immediately + if action == "delete" { + waitDefault = false + } + + knflags.AddBothBoolFlagsUnhidden(command.Flags(), &p.Wait, "wait", "", waitDefault, waitUsage) + } + timeoutUsage := fmt.Sprintf("Seconds to wait before giving up on waiting for %s to be %s.", what, until) + command.Flags().IntVar(&p.TimeoutInSeconds, "wait-timeout", waitTimeoutDefault, timeoutUsage) + + windowUsage := fmt.Sprintf("Seconds to wait for %s to be %s after a false ready condition is returned", what, until) + command.Flags().IntVar(&p.ErrorWindowInSeconds, "wait-window", 2, windowUsage) +} diff --git a/pkg/commands/wait_flags_test.go b/pkg/commands/wait_flags_test.go new file mode 100644 index 00000000..14c7a7b1 --- /dev/null +++ b/pkg/commands/wait_flags_test.go @@ -0,0 +1,152 @@ +// 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 commands + +import ( + "fmt" + "strings" + "testing" + + knflags "knative.dev/client-pkg/pkg/flags" + + "github.com/spf13/cobra" + "gotest.tools/v3/assert" +) + +type waitTestCase struct { + args []string + timeoutExpected int + isWaitExpected bool + isParseErrorExpected bool +} + +func TestAddWaitForReadyDeprecatedFlags(t *testing.T) { + for i, tc := range []waitTestCase{ + {[]string{"--no-wait"}, 60, false, false}, + {[]string{}, 60, true, false}, + {[]string{"--wait-timeout=120"}, 120, true, false}, + // Can't be easily prevented, the timeout is just ignored in this case: + {[]string{"--no-wait", "--wait-timeout=120"}, 120, false, false}, + {[]string{"--wait-timeout=bla"}, 0, true, true}, + } { + + flags := &WaitFlags{} + cmd := cobra.Command{} + flags.AddConditionWaitFlags(&cmd, 60, "create", "service", "ready") + + err := cmd.ParseFlags(tc.args) + if err != nil && !tc.isParseErrorExpected { + t.Errorf("%d: parse flags: %v", i, err) + } + if err == nil && tc.isParseErrorExpected { + t.Errorf("%d: parse error expected, but got none: %v", i, err) + } + if tc.isParseErrorExpected { + continue + } + + // reconcile to ensure wait, no-wait behave as expected + err = knflags.ReconcileBoolFlags(cmd.Flags()) + assert.NilError(t, err) + + if flags.Wait != tc.isWaitExpected { + t.Errorf("%d: wrong wait mode detected: %t (expected) != %t (actual)", i, tc.isWaitExpected, flags.Wait) + } + if flags.TimeoutInSeconds != tc.timeoutExpected { + t.Errorf("%d: Invalid timeout set. %d (expected) != %d (actual)", i, tc.timeoutExpected, flags.TimeoutInSeconds) + } + } +} + +func TestAddWaitForReadyFlags(t *testing.T) { + for i, tc := range []waitTestCase{ + {[]string{}, 60, true, false}, + {[]string{"--wait-timeout=120"}, 120, true, false}, + // Can't be easily prevented, the timeout is just ignored in this case: + {[]string{"--no-wait", "--wait-timeout=120"}, 120, false, false}, + {[]string{"--wait-timeout=bla"}, 0, true, true}, + } { + + flags := &WaitFlags{} + cmd := cobra.Command{} + flags.AddConditionWaitFlags(&cmd, 60, "create", "service", "ready") + + err := cmd.ParseFlags(tc.args) + if err != nil && !tc.isParseErrorExpected { + t.Errorf("%d: parse flags: %v", i, err) + } + if err == nil && tc.isParseErrorExpected { + t.Errorf("%d: parse error expected, but got none: %v", i, err) + } + if tc.isParseErrorExpected { + continue + } + + // reconcile to ensure wait, no-wait behave as expected + err = knflags.ReconcileBoolFlags(cmd.Flags()) + assert.NilError(t, err) + fmt.Println("wait value") + fmt.Println(flags.Wait) + if flags.Wait != tc.isWaitExpected { + t.Errorf("%d: wrong wait mode detected: %t (expected) != %t (actual)", i, tc.isWaitExpected, flags.Wait) + } + if flags.TimeoutInSeconds != tc.timeoutExpected { + t.Errorf("%d: Invalid timeout set. %d (expected) != %d (actual)", i, tc.timeoutExpected, flags.TimeoutInSeconds) + } + } +} + +func TestAddWaitUsageMessage(t *testing.T) { + + flags := &WaitFlags{} + cmd := cobra.Command{} + flags.AddConditionWaitFlags(&cmd, 60, "bla", "blub", "deleted") + if !strings.Contains(cmd.UsageString(), "blub") { + t.Error("no type returned in usage") + } + if !strings.Contains(cmd.UsageString(), "Do not wait") { + t.Error("wrong usage message") + } + if !strings.Contains(cmd.UsageString(), "60") { + t.Error("default timeout not contained") + } + if !strings.Contains(cmd.UsageString(), "deleted") { + t.Error("wrong until message") + } +} + +func TestAddWaitUsageDelete(t *testing.T) { + flags := &WaitFlags{} + cmd := cobra.Command{} + flags.AddConditionWaitFlags(&cmd, 60, "delete", "blub", "deleted") + if !strings.Contains(cmd.UsageString(), "completed. (default true)") { + t.Error("Delete has wrong default value for --no-wait") + } +} + +func TestAddWaitUsageWait(t *testing.T) { + flags := &WaitFlags{} + cmd := cobra.Command{} + flags.AddConditionWaitFlags(&cmd, 60, "wait", "blub", "ready") + if !strings.Contains(cmd.UsageString(), "blub") { + t.Error("no type returned in usage") + } + if !strings.Contains(cmd.UsageString(), "60") { + t.Error("default timeout not contained") + } + if !strings.Contains(cmd.UsageString(), "ready") { + t.Error("wrong until message") + } +} diff --git a/pkg/errors/factory_test.go b/pkg/errors/factory_test.go index 1a5e6956..03f08b7c 100644 --- a/pkg/errors/factory_test.go +++ b/pkg/errors/factory_test.go @@ -167,9 +167,10 @@ func TestIsForbiddenError(t *testing.T) { }, } for _, tc := range cases { + itc := tc t.Run(tc.Name, func(t *testing.T) { t.Parallel() - assert.Equal(t, IsForbiddenError(GetError(tc.Error)), tc.Forbidden) + assert.Equal(t, IsForbiddenError(GetError(itc.Error)), itc.Forbidden) }) } } @@ -207,9 +208,10 @@ func TestIsInternalError(t *testing.T) { } for _, tc := range cases { + itc := tc t.Run(tc.Name, func(t *testing.T) { t.Parallel() - assert.Equal(t, api_errors.IsInternalError(GetError(tc.Error)), tc.Internal) + assert.Equal(t, api_errors.IsInternalError(GetError(itc.Error)), itc.Internal) }) } } @@ -240,9 +242,10 @@ func TestStatusError(t *testing.T) { }, } for _, tc := range cases { + itc := tc t.Run(tc.Name, func(t *testing.T) { t.Parallel() - assert.Assert(t, tc.ErrorType(GetError(tc.Error))) + assert.Assert(t, itc.ErrorType(GetError(itc.Error))) }) } } diff --git a/pkg/eventing/v1/client_mock.go b/pkg/eventing/v1/client_mock.go new file mode 100644 index 00000000..b95d2561 --- /dev/null +++ b/pkg/eventing/v1/client_mock.go @@ -0,0 +1,183 @@ +// 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 v1 + +import ( + "context" + "testing" + "time" + + eventingv1 "knative.dev/eventing/pkg/apis/eventing/v1" + + "knative.dev/client-pkg/pkg/util/mock" +) + +// MockKnEventingClient is a combine of test object and recorder +type MockKnEventingClient struct { + t *testing.T + recorder *EventingRecorder +} + +// NewMockKnEventingClient returns a new mock instance which you need to record for +func NewMockKnEventingClient(t *testing.T, ns ...string) *MockKnEventingClient { + namespace := "default" + if len(ns) > 0 { + namespace = ns[0] + } + return &MockKnEventingClient{ + t: t, + recorder: &EventingRecorder{mock.NewRecorder(t, namespace)}, + } +} + +// Ensure that the interface is implemented +var _ KnEventingClient = &MockKnEventingClient{} + +// EventingRecorder is recorder for eventing objects +type EventingRecorder struct { + r *mock.Recorder +} + +// Recorder returns the recorder for registering API calls +func (c *MockKnEventingClient) Recorder() *EventingRecorder { + return c.recorder +} + +// Namespace of this client +func (c *MockKnEventingClient) Namespace() string { + return c.recorder.r.Namespace() +} + +// CreateTrigger records a call for CreatePingSource with the expected error +func (sr *EventingRecorder) CreateTrigger(trigger interface{}, err error) { + sr.r.Add("CreateTrigger", []interface{}{trigger}, []interface{}{err}) +} + +// CreateTrigger performs a previously recorded action +func (c *MockKnEventingClient) CreateTrigger(ctx context.Context, trigger *eventingv1.Trigger) error { + call := c.recorder.r.VerifyCall("CreateTrigger", trigger) + return mock.ErrorOrNil(call.Result[0]) +} + +// GetTrigger records a call for GetTrigger with the expected object or error. Either trigger or err should be nil +func (sr *EventingRecorder) GetTrigger(name interface{}, trigger *eventingv1.Trigger, err error) { + sr.r.Add("GetTrigger", []interface{}{name}, []interface{}{trigger, err}) +} + +// GetTrigger performs a previously recorded action +func (c *MockKnEventingClient) GetTrigger(ctx context.Context, name string) (*eventingv1.Trigger, error) { + call := c.recorder.r.VerifyCall("GetTrigger", name) + return call.Result[0].(*eventingv1.Trigger), mock.ErrorOrNil(call.Result[1]) +} + +// DeleteTrigger records a call for DeleteTrigger with the expected error (nil if none) +func (sr *EventingRecorder) DeleteTrigger(name interface{}, err error) { + sr.r.Add("DeleteTrigger", []interface{}{name}, []interface{}{err}) +} + +// DeleteTrigger performs a previously recorded action, failing if non has been registered +func (c *MockKnEventingClient) DeleteTrigger(ctx context.Context, name string) error { + call := c.recorder.r.VerifyCall("DeleteTrigger", name) + return mock.ErrorOrNil(call.Result[0]) +} + +// ListTriggers records a call for ListTriggers with the expected result and error (nil if none) +func (sr *EventingRecorder) ListTriggers(triggerList *eventingv1.TriggerList, err error) { + sr.r.Add("ListTriggers", nil, []interface{}{triggerList, err}) +} + +// ListTriggers performs a previously recorded action +func (c *MockKnEventingClient) ListTriggers(context.Context) (*eventingv1.TriggerList, error) { + call := c.recorder.r.VerifyCall("ListTriggers") + return call.Result[0].(*eventingv1.TriggerList), mock.ErrorOrNil(call.Result[1]) +} + +// UpdateTrigger records a call for UpdateTrigger with the expected result and error (nil if none) +func (sr *EventingRecorder) UpdateTrigger(trigger interface{}, err error) { + sr.r.Add("UpdateTrigger", []interface{}{trigger}, []interface{}{err}) +} + +// UpdateTrigger performs a previously recorded action +func (c *MockKnEventingClient) UpdateTrigger(ctx context.Context, trigger *eventingv1.Trigger) error { + call := c.recorder.r.VerifyCall("UpdateTrigger") + return mock.ErrorOrNil(call.Result[0]) +} + +func (c *MockKnEventingClient) UpdateTriggerWithRetry(ctx context.Context, name string, updateFunc TriggerUpdateFunc, nrRetries int) error { + return updateTriggerWithRetry(ctx, c, name, updateFunc, nrRetries) +} + +// CreateBroker records a call for CreateBroker with the expected error +func (sr *EventingRecorder) CreateBroker(broker interface{}, err error) { + sr.r.Add("CreateBroker", []interface{}{broker}, []interface{}{err}) +} + +// CreateBroker performs a previously recorded action +func (c *MockKnEventingClient) CreateBroker(ctx context.Context, broker *eventingv1.Broker) error { + call := c.recorder.r.VerifyCall("CreateBroker", broker) + return mock.ErrorOrNil(call.Result[0]) +} + +// GetBroker records a call for GetBroker with the expected object or error. Either trigger or err should be nil +func (sr *EventingRecorder) GetBroker(name interface{}, broker *eventingv1.Broker, err error) { + sr.r.Add("GetBroker", []interface{}{name}, []interface{}{broker, err}) +} + +// GetBroker performs a previously recorded action +func (c *MockKnEventingClient) GetBroker(ctx context.Context, name string) (*eventingv1.Broker, error) { + call := c.recorder.r.VerifyCall("GetBroker", name) + return call.Result[0].(*eventingv1.Broker), mock.ErrorOrNil(call.Result[1]) +} + +// DeleteBroker records a call for DeleteBroker with the expected error (nil if none) +func (sr *EventingRecorder) DeleteBroker(name, timeout interface{}, err error) { + sr.r.Add("DeleteBroker", []interface{}{name, timeout}, []interface{}{err}) +} + +// DeleteBroker performs a previously recorded action, failing if non has been registered +func (c *MockKnEventingClient) DeleteBroker(ctx context.Context, name string, timeout time.Duration) error { + call := c.recorder.r.VerifyCall("DeleteBroker", name, timeout) + return mock.ErrorOrNil(call.Result[0]) +} + +// ListBrokers records a call for ListBrokers with the expected result and error (nil if none) +func (sr *EventingRecorder) ListBrokers(brokerList *eventingv1.BrokerList, err error) { + sr.r.Add("ListBrokers", nil, []interface{}{brokerList, err}) +} + +// ListBrokers performs a previously recorded action +func (c *MockKnEventingClient) ListBrokers(context.Context) (*eventingv1.BrokerList, error) { + call := c.recorder.r.VerifyCall("ListBrokers") + return call.Result[0].(*eventingv1.BrokerList), mock.ErrorOrNil(call.Result[1]) +} + +// UpdateBroker records a call for UpdateBroker with the expected result and error (nil if none) +func (sr *EventingRecorder) UpdateBroker(broker *eventingv1.Broker, err error) { + sr.r.Add("UpdateBroker", []interface{}{broker}, []interface{}{err}) +} + +func (c *MockKnEventingClient) UpdateBroker(ctx context.Context, broker *eventingv1.Broker) error { + call := c.recorder.r.VerifyCall("UpdateBroker") + return mock.ErrorOrNil(call.Result[0]) +} + +func (c *MockKnEventingClient) UpdateBrokerWithRetry(ctx context.Context, name string, updateFunc BrokerUpdateFunc, nrRetries int) error { + return updateBrokerWithRetry(ctx, c, name, updateFunc, nrRetries) +} + +// Validate validates whether every recorded action has been called +func (sr *EventingRecorder) Validate() { + sr.r.CheckThatAllRecordedMethodsHaveBeenCalled() +} diff --git a/pkg/eventing/v1/client_mock_test.go b/pkg/eventing/v1/client_mock_test.go new file mode 100644 index 00000000..7a84792d --- /dev/null +++ b/pkg/eventing/v1/client_mock_test.go @@ -0,0 +1,70 @@ +// 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 v1 + +import ( + "context" + "testing" + "time" + + eventingv1 "knative.dev/eventing/pkg/apis/eventing/v1" +) + +func TestMockKnClient(t *testing.T) { + + client := NewMockKnEventingClient(t) + + recorder := client.Recorder() + + // Record all services + recorder.GetTrigger("hello", nil, nil) + recorder.CreateTrigger(&eventingv1.Trigger{}, nil) + recorder.DeleteTrigger("hello", nil) + recorder.ListTriggers(nil, nil) + recorder.UpdateTrigger(&eventingv1.Trigger{}, nil) + recorder.GetTrigger("hello", &eventingv1.Trigger{}, nil) + recorder.UpdateTrigger(&eventingv1.Trigger{}, nil) + + recorder.CreateBroker(&eventingv1.Broker{}, nil) + recorder.GetBroker("foo", nil, nil) + recorder.DeleteBroker("foo", time.Duration(10)*time.Second, nil) + recorder.ListBrokers(nil, nil) + recorder.GetBroker("foo", &eventingv1.Broker{}, nil) + recorder.UpdateBroker(&eventingv1.Broker{}, nil) + recorder.UpdateBroker(&eventingv1.Broker{}, nil) + + // Call all service + ctx := context.Background() + client.GetTrigger(ctx, "hello") + client.CreateTrigger(ctx, &eventingv1.Trigger{}) + client.DeleteTrigger(ctx, "hello") + client.ListTriggers(ctx) + client.UpdateTrigger(ctx, &eventingv1.Trigger{}) + client.UpdateTriggerWithRetry(ctx, "hello", func(origTrigger *eventingv1.Trigger) (*eventingv1.Trigger, error) { + return origTrigger, nil + }, 10) + + client.CreateBroker(ctx, &eventingv1.Broker{}) + client.GetBroker(ctx, "foo") + client.DeleteBroker(ctx, "foo", time.Duration(10)*time.Second) + client.ListBrokers(ctx) + client.UpdateBroker(ctx, &eventingv1.Broker{}) + client.UpdateBrokerWithRetry(ctx, "foo", func(origBroker *eventingv1.Broker) (*eventingv1.Broker, error) { + return origBroker, nil + }, 10) + + // Validate + recorder.Validate() +} diff --git a/pkg/eventing/v1beta1/client.go b/pkg/eventing/v1beta2/client.go similarity index 74% rename from pkg/eventing/v1beta1/client.go rename to pkg/eventing/v1beta2/client.go index 8572bee1..b1b713c2 100644 --- a/pkg/eventing/v1beta1/client.go +++ b/pkg/eventing/v1beta2/client.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package v1beta1 +package v1beta2 import ( "context" @@ -21,35 +21,37 @@ import ( "k8s.io/apimachinery/pkg/runtime" kn_errors "knative.dev/client-pkg/pkg/errors" "knative.dev/client-pkg/pkg/util" - eventingv1beta1 "knative.dev/eventing/pkg/apis/eventing/v1beta1" + eventingv1 "knative.dev/eventing/pkg/apis/eventing/v1" + eventingv1beta2 "knative.dev/eventing/pkg/apis/eventing/v1beta2" "knative.dev/eventing/pkg/client/clientset/versioned/scheme" - beta1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1" + beta1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2" "knative.dev/pkg/apis" + v1 "knative.dev/pkg/apis/duck/v1" ) -// KnEventingV1Beta1Client to Eventing Sources. All methods are relative to the +// KnEventingV1Beta2Client to Eventing Sources. All methods are relative to the // namespace specified during construction -type KnEventingV1Beta1Client interface { +type KnEventingV1Beta2Client interface { // Namespace in which this client is operating for Namespace() string // ListEventtypes is used to list eventtypes - ListEventtypes(ctx context.Context) (*eventingv1beta1.EventTypeList, error) + ListEventtypes(ctx context.Context) (*eventingv1beta2.EventTypeList, error) // GetEventtype is used to describe an eventtype - GetEventtype(ctx context.Context, name string) (*eventingv1beta1.EventType, error) + GetEventtype(ctx context.Context, name string) (*eventingv1beta2.EventType, error) // CreateEventtype is used to create an eventtype - CreateEventtype(ctx context.Context, eventtype *eventingv1beta1.EventType) error + CreateEventtype(ctx context.Context, eventtype *eventingv1beta2.EventType) error // DeleteEventtype is used to delete an eventtype DeleteEventtype(ctx context.Context, name string) error } -// KnEventingV1Beta1Client is a client for eventing v1beta1 resources +// KnEventingV1Beta2Client is a client for eventing v1beta2 resources type knEventingV1Beta1Client struct { - client beta1.EventingV1beta1Interface + client beta1.EventingV1beta2Interface namespace string } -// NewKnEventingV1Beta1Client is to invoke Eventing Types Client API to create object -func NewKnEventingV1Beta1Client(client beta1.EventingV1beta1Interface, namespace string) KnEventingV1Beta1Client { +// NewKnEventingV1Beta2Client is to invoke Eventing Types Client API to create object +func NewKnEventingV1Beta2Client(client beta1.EventingV1beta2Interface, namespace string) KnEventingV1Beta2Client { return &knEventingV1Beta1Client{ client: client, namespace: namespace, @@ -57,14 +59,14 @@ func NewKnEventingV1Beta1Client(client beta1.EventingV1beta1Interface, namespace } func updateEventingBeta1GVK(obj runtime.Object) error { - return util.UpdateGroupVersionKindWithScheme(obj, eventingv1beta1.SchemeGroupVersion, scheme.Scheme) + return util.UpdateGroupVersionKindWithScheme(obj, eventingv1beta2.SchemeGroupVersion, scheme.Scheme) } func (c *knEventingV1Beta1Client) Namespace() string { return c.namespace } -func (c *knEventingV1Beta1Client) ListEventtypes(ctx context.Context) (*eventingv1beta1.EventTypeList, error) { +func (c *knEventingV1Beta1Client) ListEventtypes(ctx context.Context) (*eventingv1beta2.EventTypeList, error) { eventTypeList, err := c.client.EventTypes(c.namespace).List(ctx, apis_v1.ListOptions{}) if err != nil { return nil, kn_errors.GetError(err) @@ -75,7 +77,7 @@ func (c *knEventingV1Beta1Client) ListEventtypes(ctx context.Context) (*eventing return nil, err } - listNew.Items = make([]eventingv1beta1.EventType, len(eventTypeList.Items)) + listNew.Items = make([]eventingv1beta2.EventType, len(eventTypeList.Items)) for idx, eventType := range eventTypeList.Items { clone := eventType.DeepCopy() err := updateEventingBeta1GVK(clone) @@ -87,7 +89,7 @@ func (c *knEventingV1Beta1Client) ListEventtypes(ctx context.Context) (*eventing return listNew, nil } -func (c *knEventingV1Beta1Client) GetEventtype(ctx context.Context, name string) (*eventingv1beta1.EventType, error) { +func (c *knEventingV1Beta1Client) GetEventtype(ctx context.Context, name string) (*eventingv1beta2.EventType, error) { eventType, err := c.client.EventTypes(c.namespace).Get(ctx, name, apis_v1.GetOptions{}) if err != nil { return nil, kn_errors.GetError(err) @@ -107,7 +109,7 @@ func (c *knEventingV1Beta1Client) DeleteEventtype(ctx context.Context, name stri return nil } -func (c *knEventingV1Beta1Client) CreateEventtype(ctx context.Context, eventtype *eventingv1beta1.EventType) error { +func (c *knEventingV1Beta1Client) CreateEventtype(ctx context.Context, eventtype *eventingv1beta2.EventType) error { _, err := c.client.EventTypes(c.namespace).Create(ctx, eventtype, apis_v1.CreateOptions{}) if err != nil { return kn_errors.GetError(err) @@ -117,12 +119,12 @@ func (c *knEventingV1Beta1Client) CreateEventtype(ctx context.Context, eventtype // EventtypeBuilder is for building the eventtype type EventtypeBuilder struct { - eventtype *eventingv1beta1.EventType + eventtype *eventingv1beta2.EventType } // NewEventtypeBuilder for building eventtype object func NewEventtypeBuilder(name string) *EventtypeBuilder { - return &EventtypeBuilder{eventtype: &eventingv1beta1.EventType{ + return &EventtypeBuilder{eventtype: &eventingv1beta2.EventType{ ObjectMeta: apis_v1.ObjectMeta{ Name: name, }, @@ -155,11 +157,21 @@ func (e *EventtypeBuilder) Source(source *apis.URL) *EventtypeBuilder { // Broker for eventtype builder func (e *EventtypeBuilder) Broker(broker string) *EventtypeBuilder { - e.eventtype.Spec.Broker = broker + e.eventtype.Spec.Reference = &v1.KReference{ + APIVersion: eventingv1.SchemeGroupVersion.String(), + Kind: "Broker", + Name: broker, + } + return e +} + +// Reference for eventtype builder +func (e *EventtypeBuilder) Reference(ref *v1.KReference) *EventtypeBuilder { + e.eventtype.Spec.Reference = ref return e } // Build to return an instance of eventtype object -func (e *EventtypeBuilder) Build() *eventingv1beta1.EventType { +func (e *EventtypeBuilder) Build() *eventingv1beta2.EventType { return e.eventtype } diff --git a/pkg/eventing/v1beta2/client_mock.go b/pkg/eventing/v1beta2/client_mock.go new file mode 100644 index 00000000..b026f1ef --- /dev/null +++ b/pkg/eventing/v1beta2/client_mock.go @@ -0,0 +1,106 @@ +// Copyright © 2022 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 v1beta2 + +import ( + "context" + "testing" + + eventingv1beta2 "knative.dev/eventing/pkg/apis/eventing/v1beta2" + + "knative.dev/client-pkg/pkg/util/mock" +) + +// MockKnEventingV1beta2Client is a combine of test object and recorder +type MockKnEventingV1beta2Client struct { + t *testing.T + recorder *EventingV1beta2Recorder +} + +// NewMockKnEventingV1beta2Client returns a new mock instance which you need to record for +func NewMockKnEventingV1beta2Client(t *testing.T, ns ...string) *MockKnEventingV1beta2Client { + namespace := "default" + if len(ns) > 0 { + namespace = ns[0] + } + return &MockKnEventingV1beta2Client{ + t: t, + recorder: &EventingV1beta2Recorder{mock.NewRecorder(t, namespace)}, + } +} + +// Ensure that the interface is implemented +var _ KnEventingV1Beta2Client = &MockKnEventingV1beta2Client{} + +// EventingV1beta2Recorder is recorder for eventingv1beta2 objects +type EventingV1beta2Recorder struct { + r *mock.Recorder +} + +// Recorder returns the recorder for registering API calls +func (c *MockKnEventingV1beta2Client) Recorder() *EventingV1beta2Recorder { + return c.recorder +} + +// Namespace of this client +func (c *MockKnEventingV1beta2Client) Namespace() string { + return c.recorder.r.Namespace() +} + +// ListEventtypes records a call for ListEventtypes with the expected result and error (nil if none) +func (sr *EventingV1beta2Recorder) ListEventtypes(eventtypeList *eventingv1beta2.EventTypeList, err error) { + sr.r.Add("ListEventtypes", nil, []interface{}{eventtypeList, err}) +} + +func (c *MockKnEventingV1beta2Client) ListEventtypes(ctx context.Context) (*eventingv1beta2.EventTypeList, error) { + call := c.recorder.r.VerifyCall("ListEventtypes") + return call.Result[0].(*eventingv1beta2.EventTypeList), mock.ErrorOrNil(call.Result[1]) +} + +// GetEventtype records a call for GetEventtype with the expected result and error (nil if none) +func (sr *EventingV1beta2Recorder) GetEventtype(name string, eventtype *eventingv1beta2.EventType, err error) { + sr.r.Add("GetEventtype", []interface{}{name}, []interface{}{eventtype, err}) +} + +// GetEventtypes records a call for GetEventtype with the expected object or error. Either eventtype or err should be nil +func (c *MockKnEventingV1beta2Client) GetEventtype(ctx context.Context, name string) (*eventingv1beta2.EventType, error) { + call := c.recorder.r.VerifyCall("GetEventtype", name) + return call.Result[0].(*eventingv1beta2.EventType), mock.ErrorOrNil(call.Result[1]) +} + +// CreateEventtype records a call for CreateEventtype with the expected error +func (sr *EventingV1beta2Recorder) CreateEventtype(eventtype interface{}, err error) { + sr.r.Add("CreateEventtype", []interface{}{eventtype}, []interface{}{err}) +} + +func (c *MockKnEventingV1beta2Client) CreateEventtype(ctx context.Context, eventtype *eventingv1beta2.EventType) error { + call := c.recorder.r.VerifyCall("CreateEventtype", eventtype) + return mock.ErrorOrNil(call.Result[0]) +} + +// DeleteEventtype records a call for DeleteEventtype with the expected error +func (sr *EventingV1beta2Recorder) DeleteEventtype(name interface{}, err error) { + sr.r.Add("DeleteEventtype", []interface{}{name}, []interface{}{err}) +} + +func (c *MockKnEventingV1beta2Client) DeleteEventtype(ctx context.Context, name string) error { + call := c.recorder.r.VerifyCall("DeleteEventtype", name) + return mock.ErrorOrNil(call.Result[0]) +} + +// Validate validates whether every recorded action has been called +func (sr *EventingV1beta2Recorder) Validate() { + sr.r.CheckThatAllRecordedMethodsHaveBeenCalled() +} diff --git a/pkg/eventing/v1beta2/client_mock_test.go b/pkg/eventing/v1beta2/client_mock_test.go new file mode 100644 index 00000000..b4160fea --- /dev/null +++ b/pkg/eventing/v1beta2/client_mock_test.go @@ -0,0 +1,41 @@ +// Copyright © 2022 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 v1beta2 + +import ( + "context" + "testing" + + "knative.dev/eventing/pkg/apis/eventing/v1beta2" +) + +func TestMockKnClient(t *testing.T) { + client := NewMockKnEventingV1beta2Client(t, "test-ns") + + recorder := client.Recorder() + + recorder.CreateEventtype(&v1beta2.EventType{}, nil) + recorder.GetEventtype("eventtype-name", &v1beta2.EventType{}, nil) + recorder.DeleteEventtype("eventtype-name", nil) + recorder.ListEventtypes(&v1beta2.EventTypeList{}, nil) + + ctx := context.Background() + client.CreateEventtype(ctx, &v1beta2.EventType{}) + client.GetEventtype(ctx, "eventtype-name") + client.DeleteEventtype(ctx, "eventtype-name") + client.ListEventtypes(ctx) + + recorder.Validate() +} diff --git a/pkg/eventing/v1beta1/client_test.go b/pkg/eventing/v1beta2/client_test.go similarity index 80% rename from pkg/eventing/v1beta1/client_test.go rename to pkg/eventing/v1beta2/client_test.go index 85715462..ebe0c076 100644 --- a/pkg/eventing/v1beta1/client_test.go +++ b/pkg/eventing/v1beta2/client_test.go @@ -12,19 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -package v1beta1 +package v1beta2 import ( "context" "fmt" "testing" + eventingv1 "knative.dev/eventing/pkg/apis/eventing/v1" + v1 "knative.dev/pkg/apis/duck/v1" + "gotest.tools/v3/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" client_testing "k8s.io/client-go/testing" - "knative.dev/eventing/pkg/apis/eventing/v1beta1" - "knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/fake" + "knative.dev/eventing/pkg/apis/eventing/v1beta2" + "knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/fake" "knative.dev/pkg/apis" ) @@ -37,9 +40,9 @@ const ( errName = "error-eventtype" ) -func setup(ns string) (fakeSvr fake.FakeEventingV1beta1, client KnEventingV1Beta1Client) { - fakeE := fake.FakeEventingV1beta1{Fake: &client_testing.Fake{}} - cli := NewKnEventingV1Beta1Client(&fakeE, ns) +func setup(ns string) (fakeSvr fake.FakeEventingV1beta2, client KnEventingV1Beta2Client) { + fakeE := fake.FakeEventingV1beta2{Fake: &client_testing.Fake{}} + cli := NewKnEventingV1Beta2Client(&fakeE, ns) return fakeE, cli } @@ -51,7 +54,21 @@ func TestNamespace(t *testing.T) { func TestBuilder(t *testing.T) { et := newEventtypeWithSourceBroker(testName, testSource, testBroker) assert.Equal(t, et.Name, testName) - assert.Equal(t, et.Spec.Broker, testBroker) + assert.Equal(t, et.Spec.Reference.Name, testBroker) + source := et.Spec.Source + assert.Assert(t, source != nil) + assert.Equal(t, source.String(), testSource) +} + +func TestBuilderWithRefence(t *testing.T) { + ref := &v1.KReference{ + APIVersion: eventingv1.SchemeGroupVersion.String(), + Kind: "Broker", + Name: testBroker, + } + et := newEventtypeWithSourceRef(testName, testSource, ref) + assert.Equal(t, et.Name, testName) + assert.Equal(t, et.Spec.Reference, ref) source := et.Spec.Source assert.Assert(t, source != nil) assert.Equal(t, source.String(), testSource) @@ -147,7 +164,7 @@ func TestKnEventingV1Beta1Client_ListEventtypes(t *testing.T) { func(a client_testing.Action) (bool, runtime.Object, error) { assert.Equal(t, testNamespace, a.GetNamespace()) - return true, &v1beta1.EventTypeList{Items: []v1beta1.EventType{ + return true, &v1beta2.EventTypeList{Items: []v1beta2.EventType{ *newEventtype("eventtype-1"), *newEventtype("eventtype-2")}}, nil }) @@ -161,7 +178,7 @@ func TestKnEventingV1Beta1Client_ListEventtypes(t *testing.T) { assert.Equal(t, list.Items[1].Name, "eventtype-2") } -func newEventtypeWithSourceBroker(name string, source string, broker string) *v1beta1.EventType { +func newEventtypeWithSourceBroker(name string, source string, broker string) *v1beta2.EventType { url, _ := apis.ParseURL(source) return NewEventtypeBuilder(name). Namespace(testNamespace). @@ -172,7 +189,18 @@ func newEventtypeWithSourceBroker(name string, source string, broker string) *v1 Build() } -func newEventtype(name string) *v1beta1.EventType { +func newEventtypeWithSourceRef(name string, source string, ref *v1.KReference) *v1beta2.EventType { + url, _ := apis.ParseURL(source) + return NewEventtypeBuilder(name). + Namespace(testNamespace). + WithGvk(). + Type(testType). + Source(url). + Reference(ref). + Build() +} + +func newEventtype(name string) *v1beta2.EventType { return NewEventtypeBuilder(name). Namespace(testNamespace). Type(testType). diff --git a/pkg/flags/podspec_helper.go b/pkg/flags/podspec_helper.go index 4a2dfd12..d1d9f1b4 100644 --- a/pkg/flags/podspec_helper.go +++ b/pkg/flags/podspec_helper.go @@ -20,6 +20,8 @@ import ( "strconv" "strings" + "k8s.io/utils/pointer" + "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/api/resource" @@ -378,6 +380,37 @@ func UpdateImagePullPolicy(spec *corev1.PodSpec, imagePullPolicy string) error { return nil } +// UpdateSecurityContext update the Security Context +func UpdateSecurityContext(spec *corev1.PodSpec, securityContext string) error { + container := containerOfPodSpec(spec) + switch strings.ToLower(securityContext) { + case "none": + // Blank any Security Context defined + container.SecurityContext = nil + case "strict": + // Add or update Security Context to default strict + container.SecurityContext = DefaultStrictSecCon() + //TODO(dsimansk): add parsing of SC options from the flag value + default: + return fmt.Errorf("invalid --security-context %s. Valid arguments: strict | none", securityContext) + } + return nil +} + +// DefaultStrictSecCon helper function to get default strict Security Context +func DefaultStrictSecCon() *corev1.SecurityContext { + return &corev1.SecurityContext{ + AllowPrivilegeEscalation: pointer.Bool(false), + RunAsNonRoot: pointer.Bool(true), + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"ALL"}, + }, + SeccompProfile: &corev1.SeccompProfile{ + Type: corev1.SeccompProfileTypeRuntimeDefault, + }, + } +} + func getPolicy(policy string) v1.PullPolicy { var ret v1.PullPolicy switch strings.ToLower(policy) { diff --git a/pkg/flags/podspec_helper_test.go b/pkg/flags/podspec_helper_test.go index 04eafd8f..e8e615ac 100644 --- a/pkg/flags/podspec_helper_test.go +++ b/pkg/flags/podspec_helper_test.go @@ -24,13 +24,12 @@ import ( "strings" "testing" - "k8s.io/apimachinery/pkg/util/intstr" - "knative.dev/client-pkg/pkg/util/test" - "gotest.tools/v3/assert" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/util/intstr" "knative.dev/client-pkg/pkg/util" + "knative.dev/client-pkg/pkg/util/test" "knative.dev/pkg/ptr" ) diff --git a/pkg/kn-source-pkg/pkg/factories/kn_source_factory.go b/pkg/kn-source-pkg/pkg/factories/kn_source_factory.go index a8e11e56..7762fa59 100644 --- a/pkg/kn-source-pkg/pkg/factories/kn_source_factory.go +++ b/pkg/kn-source-pkg/pkg/factories/kn_source_factory.go @@ -16,7 +16,7 @@ package factories import ( "k8s.io/client-go/rest" - "knative.dev/client-pkg/pkg/commands/flags" + "knative.dev/client-pkg/pkg/commands/flags/sink" "knative.dev/client-pkg/pkg/kn-source-pkg/pkg/client" "knative.dev/client-pkg/pkg/kn-source-pkg/pkg/types" ) @@ -37,7 +37,7 @@ func NewDefaultKnSourceFactory() types.KnSourceFactory { func (f *DefautKnSourceFactory) CreateKnSourceParams() *types.KnSourceParams { f.knSourceParams = &types.KnSourceParams{ - SinkFlag: flags.SinkFlags{}, + SinkFlag: sink.Flag{}, } f.knSourceParams.Initialize() return f.knSourceParams diff --git a/pkg/kn-source-pkg/pkg/factories/kn_source_factory_test.go b/pkg/kn-source-pkg/pkg/factories/kn_source_factory_test.go index aa3053a9..821d6341 100644 --- a/pkg/kn-source-pkg/pkg/factories/kn_source_factory_test.go +++ b/pkg/kn-source-pkg/pkg/factories/kn_source_factory_test.go @@ -19,7 +19,7 @@ import ( "gotest.tools/v3/assert" "k8s.io/client-go/rest" - "knative.dev/client-pkg/pkg/commands/flags" + "knative.dev/client-pkg/pkg/commands/flags/sink" "knative.dev/client-pkg/pkg/kn-source-pkg/pkg/types" "knative.dev/client-pkg/pkg/kn-source-pkg/pkg/types/typesfakes" ) @@ -49,7 +49,7 @@ func TestCreateKnSourceClient(t *testing.T) { func newDefaultKnSourceFactory() types.KnSourceFactory { return &DefautKnSourceFactory{ - knSourceParams: &types.KnSourceParams{SinkFlag: flags.SinkFlags{}}, + knSourceParams: &types.KnSourceParams{SinkFlag: sink.Flag{}}, knSourceClientFunc: fakeKnSourceClientFunc, } } diff --git a/pkg/kn-source-pkg/pkg/types/structs.go b/pkg/kn-source-pkg/pkg/types/structs.go index 82ad787f..bf965ea6 100644 --- a/pkg/kn-source-pkg/pkg/types/structs.go +++ b/pkg/kn-source-pkg/pkg/types/structs.go @@ -19,13 +19,13 @@ package types import ( "github.com/spf13/cobra" "knative.dev/client-pkg/pkg/commands" - "knative.dev/client-pkg/pkg/commands/flags" + "knative.dev/client-pkg/pkg/commands/flags/sink" ) type KnSourceParams struct { commands.KnParams - SinkFlag flags.SinkFlags + SinkFlag sink.Flag } func (p *KnSourceParams) AddCommonFlags(cmd *cobra.Command) { diff --git a/pkg/kn-source-pkg/pkg/types/structs_test.go b/pkg/kn-source-pkg/pkg/types/structs_test.go index 78bf1e2b..9b8bcd5f 100644 --- a/pkg/kn-source-pkg/pkg/types/structs_test.go +++ b/pkg/kn-source-pkg/pkg/types/structs_test.go @@ -19,7 +19,7 @@ import ( "github.com/spf13/cobra" "gotest.tools/v3/assert" - "knative.dev/client-pkg/pkg/commands/flags" + "knative.dev/client-pkg/pkg/commands/flags/sink" ) func TestAddCommonFlags(t *testing.T) { @@ -52,6 +52,6 @@ func TestAddCreateUpdateFlags(t *testing.T) { func newFakeKnSourceParams() *KnSourceParams { return &KnSourceParams{ - SinkFlag: flags.SinkFlags{}, + SinkFlag: sink.Flag{}, } } diff --git a/pkg/printers/tablegenerator.go b/pkg/printers/tablegenerator.go index 27fd94be..95fd6d8a 100644 --- a/pkg/printers/tablegenerator.go +++ b/pkg/printers/tablegenerator.go @@ -74,9 +74,8 @@ func (h *HumanReadablePrinter) GenerateTable(obj runtime.Object, options PrintOp } columns := make([]metav1beta1.TableColumnDefinition, 0, len(handler.columnDefinitions)) - for i := range handler.columnDefinitions { - columns = append(columns, handler.columnDefinitions[i]) - } + + columns = append(columns, handler.columnDefinitions...) table := &metav1beta1.Table{ ListMeta: metav1.ListMeta{ diff --git a/pkg/serving/service.go b/pkg/serving/service.go new file mode 100644 index 00000000..6e37a158 --- /dev/null +++ b/pkg/serving/service.go @@ -0,0 +1,74 @@ +// 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 serving + +import ( + "bytes" + "math/rand" + "strings" + "text/template" + "time" + + servingv1 "knative.dev/serving/pkg/apis/serving/v1" +) + +var revisionNameRand = rand.New(rand.NewSource(time.Now().UnixNano())) //nolint:gosec // Weak crypto is fine here, we use it for generating unique keys. + +var charChoices = []string{ + "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", + "y", "z", +} + +type revisionTemplContext struct { + Service string + Generation int64 +} + +func (c *revisionTemplContext) Random(l int) string { + chars := make([]string, 0, l) + for i := 0; i < l; i++ { + //nolint:gosec // Weak crypto is fine here, we use it for generating unique keys. + chars = append(chars, charChoices[revisionNameRand.Int()%len(charChoices)]) + } + return strings.Join(chars, "") +} + +// GenerateRevisionName returns an automatically-generated name suitable for the +// next revision of the given service. +func GenerateRevisionName(nameTempl string, service *servingv1.Service) (string, error) { + templ, err := template.New("revisionName").Parse(nameTempl) + if err != nil { + return "", err + } + context := &revisionTemplContext{ + Service: service.Name, + Generation: service.Generation + 1, + } + buf := new(bytes.Buffer) + err = templ.Execute(buf, context) + if err != nil { + return "", err + } + res := buf.String() + // Empty is ok. + if res == "" { + return res, nil + } + prefix := service.Name + "-" + if !strings.HasPrefix(res, prefix) { + res = prefix + res + } + return res, nil +} diff --git a/pkg/serving/service_test.go b/pkg/serving/service_test.go new file mode 100644 index 00000000..6c942f01 --- /dev/null +++ b/pkg/serving/service_test.go @@ -0,0 +1,55 @@ +// Copyright © 2018 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 serving + +import ( + "testing" + + "gotest.tools/v3/assert" + + servingv1 "knative.dev/serving/pkg/apis/serving/v1" +) + +type generateNameTest struct { + templ string + result string + err string +} + +func TestGenerateName(t *testing.T) { + revisionNameRand.Seed(1) + someRandomChars := (&revisionTemplContext{}).Random(20) + service := &servingv1.Service{} + service.Name = "foo" + service.Generation = 3 + cases := []generateNameTest{ + {"{{.Service}}-v-{{.Generation}}", "foo-v-4", ""}, + {"foo-asdf", "foo-asdf", ""}, + {"{{.Bad}}", "", "can't evaluate field Bad"}, + {"{{.Service}}-{{.Random 5}}", "foo-" + someRandomChars[0:5], ""}, + {"", "", ""}, + {"andrew", "foo-andrew", ""}, + {"{{.Random 5}}", "foo-" + someRandomChars[0:5], ""}, + } + for _, c := range cases { + revisionNameRand.Seed(1) + name, err := GenerateRevisionName(c.templ, service) + if c.err != "" { + assert.ErrorContains(t, err, c.err) + } else { + assert.Equal(t, name, c.result) + } + } +} diff --git a/pkg/serving/v1/client_mock.go b/pkg/serving/v1/client_mock.go new file mode 100644 index 00000000..f446b8d7 --- /dev/null +++ b/pkg/serving/v1/client_mock.go @@ -0,0 +1,282 @@ +// 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 v1 + +import ( + "context" + "testing" + "time" + + "gotest.tools/v3/assert" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" + servingv1 "knative.dev/serving/pkg/apis/serving/v1" + + "knative.dev/client-pkg/pkg/util/mock" + "knative.dev/client-pkg/pkg/wait" +) + +type MockKnServingClient struct { + t *testing.T + recorder *ServingRecorder +} + +// NewMockKnServiceClient returns a new mock instance which you need to record for +func NewMockKnServiceClient(t *testing.T, ns ...string) *MockKnServingClient { + namespace := "default" + if len(ns) > 0 { + namespace = ns[0] + } + return &MockKnServingClient{ + t: t, + recorder: &ServingRecorder{mock.NewRecorder(t, namespace)}, + } +} + +// recorder for service +type ServingRecorder struct { + r *mock.Recorder +} + +// Get the record to start for the recorder +func (c *MockKnServingClient) Recorder() *ServingRecorder { + return c.recorder +} + +// Namespace of this client +func (c *MockKnServingClient) Namespace() string { + return c.recorder.r.Namespace() +} + +// Get Service +func (sr *ServingRecorder) GetService(name interface{}, service *servingv1.Service, err error) { + sr.r.Add("GetService", []interface{}{name}, []interface{}{service, err}) +} + +func (c *MockKnServingClient) GetService(ctx context.Context, name string) (*servingv1.Service, error) { + call := c.recorder.r.VerifyCall("GetService", name) + return call.Result[0].(*servingv1.Service), mock.ErrorOrNil(call.Result[1]) +} + +// List services +func (sr *ServingRecorder) ListServices(opts interface{}, serviceList *servingv1.ServiceList, err error) { + sr.r.Add("ListServices", []interface{}{opts}, []interface{}{serviceList, err}) +} + +func (c *MockKnServingClient) ListServices(ctx context.Context, opts ...ListConfig) (*servingv1.ServiceList, error) { + call := c.recorder.r.VerifyCall("ListServices", opts) + return call.Result[0].(*servingv1.ServiceList), mock.ErrorOrNil(call.Result[1]) +} + +// Create a new service +func (sr *ServingRecorder) CreateService(service interface{}, err error) { + sr.r.Add("CreateService", []interface{}{service}, []interface{}{err}) +} + +func (c *MockKnServingClient) CreateService(ctx context.Context, service *servingv1.Service) error { + call := c.recorder.r.VerifyCall("CreateService", service) + return mock.ErrorOrNil(call.Result[0]) +} + +// Update the given service +func (sr *ServingRecorder) UpdateService(service interface{}, hasChanged bool, err error) { + sr.r.Add("UpdateService", []interface{}{service}, []interface{}{hasChanged, err}) +} + +func (c *MockKnServingClient) UpdateService(ctx context.Context, service *servingv1.Service) (bool, error) { + call := c.recorder.r.VerifyCall("UpdateService", service) + return call.Result[0].(bool), mock.ErrorOrNil(call.Result[1]) +} + +// Delegate to shared retry method +func (c *MockKnServingClient) UpdateServiceWithRetry(ctx context.Context, name string, updateFunc ServiceUpdateFunc, maxRetry int) (bool, error) { + return updateServiceWithRetry(ctx, c, name, updateFunc, maxRetry) +} + +// Update the given service +func (sr *ServingRecorder) ApplyService(service interface{}, hasChanged bool, err error) { + sr.r.Add("ApplyService", []interface{}{service}, []interface{}{hasChanged, err}) +} + +func (c *MockKnServingClient) ApplyService(ctx context.Context, service *servingv1.Service) (bool, error) { + call := c.recorder.r.VerifyCall("ApplyService", service) + return call.Result[0].(bool), mock.ErrorOrNil(call.Result[1]) +} + +// Delete a service by name +func (sr *ServingRecorder) DeleteService(name, timeout interface{}, err error) { + sr.r.Add("DeleteService", []interface{}{name, timeout}, []interface{}{err}) +} + +func (c *MockKnServingClient) DeleteService(ctx context.Context, name string, timeout time.Duration) error { + call := c.recorder.r.VerifyCall("DeleteService", name, timeout) + return mock.ErrorOrNil(call.Result[0]) +} + +// Wait for a service to become ready, but not longer than provided timeout +func (sr *ServingRecorder) WaitForService(name interface{}, wconfig interface{}, callback interface{}, err error, duration time.Duration) { + sr.r.Add("WaitForService", []interface{}{name, wconfig, callback}, []interface{}{err, duration}) +} + +func (c *MockKnServingClient) WaitForService(ctx context.Context, name string, wconfig WaitConfig, msgCallback wait.MessageCallback) (error, time.Duration) { + call := c.recorder.r.VerifyCall("WaitForService", name, wconfig, msgCallback) + return mock.ErrorOrNil(call.Result[0]), call.Result[1].(time.Duration) +} + +// Get a revision by name +func (sr *ServingRecorder) GetRevision(name interface{}, revision *servingv1.Revision, err error) { + sr.r.Add("GetRevision", []interface{}{name}, []interface{}{revision, err}) +} + +func (c *MockKnServingClient) GetRevision(ctx context.Context, name string) (*servingv1.Revision, error) { + call := c.recorder.r.VerifyCall("GetRevision", name) + return call.Result[0].(*servingv1.Revision), mock.ErrorOrNil(call.Result[1]) +} + +// List revisions +func (sr *ServingRecorder) ListRevisions(opts interface{}, revisionList *servingv1.RevisionList, err error) { + sr.r.Add("ListRevisions", []interface{}{opts}, []interface{}{revisionList, err}) +} + +func (c *MockKnServingClient) ListRevisions(ctx context.Context, opts ...ListConfig) (*servingv1.RevisionList, error) { + call := c.recorder.r.VerifyCall("ListRevisions", opts) + return call.Result[0].(*servingv1.RevisionList), mock.ErrorOrNil(call.Result[1]) +} + +// Delete a revision +func (sr *ServingRecorder) DeleteRevision(name, timeout interface{}, err error) { + sr.r.Add("DeleteRevision", []interface{}{name, timeout}, []interface{}{err}) +} + +func (c *MockKnServingClient) DeleteRevision(ctx context.Context, name string, timeout time.Duration) error { + call := c.recorder.r.VerifyCall("DeleteRevision", name, timeout) + return mock.ErrorOrNil(call.Result[0]) +} + +// Wait for a revision to become ready, but not longer than provided timeout +func (sr *ServingRecorder) WaitForRevision(name interface{}, timeout interface{}, callback interface{}, err error, duration time.Duration) { + sr.r.Add("WaitForRevision", []interface{}{name, timeout, callback}, []interface{}{err, duration}) +} + +func (c *MockKnServingClient) WaitForRevision(ctx context.Context, name string, timeout time.Duration, msgCallback wait.MessageCallback) (error, time.Duration) { + call := c.recorder.r.VerifyCall("WaitForRevision", name, timeout, msgCallback) + return mock.ErrorOrNil(call.Result[0]), call.Result[1].(time.Duration) +} + +// Get a route by its unique name +func (sr *ServingRecorder) GetRoute(name interface{}, route *servingv1.Route, err error) { + sr.r.Add("GetRoute", []interface{}{name}, []interface{}{route, err}) +} + +func (c *MockKnServingClient) GetRoute(ctx context.Context, name string) (*servingv1.Route, error) { + call := c.recorder.r.VerifyCall("GetRoute", name) + return call.Result[0].(*servingv1.Route), mock.ErrorOrNil(call.Result[1]) + +} + +// List routes +func (sr *ServingRecorder) ListRoutes(opts interface{}, routeList *servingv1.RouteList, err error) { + sr.r.Add("ListRoutes", []interface{}{opts}, []interface{}{routeList, err}) +} + +func (c *MockKnServingClient) ListRoutes(ctx context.Context, opts ...ListConfig) (*servingv1.RouteList, error) { + call := c.recorder.r.VerifyCall("ListRoutes", opts) + return call.Result[0].(*servingv1.RouteList), mock.ErrorOrNil(call.Result[1]) +} + +// GetConfiguration records a call to GetConfiguration with possible return values +func (sr *ServingRecorder) GetConfiguration(name string, config *servingv1.Configuration, err error) { + sr.r.Add("GetConfiguration", []interface{}{name}, []interface{}{config, err}) + +} + +// GetBaseRevision returns the base revision +func (c *MockKnServingClient) GetBaseRevision(ctx context.Context, service *servingv1.Service) (*servingv1.Revision, error) { + return getBaseRevision(ctx, c, service) +} + +// GetConfiguration returns a configuration looked up by name +func (c *MockKnServingClient) GetConfiguration(ctx context.Context, name string) (*servingv1.Configuration, error) { + call := c.recorder.r.VerifyCall("GetConfiguration", name) + return call.Result[0].(*servingv1.Configuration), mock.ErrorOrNil(call.Result[1]) +} + +// CreateRevision records a call CreateRevision +func (sr *ServingRecorder) CreateRevision(revision interface{}, err error) { + sr.r.Add("CreateRevision", []interface{}{revision}, []interface{}{err}) +} + +// CreateRevision creates a new revision +func (c *MockKnServingClient) CreateRevision(ctx context.Context, revision *servingv1.Revision) error { + call := c.recorder.r.VerifyCall("CreateRevision", revision) + return mock.ErrorOrNil(call.Result[0]) +} + +// UpdateRevision records a call UpdateRevision +func (sr *ServingRecorder) UpdateRevision(revision interface{}, err error) { + sr.r.Add("UpdateRevision", []interface{}{revision}, []interface{}{err}) +} + +// UpdateRevision updates given revision +func (c *MockKnServingClient) UpdateRevision(ctx context.Context, revision *servingv1.Revision) error { + call := c.recorder.r.VerifyCall("UpdateRevision", revision) + return mock.ErrorOrNil(call.Result[0]) +} + +// Check that every recorded method has been called +func (sr *ServingRecorder) Validate() { + sr.r.CheckThatAllRecordedMethodsHaveBeenCalled() +} + +// HasLabelSelector returns a comparable which can be used for asserting that list methods are called +// with the appropriate label selector +func HasLabelSelector(keyAndValues ...string) func(t *testing.T, a interface{}) { + return func(t *testing.T, a interface{}) { + lc := a.([]ListConfig) + listConfigCollector := listConfigCollector{ + Labels: make(labels.Set), + Fields: make(fields.Set), + } + lc[0](&listConfigCollector) + for i := 0; i < len(keyAndValues); i += 2 { + assert.Equal(t, listConfigCollector.Labels[keyAndValues[i]], keyAndValues[i+1]) + } + } +} + +// HasFieldSelector returns a comparable which can be used for asserting that list methods are called +// with the appropriate field selectors +func HasFieldSelector(keyAndValues ...string) func(t *testing.T, a interface{}) { + return func(t *testing.T, a interface{}) { + lc := a.([]ListConfig) + listConfigCollector := listConfigCollector{ + Labels: make(labels.Set), + Fields: make(fields.Set), + } + lc[0](&listConfigCollector) + for i := 0; i < len(keyAndValues); i += 2 { + assert.Equal(t, listConfigCollector.Fields[keyAndValues[i]], keyAndValues[i+1]) + } + } +} + +// HasSelector returns a comparable which can be used for asserting that list methods are called +// with the appropriate label and field selectors +func HasSelector(labelKeysAndValues []string, fieldKeysAndValue []string) func(t *testing.T, a interface{}) { + return func(t *testing.T, a interface{}) { + HasLabelSelector(labelKeysAndValues...)(t, a) + HasFieldSelector(fieldKeysAndValue...)(t, a) + } +} diff --git a/pkg/serving/v1/client_mock_test.go b/pkg/serving/v1/client_mock_test.go new file mode 100644 index 00000000..34c04000 --- /dev/null +++ b/pkg/serving/v1/client_mock_test.go @@ -0,0 +1,111 @@ +// 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 v1 + +import ( + "context" + "testing" + "time" + + "knative.dev/serving/pkg/apis/serving" + servingv1 "knative.dev/serving/pkg/apis/serving/v1" + + "knative.dev/client-pkg/pkg/util/mock" + "knative.dev/client-pkg/pkg/wait" +) + +func TestMockKnClient(t *testing.T) { + + client := NewMockKnServiceClient(t) + + recorder := client.Recorder() + + // Record all services + recorder.GetService("hello", nil, nil) + recorder.ListServices(mock.Any(), nil, nil) + recorder.ListServices(mock.Any(), nil, nil) + recorder.CreateService(&servingv1.Service{}, nil) + recorder.UpdateService(&servingv1.Service{}, false, nil) + recorder.ApplyService(&servingv1.Service{}, true, nil) + recorder.DeleteService("hello", time.Duration(10)*time.Second, nil) + recorder.WaitForService("hello", WaitConfig{ + Timeout: time.Duration(10) * time.Second, + ErrorWindow: time.Duration(2) * time.Second, + }, wait.NoopMessageCallback(), nil, 10*time.Second) + recorder.GetRevision("hello", nil, nil) + recorder.ListRevisions(mock.Any(), nil, nil) + recorder.CreateRevision(&servingv1.Revision{}, nil) + recorder.UpdateRevision(&servingv1.Revision{}, nil) + recorder.DeleteRevision("hello", time.Duration(10)*time.Second, nil) + recorder.WaitForRevision("hello", time.Duration(10)*time.Second, wait.NoopMessageCallback(), nil, 10*time.Second) + recorder.GetRoute("hello", nil, nil) + recorder.ListRoutes(mock.Any(), nil, nil) + recorder.GetConfiguration("hello", nil, nil) + + // Call all services + ctx := context.Background() + client.GetService(ctx, "hello") + client.ListServices(ctx, WithName("blub")) + client.ListServices(ctx, WithLabel("foo", "bar")) + client.CreateService(ctx, &servingv1.Service{}) + client.UpdateService(ctx, &servingv1.Service{}) + client.ApplyService(ctx, &servingv1.Service{}) + client.DeleteService(ctx, "hello", time.Duration(10)*time.Second) + client.WaitForService(ctx, "hello", WaitConfig{ + time.Duration(10) * time.Second, + time.Duration(2) * time.Second, + }, wait.NoopMessageCallback()) + client.GetRevision(ctx, "hello") + client.ListRevisions(ctx, WithName("blub")) + client.CreateRevision(ctx, &servingv1.Revision{}) + client.UpdateRevision(ctx, &servingv1.Revision{}) + client.DeleteRevision(ctx, "hello", time.Duration(10)*time.Second) + client.WaitForRevision(ctx, "hello", time.Duration(10)*time.Second, wait.NoopMessageCallback()) + client.GetRoute(ctx, "hello") + client.ListRoutes(ctx, WithName("blub")) + client.GetConfiguration(ctx, "hello") + + // Validate + recorder.Validate() +} + +func TestHasLabelSelector(t *testing.T) { + assertFunction := HasLabelSelector(serving.ServiceLabelKey, "myservice") + listConfig := []ListConfig{ + WithService("myservice"), + } + assertFunction(t, listConfig) +} + +func TestHasFieldSelector(t *testing.T) { + assertFunction := HasFieldSelector("metadata.name", "myname") + listConfig := []ListConfig{ + WithName("myname"), + } + assertFunction(t, listConfig) +} + +func TestHasSelector(t *testing.T) { + assertFunction := HasSelector( + []string{serving.ServiceLabelKey, "myservice"}, + []string{"metadata.name", "myname"}) + listConfig := []ListConfig{ + func(lo *listConfigCollector) { + lo.Labels[serving.ServiceLabelKey] = "myservice" + lo.Fields["metadata.name"] = "myname" + }, + } + assertFunction(t, listConfig) +} diff --git a/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/doc.go b/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/doc.go similarity index 97% rename from vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/doc.go rename to vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/doc.go index 68b15a55..07add715 100644 --- a/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/doc.go +++ b/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/doc.go @@ -17,4 +17,4 @@ limitations under the License. // Code generated by client-gen. DO NOT EDIT. // This package has the automatically generated typed clients. -package v1beta1 +package v1beta2 diff --git a/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/eventing_client.go b/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/eventing_client.go similarity index 71% rename from vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/eventing_client.go rename to vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/eventing_client.go index ab8a0f85..ced74363 100644 --- a/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/eventing_client.go +++ b/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/eventing_client.go @@ -16,34 +16,34 @@ limitations under the License. // Code generated by client-gen. DO NOT EDIT. -package v1beta1 +package v1beta2 import ( "net/http" rest "k8s.io/client-go/rest" - v1beta1 "knative.dev/eventing/pkg/apis/eventing/v1beta1" + v1beta2 "knative.dev/eventing/pkg/apis/eventing/v1beta2" "knative.dev/eventing/pkg/client/clientset/versioned/scheme" ) -type EventingV1beta1Interface interface { +type EventingV1beta2Interface interface { RESTClient() rest.Interface EventTypesGetter } -// EventingV1beta1Client is used to interact with features provided by the eventing.knative.dev group. -type EventingV1beta1Client struct { +// EventingV1beta2Client is used to interact with features provided by the eventing.knative.dev group. +type EventingV1beta2Client struct { restClient rest.Interface } -func (c *EventingV1beta1Client) EventTypes(namespace string) EventTypeInterface { +func (c *EventingV1beta2Client) EventTypes(namespace string) EventTypeInterface { return newEventTypes(c, namespace) } -// NewForConfig creates a new EventingV1beta1Client for the given config. +// NewForConfig creates a new EventingV1beta2Client for the given config. // NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), // where httpClient was generated with rest.HTTPClientFor(c). -func NewForConfig(c *rest.Config) (*EventingV1beta1Client, error) { +func NewForConfig(c *rest.Config) (*EventingV1beta2Client, error) { config := *c if err := setConfigDefaults(&config); err != nil { return nil, err @@ -55,9 +55,9 @@ func NewForConfig(c *rest.Config) (*EventingV1beta1Client, error) { return NewForConfigAndClient(&config, httpClient) } -// NewForConfigAndClient creates a new EventingV1beta1Client for the given config and http client. +// NewForConfigAndClient creates a new EventingV1beta2Client for the given config and http client. // Note the http client provided takes precedence over the configured transport values. -func NewForConfigAndClient(c *rest.Config, h *http.Client) (*EventingV1beta1Client, error) { +func NewForConfigAndClient(c *rest.Config, h *http.Client) (*EventingV1beta2Client, error) { config := *c if err := setConfigDefaults(&config); err != nil { return nil, err @@ -66,12 +66,12 @@ func NewForConfigAndClient(c *rest.Config, h *http.Client) (*EventingV1beta1Clie if err != nil { return nil, err } - return &EventingV1beta1Client{client}, nil + return &EventingV1beta2Client{client}, nil } -// NewForConfigOrDie creates a new EventingV1beta1Client for the given config and +// NewForConfigOrDie creates a new EventingV1beta2Client for the given config and // panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *EventingV1beta1Client { +func NewForConfigOrDie(c *rest.Config) *EventingV1beta2Client { client, err := NewForConfig(c) if err != nil { panic(err) @@ -79,13 +79,13 @@ func NewForConfigOrDie(c *rest.Config) *EventingV1beta1Client { return client } -// New creates a new EventingV1beta1Client for the given RESTClient. -func New(c rest.Interface) *EventingV1beta1Client { - return &EventingV1beta1Client{c} +// New creates a new EventingV1beta2Client for the given RESTClient. +func New(c rest.Interface) *EventingV1beta2Client { + return &EventingV1beta2Client{c} } func setConfigDefaults(config *rest.Config) error { - gv := v1beta1.SchemeGroupVersion + gv := v1beta2.SchemeGroupVersion config.GroupVersion = &gv config.APIPath = "/apis" config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() @@ -99,7 +99,7 @@ func setConfigDefaults(config *rest.Config) error { // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. -func (c *EventingV1beta1Client) RESTClient() rest.Interface { +func (c *EventingV1beta2Client) RESTClient() rest.Interface { if c == nil { return nil } diff --git a/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/eventtype.go b/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/eventtype.go similarity index 83% rename from vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/eventtype.go rename to vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/eventtype.go index 7005e330..95ba7873 100644 --- a/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/eventtype.go +++ b/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/eventtype.go @@ -16,7 +16,7 @@ limitations under the License. // Code generated by client-gen. DO NOT EDIT. -package v1beta1 +package v1beta2 import ( "context" @@ -26,7 +26,7 @@ import ( types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" rest "k8s.io/client-go/rest" - v1beta1 "knative.dev/eventing/pkg/apis/eventing/v1beta1" + v1beta2 "knative.dev/eventing/pkg/apis/eventing/v1beta2" scheme "knative.dev/eventing/pkg/client/clientset/versioned/scheme" ) @@ -38,15 +38,15 @@ type EventTypesGetter interface { // EventTypeInterface has methods to work with EventType resources. type EventTypeInterface interface { - Create(ctx context.Context, eventType *v1beta1.EventType, opts v1.CreateOptions) (*v1beta1.EventType, error) - Update(ctx context.Context, eventType *v1beta1.EventType, opts v1.UpdateOptions) (*v1beta1.EventType, error) - UpdateStatus(ctx context.Context, eventType *v1beta1.EventType, opts v1.UpdateOptions) (*v1beta1.EventType, error) + Create(ctx context.Context, eventType *v1beta2.EventType, opts v1.CreateOptions) (*v1beta2.EventType, error) + Update(ctx context.Context, eventType *v1beta2.EventType, opts v1.UpdateOptions) (*v1beta2.EventType, error) + UpdateStatus(ctx context.Context, eventType *v1beta2.EventType, opts v1.UpdateOptions) (*v1beta2.EventType, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta1.EventType, error) - List(ctx context.Context, opts v1.ListOptions) (*v1beta1.EventTypeList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta2.EventType, error) + List(ctx context.Context, opts v1.ListOptions) (*v1beta2.EventTypeList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.EventType, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta2.EventType, err error) EventTypeExpansion } @@ -57,7 +57,7 @@ type eventTypes struct { } // newEventTypes returns a EventTypes -func newEventTypes(c *EventingV1beta1Client, namespace string) *eventTypes { +func newEventTypes(c *EventingV1beta2Client, namespace string) *eventTypes { return &eventTypes{ client: c.RESTClient(), ns: namespace, @@ -65,8 +65,8 @@ func newEventTypes(c *EventingV1beta1Client, namespace string) *eventTypes { } // Get takes name of the eventType, and returns the corresponding eventType object, and an error if there is any. -func (c *eventTypes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.EventType, err error) { - result = &v1beta1.EventType{} +func (c *eventTypes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.EventType, err error) { + result = &v1beta2.EventType{} err = c.client.Get(). Namespace(c.ns). Resource("eventtypes"). @@ -78,12 +78,12 @@ func (c *eventTypes) Get(ctx context.Context, name string, options v1.GetOptions } // List takes label and field selectors, and returns the list of EventTypes that match those selectors. -func (c *eventTypes) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.EventTypeList, err error) { +func (c *eventTypes) List(ctx context.Context, opts v1.ListOptions) (result *v1beta2.EventTypeList, err error) { var timeout time.Duration if opts.TimeoutSeconds != nil { timeout = time.Duration(*opts.TimeoutSeconds) * time.Second } - result = &v1beta1.EventTypeList{} + result = &v1beta2.EventTypeList{} err = c.client.Get(). Namespace(c.ns). Resource("eventtypes"). @@ -110,8 +110,8 @@ func (c *eventTypes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Inte } // Create takes the representation of a eventType and creates it. Returns the server's representation of the eventType, and an error, if there is any. -func (c *eventTypes) Create(ctx context.Context, eventType *v1beta1.EventType, opts v1.CreateOptions) (result *v1beta1.EventType, err error) { - result = &v1beta1.EventType{} +func (c *eventTypes) Create(ctx context.Context, eventType *v1beta2.EventType, opts v1.CreateOptions) (result *v1beta2.EventType, err error) { + result = &v1beta2.EventType{} err = c.client.Post(). Namespace(c.ns). Resource("eventtypes"). @@ -123,8 +123,8 @@ func (c *eventTypes) Create(ctx context.Context, eventType *v1beta1.EventType, o } // Update takes the representation of a eventType and updates it. Returns the server's representation of the eventType, and an error, if there is any. -func (c *eventTypes) Update(ctx context.Context, eventType *v1beta1.EventType, opts v1.UpdateOptions) (result *v1beta1.EventType, err error) { - result = &v1beta1.EventType{} +func (c *eventTypes) Update(ctx context.Context, eventType *v1beta2.EventType, opts v1.UpdateOptions) (result *v1beta2.EventType, err error) { + result = &v1beta2.EventType{} err = c.client.Put(). Namespace(c.ns). Resource("eventtypes"). @@ -138,8 +138,8 @@ func (c *eventTypes) Update(ctx context.Context, eventType *v1beta1.EventType, o // UpdateStatus was generated because the type contains a Status member. // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *eventTypes) UpdateStatus(ctx context.Context, eventType *v1beta1.EventType, opts v1.UpdateOptions) (result *v1beta1.EventType, err error) { - result = &v1beta1.EventType{} +func (c *eventTypes) UpdateStatus(ctx context.Context, eventType *v1beta2.EventType, opts v1.UpdateOptions) (result *v1beta2.EventType, err error) { + result = &v1beta2.EventType{} err = c.client.Put(). Namespace(c.ns). Resource("eventtypes"). @@ -180,8 +180,8 @@ func (c *eventTypes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions } // Patch applies the patch and returns the patched eventType. -func (c *eventTypes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.EventType, err error) { - result = &v1beta1.EventType{} +func (c *eventTypes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta2.EventType, err error) { + result = &v1beta2.EventType{} err = c.client.Patch(pt). Namespace(c.ns). Resource("eventtypes"). diff --git a/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/fake/doc.go b/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/fake/doc.go similarity index 100% rename from vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/fake/doc.go rename to vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/fake/doc.go diff --git a/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/fake/fake_eventing_client.go b/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/fake/fake_eventing_client.go similarity index 77% rename from vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/fake/fake_eventing_client.go rename to vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/fake/fake_eventing_client.go index aa6fcf6b..784f0a65 100644 --- a/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/fake/fake_eventing_client.go +++ b/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/fake/fake_eventing_client.go @@ -21,20 +21,20 @@ package fake import ( rest "k8s.io/client-go/rest" testing "k8s.io/client-go/testing" - v1beta1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1" + v1beta2 "knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2" ) -type FakeEventingV1beta1 struct { +type FakeEventingV1beta2 struct { *testing.Fake } -func (c *FakeEventingV1beta1) EventTypes(namespace string) v1beta1.EventTypeInterface { +func (c *FakeEventingV1beta2) EventTypes(namespace string) v1beta2.EventTypeInterface { return &FakeEventTypes{c, namespace} } // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. -func (c *FakeEventingV1beta1) RESTClient() rest.Interface { +func (c *FakeEventingV1beta2) RESTClient() rest.Interface { var ret *rest.RESTClient return ret } diff --git a/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/fake/fake_eventtype.go b/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/fake/fake_eventtype.go similarity index 76% rename from vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/fake/fake_eventtype.go rename to vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/fake/fake_eventtype.go index 7de907f2..5dd5fa7f 100644 --- a/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/fake/fake_eventtype.go +++ b/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/fake/fake_eventtype.go @@ -26,34 +26,34 @@ import ( types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" testing "k8s.io/client-go/testing" - v1beta1 "knative.dev/eventing/pkg/apis/eventing/v1beta1" + v1beta2 "knative.dev/eventing/pkg/apis/eventing/v1beta2" ) // FakeEventTypes implements EventTypeInterface type FakeEventTypes struct { - Fake *FakeEventingV1beta1 + Fake *FakeEventingV1beta2 ns string } -var eventtypesResource = v1beta1.SchemeGroupVersion.WithResource("eventtypes") +var eventtypesResource = v1beta2.SchemeGroupVersion.WithResource("eventtypes") -var eventtypesKind = v1beta1.SchemeGroupVersion.WithKind("EventType") +var eventtypesKind = v1beta2.SchemeGroupVersion.WithKind("EventType") // Get takes name of the eventType, and returns the corresponding eventType object, and an error if there is any. -func (c *FakeEventTypes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.EventType, err error) { +func (c *FakeEventTypes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.EventType, err error) { obj, err := c.Fake. - Invokes(testing.NewGetAction(eventtypesResource, c.ns, name), &v1beta1.EventType{}) + Invokes(testing.NewGetAction(eventtypesResource, c.ns, name), &v1beta2.EventType{}) if obj == nil { return nil, err } - return obj.(*v1beta1.EventType), err + return obj.(*v1beta2.EventType), err } // List takes label and field selectors, and returns the list of EventTypes that match those selectors. -func (c *FakeEventTypes) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.EventTypeList, err error) { +func (c *FakeEventTypes) List(ctx context.Context, opts v1.ListOptions) (result *v1beta2.EventTypeList, err error) { obj, err := c.Fake. - Invokes(testing.NewListAction(eventtypesResource, eventtypesKind, c.ns, opts), &v1beta1.EventTypeList{}) + Invokes(testing.NewListAction(eventtypesResource, eventtypesKind, c.ns, opts), &v1beta2.EventTypeList{}) if obj == nil { return nil, err @@ -63,8 +63,8 @@ func (c *FakeEventTypes) List(ctx context.Context, opts v1.ListOptions) (result if label == nil { label = labels.Everything() } - list := &v1beta1.EventTypeList{ListMeta: obj.(*v1beta1.EventTypeList).ListMeta} - for _, item := range obj.(*v1beta1.EventTypeList).Items { + list := &v1beta2.EventTypeList{ListMeta: obj.(*v1beta2.EventTypeList).ListMeta} + for _, item := range obj.(*v1beta2.EventTypeList).Items { if label.Matches(labels.Set(item.Labels)) { list.Items = append(list.Items, item) } @@ -80,43 +80,43 @@ func (c *FakeEventTypes) Watch(ctx context.Context, opts v1.ListOptions) (watch. } // Create takes the representation of a eventType and creates it. Returns the server's representation of the eventType, and an error, if there is any. -func (c *FakeEventTypes) Create(ctx context.Context, eventType *v1beta1.EventType, opts v1.CreateOptions) (result *v1beta1.EventType, err error) { +func (c *FakeEventTypes) Create(ctx context.Context, eventType *v1beta2.EventType, opts v1.CreateOptions) (result *v1beta2.EventType, err error) { obj, err := c.Fake. - Invokes(testing.NewCreateAction(eventtypesResource, c.ns, eventType), &v1beta1.EventType{}) + Invokes(testing.NewCreateAction(eventtypesResource, c.ns, eventType), &v1beta2.EventType{}) if obj == nil { return nil, err } - return obj.(*v1beta1.EventType), err + return obj.(*v1beta2.EventType), err } // Update takes the representation of a eventType and updates it. Returns the server's representation of the eventType, and an error, if there is any. -func (c *FakeEventTypes) Update(ctx context.Context, eventType *v1beta1.EventType, opts v1.UpdateOptions) (result *v1beta1.EventType, err error) { +func (c *FakeEventTypes) Update(ctx context.Context, eventType *v1beta2.EventType, opts v1.UpdateOptions) (result *v1beta2.EventType, err error) { obj, err := c.Fake. - Invokes(testing.NewUpdateAction(eventtypesResource, c.ns, eventType), &v1beta1.EventType{}) + Invokes(testing.NewUpdateAction(eventtypesResource, c.ns, eventType), &v1beta2.EventType{}) if obj == nil { return nil, err } - return obj.(*v1beta1.EventType), err + return obj.(*v1beta2.EventType), err } // UpdateStatus was generated because the type contains a Status member. // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeEventTypes) UpdateStatus(ctx context.Context, eventType *v1beta1.EventType, opts v1.UpdateOptions) (*v1beta1.EventType, error) { +func (c *FakeEventTypes) UpdateStatus(ctx context.Context, eventType *v1beta2.EventType, opts v1.UpdateOptions) (*v1beta2.EventType, error) { obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(eventtypesResource, "status", c.ns, eventType), &v1beta1.EventType{}) + Invokes(testing.NewUpdateSubresourceAction(eventtypesResource, "status", c.ns, eventType), &v1beta2.EventType{}) if obj == nil { return nil, err } - return obj.(*v1beta1.EventType), err + return obj.(*v1beta2.EventType), err } // Delete takes name of the eventType and deletes it. Returns an error if one occurs. func (c *FakeEventTypes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(eventtypesResource, c.ns, name, opts), &v1beta1.EventType{}) + Invokes(testing.NewDeleteActionWithOptions(eventtypesResource, c.ns, name, opts), &v1beta2.EventType{}) return err } @@ -125,17 +125,17 @@ func (c *FakeEventTypes) Delete(ctx context.Context, name string, opts v1.Delete func (c *FakeEventTypes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { action := testing.NewDeleteCollectionAction(eventtypesResource, c.ns, listOpts) - _, err := c.Fake.Invokes(action, &v1beta1.EventTypeList{}) + _, err := c.Fake.Invokes(action, &v1beta2.EventTypeList{}) return err } // Patch applies the patch and returns the patched eventType. -func (c *FakeEventTypes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.EventType, err error) { +func (c *FakeEventTypes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta2.EventType, err error) { obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(eventtypesResource, c.ns, name, pt, data, subresources...), &v1beta1.EventType{}) + Invokes(testing.NewPatchSubresourceAction(eventtypesResource, c.ns, name, pt, data, subresources...), &v1beta2.EventType{}) if obj == nil { return nil, err } - return obj.(*v1beta1.EventType), err + return obj.(*v1beta2.EventType), err } diff --git a/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/generated_expansion.go b/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/generated_expansion.go similarity index 97% rename from vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/generated_expansion.go rename to vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/generated_expansion.go index cd07c63e..18a7ab38 100644 --- a/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/generated_expansion.go +++ b/vendor/knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/generated_expansion.go @@ -16,6 +16,6 @@ limitations under the License. // Code generated by client-gen. DO NOT EDIT. -package v1beta1 +package v1beta2 type EventTypeExpansion interface{} diff --git a/vendor/modules.txt b/vendor/modules.txt index 023ad083..ca9ca233 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1002,8 +1002,8 @@ knative.dev/eventing/pkg/apis/sources/v1beta2 knative.dev/eventing/pkg/client/clientset/versioned/scheme knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1 knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1/fake -knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1 -knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta1/fake +knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2 +knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1beta2/fake knative.dev/eventing/pkg/client/clientset/versioned/typed/messaging/v1 knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1 knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1/fake