From 003dba3616e9f86e359058e3424a49d290bfe53b Mon Sep 17 00:00:00 2001 From: Ying Chun Guo Date: Tue, 10 Dec 2019 19:52:46 +0800 Subject: [PATCH] Add creating ApiServerSource event source to Client (#415) --- docs/cmd/kn.md | 1 + docs/cmd/kn_source.md | 31 +++++ docs/cmd/kn_source_apiserver.md | 32 +++++ docs/cmd/kn_source_apiserver_create.md | 47 ++++++++ docs/cmd/kn_source_apiserver_delete.md | 39 +++++++ pkg/eventing/sources/v1alpha1/client.go | 25 ++++ pkg/eventing/sources/v1alpha1/client_test.go | 108 +++++++++++++++++ pkg/kn/commands/flags/sink.go | 58 +++++++++ pkg/kn/commands/source/apiserver/apiserver.go | 31 +++++ pkg/kn/commands/source/apiserver/create.go | 110 ++++++++++++++++++ .../commands/source/apiserver/create_flag.go | 89 ++++++++++++++ .../source/apiserver/create_flag_test.go | 105 +++++++++++++++++ .../commands/source/apiserver/create_test.go | 102 ++++++++++++++++ pkg/kn/commands/source/apiserver/delete.go | 59 ++++++++++ .../commands/source/apiserver/delete_test.go | 61 ++++++++++ pkg/kn/commands/source/source.go | 32 +++++ pkg/kn/commands/testing_helper.go | 6 + pkg/kn/core/root.go | 2 + vendor/modules.txt | 14 +-- 19 files changed, 945 insertions(+), 7 deletions(-) create mode 100644 docs/cmd/kn_source.md create mode 100644 docs/cmd/kn_source_apiserver.md create mode 100644 docs/cmd/kn_source_apiserver_create.md create mode 100644 docs/cmd/kn_source_apiserver_delete.md create mode 100644 pkg/eventing/sources/v1alpha1/client_test.go create mode 100644 pkg/kn/commands/flags/sink.go create mode 100644 pkg/kn/commands/source/apiserver/apiserver.go create mode 100644 pkg/kn/commands/source/apiserver/create.go create mode 100644 pkg/kn/commands/source/apiserver/create_flag.go create mode 100644 pkg/kn/commands/source/apiserver/create_flag_test.go create mode 100644 pkg/kn/commands/source/apiserver/create_test.go create mode 100644 pkg/kn/commands/source/apiserver/delete.go create mode 100644 pkg/kn/commands/source/apiserver/delete_test.go create mode 100644 pkg/kn/commands/source/source.go diff --git a/docs/cmd/kn.md b/docs/cmd/kn.md index 4053f7cae..47ff5bd7d 100644 --- a/docs/cmd/kn.md +++ b/docs/cmd/kn.md @@ -27,5 +27,6 @@ Manage your Knative building blocks: * [kn revision](kn_revision.md) - Revision command group * [kn route](kn_route.md) - Route command group * [kn service](kn_service.md) - Service command group +* [kn source](kn_source.md) - Event Source command group * [kn version](kn_version.md) - Prints the client version diff --git a/docs/cmd/kn_source.md b/docs/cmd/kn_source.md new file mode 100644 index 000000000..388039c02 --- /dev/null +++ b/docs/cmd/kn_source.md @@ -0,0 +1,31 @@ +## kn source + +Event Source command group + +### Synopsis + +Event Source command group + +``` +kn source [flags] +``` + +### Options + +``` + -h, --help help for source +``` + +### Options inherited from parent commands + +``` + --config string kn config file (default is $HOME/.kn/config.yaml) + --kubeconfig string kubectl config file (default is $HOME/.kube/config) + --log-http log http traffic +``` + +### SEE ALSO + +* [kn](kn.md) - Knative client +* [kn source apiserver](kn_source_apiserver.md) - Kubernetes API Server Event Source command group + diff --git a/docs/cmd/kn_source_apiserver.md b/docs/cmd/kn_source_apiserver.md new file mode 100644 index 000000000..8755556b4 --- /dev/null +++ b/docs/cmd/kn_source_apiserver.md @@ -0,0 +1,32 @@ +## kn source apiserver + +Kubernetes API Server Event Source command group + +### Synopsis + +Kubernetes API Server Event Source command group + +``` +kn source apiserver [flags] +``` + +### Options + +``` + -h, --help help for apiserver +``` + +### Options inherited from parent commands + +``` + --config string kn config file (default is $HOME/.kn/config.yaml) + --kubeconfig string kubectl config file (default is $HOME/.kube/config) + --log-http log http traffic +``` + +### SEE ALSO + +* [kn source](kn_source.md) - Event Source command group +* [kn source apiserver create](kn_source_apiserver_create.md) - Create an ApiServerSource, which watches for Kubernetes events and forwards them to a sink +* [kn source apiserver delete](kn_source_apiserver_delete.md) - Delete an ApiServerSource. + diff --git a/docs/cmd/kn_source_apiserver_create.md b/docs/cmd/kn_source_apiserver_create.md new file mode 100644 index 000000000..0ddc5e5b6 --- /dev/null +++ b/docs/cmd/kn_source_apiserver_create.md @@ -0,0 +1,47 @@ +## kn source apiserver create + +Create an ApiServerSource, which watches for Kubernetes events and forwards them to a sink + +### Synopsis + +Create an ApiServerSource, which watches for Kubernetes events and forwards them to a sink + +``` +kn source apiserver create NAME --resource RESOURCE --service-account ACCOUNTNAME --sink SINK --mode MODE [flags] +``` + +### Examples + +``` + + # Create an ApiServerSource 'k8sevents' which consumes Kubernetes events and sends message to service 'mysvc' as a cloudevent + kn source apiserver create k8sevents --resource Event --service-account myaccountname --sink svc:mysvc +``` + +### Options + +``` + -h, --help help for create + --mode string The mode the receive adapter controller runs under:, + "Ref" sends only the reference to the resource, + "Resource" send the full resource. (default "Ref") + -n, --namespace string Specify the namespace to operate in. + --resource strings Comma seperate Kind:APIVersion:isController list, e.g. Event:v1:true. + "APIVersion" and "isControler" can be omitted. + "APIVersion" is "v1" by default, "isController" is "false" by default. + --service-account string Name of the service account to use to run this source + -s, --sink string Addressable sink for events +``` + +### Options inherited from parent commands + +``` + --config string kn config file (default is $HOME/.kn/config.yaml) + --kubeconfig string kubectl config file (default is $HOME/.kube/config) + --log-http log http traffic +``` + +### SEE ALSO + +* [kn source apiserver](kn_source_apiserver.md) - Kubernetes API Server Event Source command group + diff --git a/docs/cmd/kn_source_apiserver_delete.md b/docs/cmd/kn_source_apiserver_delete.md new file mode 100644 index 000000000..9b362ca86 --- /dev/null +++ b/docs/cmd/kn_source_apiserver_delete.md @@ -0,0 +1,39 @@ +## kn source apiserver delete + +Delete an ApiServerSource. + +### Synopsis + +Delete an ApiServerSource. + +``` +kn source apiserver delete NAME [flags] +``` + +### Examples + +``` + + # Delete an ApiServerSource 'k8sevents' in default namespace + kn source apiserver delete k8sevents +``` + +### Options + +``` + -h, --help help for delete + -n, --namespace string Specify the namespace to operate in. +``` + +### Options inherited from parent commands + +``` + --config string kn config file (default is $HOME/.kn/config.yaml) + --kubeconfig string kubectl config file (default is $HOME/.kube/config) + --log-http log http traffic +``` + +### SEE ALSO + +* [kn source apiserver](kn_source_apiserver.md) - Kubernetes API Server Event Source command group + diff --git a/pkg/eventing/sources/v1alpha1/client.go b/pkg/eventing/sources/v1alpha1/client.go index 3d1b0dfcf..fd6f38d74 100644 --- a/pkg/eventing/sources/v1alpha1/client.go +++ b/pkg/eventing/sources/v1alpha1/client.go @@ -15,6 +15,10 @@ package v1alpha1 import ( + apis_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + kn_errors "knative.dev/client/pkg/errors" + "knative.dev/eventing/pkg/apis/sources/v1alpha1" client_v1alpha1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1alpha1" ) @@ -23,6 +27,12 @@ import ( type KnSourcesClient interface { // Namespace in which this client is operating for Namespace() string + + // Get an ApiServerSource by object + CreateApiServerSource(apisvrsrc *v1alpha1.ApiServerSource) (*v1alpha1.ApiServerSource, error) + + // Delete an ApiServerSource by name + DeleteApiServerSource(name string) error } // knSourcesClient is a combination of Sources client interface and namespace @@ -41,6 +51,21 @@ func NewKnSourcesClient(client client_v1alpha1.SourcesV1alpha1Interface, namespa } } +//CreateApiServerSource is used to create an instance of ApiServerSource +func (c *knSourcesClient) CreateApiServerSource(apisvrsrc *v1alpha1.ApiServerSource) (*v1alpha1.ApiServerSource, error) { + ins, err := c.client.ApiServerSources(c.namespace).Create(apisvrsrc) + if err != nil { + return nil, kn_errors.GetError(err) + } + return ins, nil +} + +//DeleteApiServerSource is used to create an instance of ApiServerSource +func (c *knSourcesClient) DeleteApiServerSource(name string) error { + err := c.client.ApiServerSources(c.namespace).Delete(name, &apis_v1.DeleteOptions{}) + return err +} + // Return the client's namespace func (c *knSourcesClient) Namespace() string { return c.namespace diff --git a/pkg/eventing/sources/v1alpha1/client_test.go b/pkg/eventing/sources/v1alpha1/client_test.go new file mode 100644 index 000000000..33c6d2c4f --- /dev/null +++ b/pkg/eventing/sources/v1alpha1/client_test.go @@ -0,0 +1,108 @@ +// 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 v1alpha1 + +import ( + "fmt" + "testing" + + "gotest.tools/assert" + "k8s.io/apimachinery/pkg/runtime" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + client_testing "k8s.io/client-go/testing" + "knative.dev/eventing/pkg/apis/sources/v1alpha1" + "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1alpha1/fake" +) + +var testNamespace = "test-ns" + +func setup() (sources fake.FakeSourcesV1alpha1, client KnSourcesClient) { + sources = fake.FakeSourcesV1alpha1{Fake: &client_testing.Fake{}} + client = NewKnSourcesClient(&sources, testNamespace) + return +} + +func TestDeleteApiServerSource(t *testing.T) { + var srcName = "new-src" + sourcesServer, client := setup() + + apisourceNew := newApiServerSource(srcName) + + sourcesServer.AddReactor("create", "apiserversources", + func(a client_testing.Action) (bool, runtime.Object, error) { + assert.Equal(t, testNamespace, a.GetNamespace()) + name := a.(client_testing.CreateAction).GetObject().(metav1.Object).GetName() + if name == apisourceNew.Name { + apisourceNew.Generation = 2 + return true, apisourceNew, nil + } + return true, nil, fmt.Errorf("error while creating apiserversource %s", name) + }) + + t.Run("create apiserversource without error", func(t *testing.T) { + ins, err := client.CreateApiServerSource(apisourceNew) + assert.NilError(t, err) + assert.Equal(t, ins.Name, srcName) + assert.Equal(t, ins.Namespace, testNamespace) + }) + + t.Run("create apiserversource with an error returns an error object", func(t *testing.T) { + _, err := client.CreateApiServerSource(newApiServerSource("unknown")) + assert.ErrorContains(t, err, "unknown") + }) +} + +func TestCreateApiServerSource(t *testing.T) { + var srcName = "new-src" + sourcesServer, client := setup() + + apisourceNew := newApiServerSource(srcName) + + sourcesServer.AddReactor("create", "apiserversources", + func(a client_testing.Action) (bool, runtime.Object, error) { + assert.Equal(t, testNamespace, a.GetNamespace()) + name := a.(client_testing.CreateAction).GetObject().(metav1.Object).GetName() + if name == apisourceNew.Name { + apisourceNew.Generation = 2 + return true, apisourceNew, nil + } + return true, nil, fmt.Errorf("error while creating apiserversource %s", name) + }) + + t.Run("create apiserversource without error", func(t *testing.T) { + ins, err := client.CreateApiServerSource(apisourceNew) + assert.NilError(t, err) + assert.Equal(t, ins.Name, srcName) + assert.Equal(t, ins.Namespace, testNamespace) + }) + + t.Run("create apiserversource with an error returns an error object", func(t *testing.T) { + _, err := client.CreateApiServerSource(newApiServerSource("unknown")) + assert.ErrorContains(t, err, "unknown") + }) +} + +func newApiServerSource(name string) *v1alpha1.ApiServerSource { + src := &v1alpha1.ApiServerSource{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: testNamespace, + }, + } + src.Name = name + src.Namespace = testNamespace + return src +} diff --git a/pkg/kn/commands/flags/sink.go b/pkg/kn/commands/flags/sink.go new file mode 100644 index 000000000..f107b10dd --- /dev/null +++ b/pkg/kn/commands/flags/sink.go @@ -0,0 +1,58 @@ +// 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 + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + v1 "k8s.io/api/core/v1" + apisv1alpha1 "knative.dev/pkg/apis/v1alpha1" + + "knative.dev/client/pkg/serving/v1alpha1" +) + +type SinkFlags struct { + sink string +} + +func (i *SinkFlags) Add(cmd *cobra.Command) { + cmd.Flags().StringVarP(&i.sink, "sink", "s", "", "Addressable sink for events") +} + +func (i *SinkFlags) ResolveSink(client v1alpha1.KnServingClient) (*apisv1alpha1.Destination, error) { + if i.sink == "" { + return nil, nil + } + + if strings.HasPrefix(i.sink, "svc:") { + serviceName := i.sink[4:] + service, err := client.GetService(serviceName) + if err != nil { + return nil, err + } + return &apisv1alpha1.Destination{ + Ref: &v1.ObjectReference{ + Kind: service.Kind, + APIVersion: service.APIVersion, + Name: service.Name, + Namespace: service.Namespace, + }, + }, nil + } + + return nil, fmt.Errorf("Not supported sink type: %s", i.sink) +} diff --git a/pkg/kn/commands/source/apiserver/apiserver.go b/pkg/kn/commands/source/apiserver/apiserver.go new file mode 100644 index 000000000..3eaffc1c5 --- /dev/null +++ b/pkg/kn/commands/source/apiserver/apiserver.go @@ -0,0 +1,31 @@ +// 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 apiserver + +import ( + "github.com/spf13/cobra" + + "knative.dev/client/pkg/kn/commands" +) + +func NewApiServerCommand(p *commands.KnParams) *cobra.Command { + apiServerImporterCmd := &cobra.Command{ + Use: "apiserver", + Short: "Kubernetes API Server Event Source command group", + } + apiServerImporterCmd.AddCommand(NewApiServerCreateCommand(p)) + apiServerImporterCmd.AddCommand(NewApiServerDeleteCommand(p)) + return apiServerImporterCmd +} diff --git a/pkg/kn/commands/source/apiserver/create.go b/pkg/kn/commands/source/apiserver/create.go new file mode 100644 index 000000000..65d400ea2 --- /dev/null +++ b/pkg/kn/commands/source/apiserver/create.go @@ -0,0 +1,110 @@ +// 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 apiserver + +import ( + "errors" + "fmt" + + "github.com/spf13/cobra" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/client/pkg/kn/commands" + "knative.dev/client/pkg/kn/commands/flags" + "knative.dev/eventing/pkg/apis/sources/v1alpha1" +) + +func NewApiServerCreateCommand(p *commands.KnParams) *cobra.Command { + var apiServerUpdateFlags ApiServerSourceUpdateFlags + var sinkFlags flags.SinkFlags + + cmd := &cobra.Command{ + Use: "create NAME --resource RESOURCE --service-account ACCOUNTNAME --sink SINK --mode MODE", + Short: "Create an ApiServerSource, which watches for Kubernetes events and forwards them to a sink", + Example: ` + # Create an ApiServerSource 'k8sevents' which consumes Kubernetes events and sends message to service 'mysvc' as a cloudevent + kn source apiserver create k8sevents --resource Event --service-account myaccountname --sink svc:mysvc`, + + RunE: func(cmd *cobra.Command, args []string) (err error) { + if len(args) != 1 { + return errors.New("'source apiserver create' requires the name of the source as single argument") + } + name := args[0] + + // get namespace + namespace, err := p.GetNamespace(cmd) + if err != nil { + return err + } + + // get client + sourcesClient, err := p.NewSourcesClient(namespace) + if err != nil { + return err + } + + // resolve sink + servingClient, err := p.NewServingClient(namespace) + if err != nil { + return err + } + + objectRef, err := sinkFlags.ResolveSink(servingClient) + if err != nil { + return fmt.Errorf( + "cannot create ApiServerSource '%s' in namespace '%s' "+ + "because %s", name, namespace, err) + } + + // construct ApiServerSource + apisrvsrc := constructApiServerSource(name, namespace, apiServerUpdateFlags) + apisrvsrc.Spec.Sink = objectRef + + // create + _, err = sourcesClient.CreateApiServerSource(apisrvsrc) + if err != nil { + return fmt.Errorf( + "cannot create ApiServerSource '%s' in namespace '%s' "+ + "because %s", name, namespace, err) + } + fmt.Fprintf(cmd.OutOrStdout(), "ApiServerSource '%s' successfully created in namespace '%s'.\n", args[0], namespace) + return nil + }, + } + commands.AddNamespaceFlags(cmd.Flags(), false) + apiServerUpdateFlags.Add(cmd) + sinkFlags.Add(cmd) + cmd.MarkFlagRequired("schedule") + + return cmd +} + +// constructApiServerSource is to create an instance of v1alpha1.ApiServerSource +func constructApiServerSource(name string, namespace string, apiServerFlags ApiServerSourceUpdateFlags) *v1alpha1.ApiServerSource { + + source := v1alpha1.ApiServerSource{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: v1alpha1.ApiServerSourceSpec{ + Resources: apiServerFlags.GetApiServerResourceArray(), + ServiceAccountName: apiServerFlags.ServiceAccountName, + Mode: apiServerFlags.Mode, + }, + } + + return &source +} diff --git a/pkg/kn/commands/source/apiserver/create_flag.go b/pkg/kn/commands/source/apiserver/create_flag.go new file mode 100644 index 000000000..b3048758d --- /dev/null +++ b/pkg/kn/commands/source/apiserver/create_flag.go @@ -0,0 +1,89 @@ +// 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 apiserver + +import ( + "strconv" + "strings" + + "github.com/spf13/cobra" + sources_v1alpha1 "knative.dev/eventing/pkg/apis/sources/v1alpha1" +) + +const ( + ApiVersionSplitChar = ":" + DefaultApiVersion = "v1" +) + +// ApiServerSourceUpdateFlags are flags for create and update a ApiServerSource +type ApiServerSourceUpdateFlags struct { + ServiceAccountName string + Mode string + Resources []string +} + +// GetApiServerResourceArray is to return an array of ApiServerResource from a string. A sample is Event:v1:true,Pod:v2:false +func (f *ApiServerSourceUpdateFlags) GetApiServerResourceArray() []sources_v1alpha1.ApiServerResource { + var resourceList []sources_v1alpha1.ApiServerResource + for _, r := range f.Resources { + version, kind, controller := getValidResource(r) + resourceRef := sources_v1alpha1.ApiServerResource{ + APIVersion: version, + Kind: kind, + Controller: controller, + } + resourceList = append(resourceList, resourceRef) + } + return resourceList +} + +func getValidResource(resource string) (string, string, bool) { + var version = DefaultApiVersion // v1 as default + var isController = false //false as default + var err error + + parts := strings.Split(resource, ApiVersionSplitChar) + kind := parts[0] + if len(parts) >= 2 { + version = parts[1] + } + if len(parts) >= 3 { + isController, err = strconv.ParseBool(parts[2]) + if err != nil { + isController = false + } + } + return version, kind, isController +} + +//Add is to set parameters +func (f *ApiServerSourceUpdateFlags) Add(cmd *cobra.Command) { + cmd.Flags().StringVar(&f.ServiceAccountName, + "service-account", + "", + "Name of the service account to use to run this source") + cmd.Flags().StringVar(&f.Mode, + "mode", + "Ref", + `The mode the receive adapter controller runs under:, +"Ref" sends only the reference to the resource, +"Resource" send the full resource.`) + cmd.Flags().StringSliceVar(&f.Resources, + "resource", + nil, + `Comma seperate Kind:APIVersion:isController list, e.g. Event:v1:true. +"APIVersion" and "isControler" can be omitted. +"APIVersion" is "v1" by default, "isController" is "false" by default. `) +} diff --git a/pkg/kn/commands/source/apiserver/create_flag_test.go b/pkg/kn/commands/source/apiserver/create_flag_test.go new file mode 100644 index 000000000..3db94d995 --- /dev/null +++ b/pkg/kn/commands/source/apiserver/create_flag_test.go @@ -0,0 +1,105 @@ +// 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 apiserver + +import ( + "testing" + + "gotest.tools/assert" + sources_v1alpha1 "knative.dev/eventing/pkg/apis/sources/v1alpha1" +) + +func TestGetApiServerResourceArray(t *testing.T) { + t.Run("get single apiserver resource", func(t *testing.T) { + createFlag := ApiServerSourceUpdateFlags{ + ServiceAccountName: "test-sa", + Mode: "Ref", + Resources: []string{"Service:serving.knative.dev/v1alpha1:true"}, + } + created := createFlag.GetApiServerResourceArray() + wanted := []sources_v1alpha1.ApiServerResource{{ + APIVersion: "serving.knative.dev/v1alpha1", + Kind: "Service", + Controller: true, + }} + assert.DeepEqual(t, wanted, created) + + // default isController + createFlag = ApiServerSourceUpdateFlags{ + ServiceAccountName: "test-sa", + Mode: "Ref", + Resources: []string{"Service:serving.knative.dev/v1alpha1"}, + } + created = createFlag.GetApiServerResourceArray() + wanted = []sources_v1alpha1.ApiServerResource{{ + APIVersion: "serving.knative.dev/v1alpha1", + Kind: "Service", + Controller: false, + }} + assert.DeepEqual(t, wanted, created) + + // default api version and isController + createFlag = ApiServerSourceUpdateFlags{ + ServiceAccountName: "test-sa", + Mode: "Ref", + Resources: []string{"Service"}, + } + created = createFlag.GetApiServerResourceArray() + wanted = []sources_v1alpha1.ApiServerResource{{ + APIVersion: "v1", + Kind: "Service", + Controller: false, + }} + assert.DeepEqual(t, wanted, created) + }) + + t.Run("get multiple apiserver resources", func(t *testing.T) { + createFlag := ApiServerSourceUpdateFlags{ + ServiceAccountName: "test-sa", + Mode: "Resource", + Resources: []string{"Event:v1:true", "Pod:v2:false"}, + } + created := createFlag.GetApiServerResourceArray() + wanted := []sources_v1alpha1.ApiServerResource{{ + APIVersion: "v1", + Kind: "Event", + Controller: true, + }, { + APIVersion: "v2", + Kind: "Pod", + Controller: false, + }} + assert.DeepEqual(t, wanted, created) + + // default api version and isController + createFlag = ApiServerSourceUpdateFlags{ + ServiceAccountName: "test-sa", + Mode: "Resource", + Resources: []string{"Event", "Pod"}, + } + created = createFlag.GetApiServerResourceArray() + + wanted = []sources_v1alpha1.ApiServerResource{{ + APIVersion: "v1", + Kind: "Event", + Controller: false, + }, { + APIVersion: "v1", + Kind: "Pod", + Controller: false, + }} + assert.DeepEqual(t, wanted, created) + }) +} diff --git a/pkg/kn/commands/source/apiserver/create_test.go b/pkg/kn/commands/source/apiserver/create_test.go new file mode 100644 index 000000000..9bc2ebf2d --- /dev/null +++ b/pkg/kn/commands/source/apiserver/create_test.go @@ -0,0 +1,102 @@ +// 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 apiserver + +import ( + "errors" + "fmt" + "testing" + + "gotest.tools/assert" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + client_testing "k8s.io/client-go/testing" + "knative.dev/client/pkg/kn/commands" + "knative.dev/client/pkg/util" + "knative.dev/eventing/pkg/apis/sources/v1alpha1" + apisv1alpha1 "knative.dev/pkg/apis/v1alpha1" +) + +var ( + testApiServerSrcName = "foo" +) + +func fakeApiServerSourceCreate(args []string, withExistingService bool, sync bool) ( + action client_testing.Action, + src *v1alpha1.ApiServerSource, + output string, + err error) { + knParams := &commands.KnParams{} + cmd, fakeSource, buf := commands.CreateSourcesTestKnCommand(NewApiServerCommand(knParams), knParams) + fakeSource.AddReactor("create", "apiserversources", + func(a client_testing.Action) (bool, runtime.Object, error) { + createAction, ok := a.(client_testing.CreateAction) + action = createAction + if !ok { + return true, nil, fmt.Errorf("wrong kind of action %v", a) + } + src, ok = createAction.GetObject().(*v1alpha1.ApiServerSource) + if !ok { + return true, nil, errors.New("was passed the wrong object") + } + return true, src, nil + }) + cmd.SetArgs(args) + err = cmd.Execute() + if err != nil { + output = err.Error() + return + } + output = buf.String() + return +} + +func TestApiServerSourceCreate(t *testing.T) { + action, created, output, err := fakeApiServerSourceCreate([]string{ + "apiserver", "create", testApiServerSrcName, "--resource", "Event:v1:true", "--service-account", "myaccountname", "--sink", "svc:mysvc"}, true, false) + if err != nil { + t.Fatal(err) + } else if !action.Matches("create", "apiserversources") { + t.Fatalf("Bad action %v", action) + } + + //construct a wanted instance + wanted := &v1alpha1.ApiServerSource{ + ObjectMeta: metav1.ObjectMeta{ + Name: testApiServerSrcName, + Namespace: commands.FakeNamespace, + }, + Spec: v1alpha1.ApiServerSourceSpec{ + Resources: []v1alpha1.ApiServerResource{{ + APIVersion: "v1", + Kind: "Event", + Controller: true, + }}, + ServiceAccountName: "myaccountname", + Mode: "Ref", + Sink: &apisv1alpha1.Destination{ + Ref: &v1.ObjectReference{ + Kind: "Service", + APIVersion: "serving.knative.dev/v1alpha1", + }, + }, + }, + } + + //assert equal + assert.DeepEqual(t, wanted, created) + assert.Check(t, util.ContainsAll(output, "ApiServerSource", testApiServerSrcName, "created", "namespace", commands.FakeNamespace)) +} diff --git a/pkg/kn/commands/source/apiserver/delete.go b/pkg/kn/commands/source/apiserver/delete.go new file mode 100644 index 000000000..a4d9ea5ef --- /dev/null +++ b/pkg/kn/commands/source/apiserver/delete.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 apiserver + +import ( + "errors" + "fmt" + + "github.com/spf13/cobra" + "knative.dev/client/pkg/kn/commands" +) + +// NewRevisionDeleteCommand represent 'revision delete' command +func NewApiServerDeleteCommand(p *commands.KnParams) *cobra.Command { + ApiServerDeleteCommand := &cobra.Command{ + Use: "delete NAME", + Short: "Delete an ApiServerSource.", + Example: ` + # Delete an ApiServerSource 'k8sevents' in default namespace + kn source apiserver delete k8sevents`, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("'source apiserver delete' requires the name of the source as single argument") + } + name := args[0] + + namespace, err := p.GetNamespace(cmd) + if err != nil { + return err + } + + sourcesClient, err := p.NewSourcesClient(namespace) + if err != nil { + return err + } + + err = sourcesClient.DeleteApiServerSource(name) + if err != nil { + return err + } + fmt.Fprintf(cmd.OutOrStdout(), "ApiServerSource '%s' deleted in namespace '%s'.\n", args[0], namespace) + return nil + }, + } + commands.AddNamespaceFlags(ApiServerDeleteCommand.Flags(), false) + return ApiServerDeleteCommand +} diff --git a/pkg/kn/commands/source/apiserver/delete_test.go b/pkg/kn/commands/source/apiserver/delete_test.go new file mode 100644 index 000000000..0b2603f38 --- /dev/null +++ b/pkg/kn/commands/source/apiserver/delete_test.go @@ -0,0 +1,61 @@ +// 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 apiserver + +import ( + "testing" + + "gotest.tools/assert" + "k8s.io/apimachinery/pkg/runtime" + client_testing "k8s.io/client-go/testing" + "knative.dev/client/pkg/kn/commands" + "knative.dev/client/pkg/util" +) + +func fakeServiceDelete(args []string) (action client_testing.Action, name string, output string, err error) { + knParams := &commands.KnParams{} + cmd, fakeSource, buf := commands.CreateSourcesTestKnCommand(NewApiServerCommand(knParams), knParams) + fakeSource.AddReactor("delete", "apiserversources", + func(a client_testing.Action) (bool, runtime.Object, error) { + deleteAction, _ := a.(client_testing.DeleteAction) + action = deleteAction + name = deleteAction.GetName() + return true, nil, nil + }) + cmd.SetArgs(args) + err = cmd.Execute() + if err != nil { + return + } + output = buf.String() + return +} + +func TestServiceDelete(t *testing.T) { + srcName := "src-12345" + action, name, output, err := fakeServiceDelete([]string{"apiserver", "delete", srcName}) + if err != nil { + t.Error(err) + return + } + if action == nil { + t.Errorf("No action") + } else if !action.Matches("delete", "apiserversources") { + t.Errorf("Bad action %v", action) + } else if name != srcName { + t.Errorf("Bad service name returned after delete.") + } + assert.Check(t, util.ContainsAll(output, "ApiServerSource", srcName, "deleted", "namespace", commands.FakeNamespace)) +} diff --git a/pkg/kn/commands/source/source.go b/pkg/kn/commands/source/source.go new file mode 100644 index 000000000..d3cbdc723 --- /dev/null +++ b/pkg/kn/commands/source/source.go @@ -0,0 +1,32 @@ +// 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 source + +import ( + "github.com/spf13/cobra" + + "knative.dev/client/pkg/kn/commands" + "knative.dev/client/pkg/kn/commands/source/apiserver" +) + +func NewSourceCommand(p *commands.KnParams) *cobra.Command { + sourceCmd := &cobra.Command{ + Use: "source", + Short: "Event Source command group", + } + //sourceCmd.AddCommand(cron.NewCronCommand(p)) + sourceCmd.AddCommand(apiserver.NewApiServerCommand(p)) + return sourceCmd +} diff --git a/pkg/kn/commands/testing_helper.go b/pkg/kn/commands/testing_helper.go index 01feb6e0d..5ad47342c 100644 --- a/pkg/kn/commands/testing_helper.go +++ b/pkg/kn/commands/testing_helper.go @@ -60,6 +60,12 @@ func CreateTestKnCommand(cmd *cobra.Command, knParams *KnParams) (*cobra.Command // CreateSourcesTestKnCommand helper for creating test commands func CreateSourcesTestKnCommand(cmd *cobra.Command, knParams *KnParams) (*cobra.Command, *sources_fake.FakeSourcesV1alpha1, *bytes.Buffer) { buf := new(bytes.Buffer) + // create fake serving client because the sink of source depends on serving client + fakeServing := &fake.FakeServingV1alpha1{&client_testing.Fake{}} + knParams.NewServingClient = func(namespace string) (v1alpha1.KnServingClient, error) { + return v1alpha1.NewKnServingClient(fakeServing, FakeNamespace), nil + } + // create fake sources client fakeEventing := &sources_fake.FakeSourcesV1alpha1{&client_testing.Fake{}} knParams.Output = buf knParams.NewSourcesClient = func(namespace string) (sources_client.KnSourcesClient, error) { diff --git a/pkg/kn/core/root.go b/pkg/kn/core/root.go index 7f4bfeaf6..6f1d040e4 100644 --- a/pkg/kn/core/root.go +++ b/pkg/kn/core/root.go @@ -35,6 +35,7 @@ import ( "knative.dev/client/pkg/kn/commands/revision" "knative.dev/client/pkg/kn/commands/route" "knative.dev/client/pkg/kn/commands/service" + "knative.dev/client/pkg/kn/commands/source" "knative.dev/client/pkg/kn/commands/version" "knative.dev/client/pkg/kn/flags" ) @@ -142,6 +143,7 @@ func NewKnCommand(params ...commands.KnParams) *cobra.Command { rootCmd.AddCommand(route.NewRouteCommand(p)) rootCmd.AddCommand(completion.NewCompletionCommand(p)) rootCmd.AddCommand(version.NewVersionCommand(p)) + rootCmd.AddCommand(source.NewSourceCommand(p)) // Deal with empty and unknown sub command groups EmptyAndUnknownSubCommands(rootCmd) diff --git a/vendor/modules.txt b/vendor/modules.txt index 126951544..2b08f7677 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -357,8 +357,8 @@ gotest.tools/internal/source gotest.tools/internal/difflib # k8s.io/api v0.0.0-20191016110246-af539daaa43a k8s.io/api/core/v1 -k8s.io/api/authentication/v1 k8s.io/api/apps/v1 +k8s.io/api/authentication/v1 k8s.io/api/admissionregistration/v1beta1 k8s.io/api/apps/v1beta1 k8s.io/api/apps/v1beta2 @@ -437,9 +437,9 @@ k8s.io/apimachinery/pkg/version k8s.io/apimachinery/pkg/runtime/serializer/protobuf k8s.io/apimachinery/pkg/runtime/serializer/recognizer k8s.io/apimachinery/pkg/util/clock -k8s.io/apimachinery/pkg/util/framer k8s.io/apimachinery/pkg/util/cache k8s.io/apimachinery/pkg/util/diff +k8s.io/apimachinery/pkg/util/framer k8s.io/apimachinery/pkg/apis/meta/internalversion # k8s.io/cli-runtime v0.0.0-20191016113937-7693ce2cae74 k8s.io/cli-runtime/pkg/genericclioptions @@ -475,9 +475,9 @@ k8s.io/client-go/tools/metrics k8s.io/client-go/transport k8s.io/client-go/util/cert k8s.io/client-go/util/flowcontrol +k8s.io/client-go/tools/cache k8s.io/client-go/tools/clientcmd/api/v1 k8s.io/client-go/dynamic -k8s.io/client-go/tools/cache k8s.io/client-go/kubernetes/scheme k8s.io/client-go/third_party/forked/golang/template k8s.io/client-go/pkg/apis/clientauthentication @@ -621,12 +621,12 @@ k8s.io/utils/integer k8s.io/utils/buffer k8s.io/utils/trace # knative.dev/eventing v0.10.0 +knative.dev/eventing/pkg/apis/sources/v1alpha1 knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1alpha1 knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1alpha1/fake -knative.dev/eventing/pkg/apis/sources/v1alpha1 -knative.dev/eventing/pkg/client/clientset/versioned/scheme knative.dev/eventing/pkg/apis/duck knative.dev/eventing/pkg/apis/sources +knative.dev/eventing/pkg/client/clientset/versioned/scheme knative.dev/eventing/pkg/apis/eventing/v1alpha1 knative.dev/eventing/pkg/apis/messaging/v1alpha1 knative.dev/eventing/pkg/apis/duck/v1alpha1 @@ -635,13 +635,13 @@ knative.dev/eventing/pkg/apis/messaging # knative.dev/pkg v0.0.0-20191107185656-884d50f09454 knative.dev/pkg/apis knative.dev/pkg/apis/duck/v1 +knative.dev/pkg/apis/v1alpha1 knative.dev/pkg/ptr +knative.dev/pkg/kmeta knative.dev/pkg/kmp knative.dev/pkg/apis/duck -knative.dev/pkg/apis/v1alpha1 knative.dev/pkg/profiling knative.dev/pkg/apis/duck/v1alpha1 -knative.dev/pkg/kmeta knative.dev/pkg/configmap knative.dev/pkg/logging knative.dev/pkg/network