Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Faas" scenario — create a trigger alongside its source. #593

Closed
wants to merge 10 commits into from
7 changes: 7 additions & 0 deletions pkg/dynamic/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ type KnDynamicClient interface {

// ListSourceCRDs returns list of eventing sources CRDs
ListSourcesTypes() (*unstructured.UnstructuredList, error)

// RawClient returns the raw dynamic client interface
RawClient() dynamic.Interface
}

// knDynamicClient is a combination of client-go Dynamic client interface and namespace
Expand Down Expand Up @@ -87,3 +90,7 @@ func (c *knDynamicClient) ListSourcesTypes() (*unstructured.UnstructuredList, er
options.LabelSelector = sourcesLabels.String()
return c.ListCRDs(options)
}

func (c knDynamicClient) RawClient() dynamic.Interface {
return c.client
}
24 changes: 17 additions & 7 deletions pkg/dynamic/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/dynamic/fake"
"k8s.io/apimachinery/pkg/runtime/schema"
k8s_fake "k8s.io/client-go/dynamic/fake"
eventing_v1alpha1 "knative.dev/eventing/pkg/apis/eventing/v1alpha1"
serving_v1alpha1 "knative.dev/serving/pkg/apis/serving/v1alpha1"
)

const testNamespace = "testns"
Expand All @@ -43,18 +46,14 @@ func newUnstructured(name string) *unstructured.Unstructured {
}
}

func createFakeKnDynamicClient(objects ...runtime.Object) KnDynamicClient {
client := fake.NewSimpleDynamicClient(runtime.NewScheme(), objects...)
return NewKnDynamicClient(client, testNamespace)
}

func TestNamespace(t *testing.T) {
client := createFakeKnDynamicClient(newUnstructured("foo"))
client := createFakeKnDynamicClient(testNamespace, newUnstructured("foo"))
assert.Equal(t, client.Namespace(), testNamespace)
}

func TestListCRDs(t *testing.T) {
client := createFakeKnDynamicClient(
testNamespace,
newUnstructured("foo"),
newUnstructured("bar"),
)
Expand Down Expand Up @@ -84,6 +83,7 @@ func TestListCRDs(t *testing.T) {

func TestListSourceTypes(t *testing.T) {
client := createFakeKnDynamicClient(
testNamespace,
newUnstructured("foo"),
newUnstructured("bar"),
)
Expand All @@ -99,3 +99,13 @@ func TestListSourceTypes(t *testing.T) {
assert.Equal(t, uList.Items[1].GetName(), "bar")
})
}

// createFakeKnDynamicClient gives you a dynamic client for testing contianing the given objects.
// See also the one in the fake package. Duplicated here to avoid a dependency loop.
func createFakeKnDynamicClient(testNamespace string, objects ...runtime.Object) KnDynamicClient {
scheme := runtime.NewScheme()
scheme.AddKnownTypeWithName(schema.GroupVersionKind{Group: "serving.knative.dev", Version: "v1alpha1", Kind: "Service"}, &serving_v1alpha1.Service{})
scheme.AddKnownTypeWithName(schema.GroupVersionKind{Group: "eventing.knative.dev", Version: "v1alpha1", Kind: "Broker"}, &eventing_v1alpha1.Broker{})
client := k8s_fake.NewSimpleDynamicClient(scheme, objects...)
return NewKnDynamicClient(client, testNamespace)
}
34 changes: 34 additions & 0 deletions pkg/dynamic/fake/fake.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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 fake

import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
k8s_fake "k8s.io/client-go/dynamic/fake"

"knative.dev/client/pkg/dynamic"
eventing_v1alpha1 "knative.dev/eventing/pkg/apis/eventing/v1alpha1"
serving_v1alpha1 "knative.dev/serving/pkg/apis/serving/v1alpha1"
)

// CreateFakeKnDynamicClient gives you a dynamic client for testing contianing the given objects.
func CreateFakeKnDynamicClient(testNamespace string, objects ...runtime.Object) dynamic.KnDynamicClient {
scheme := runtime.NewScheme()
scheme.AddKnownTypeWithName(schema.GroupVersionKind{Group: "serving.knative.dev", Version: "v1alpha1", Kind: "Service"}, &serving_v1alpha1.Service{})
scheme.AddKnownTypeWithName(schema.GroupVersionKind{Group: "eventing.knative.dev", Version: "v1alpha1", Kind: "Broker"}, &eventing_v1alpha1.Broker{})
client := k8s_fake.NewSimpleDynamicClient(scheme, objects...)
return dynamic.NewKnDynamicClient(client, testNamespace)
}
79 changes: 64 additions & 15 deletions pkg/kn/commands/flags/sink.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ import (

"github.com/spf13/cobra"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
kn_dynamic "knative.dev/client/pkg/dynamic"
"knative.dev/pkg/apis"
duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1"

"knative.dev/client/pkg/serving/v1alpha1"
)

type SinkFlags struct {
Expand All @@ -33,26 +35,73 @@ 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) (*duckv1beta1.Destination, error) {
// SinkPrefixes maps prefixes used for sinks to their GroupVersionResources.
var SinkPrefixes = map[string]schema.GroupVersionResource{
"broker": {
Resource: "brokers",
Group: "eventing.knative.dev",
Version: "v1alpha1",
},
"service": {
Resource: "services",
Group: "serving.knative.dev",
Version: "v1alpha1",
},
// Shorthand alias for service
"svc": {
Resource: "services",
Group: "serving.knative.dev",
Version: "v1alpha1",
},
}

// 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(knclient kn_dynamic.KnDynamicClient, namespace string) (*duckv1beta1.Destination, error) {
client := knclient.RawClient()
if i.sink == "" {
return nil, nil
}

if strings.HasPrefix(i.sink, "svc:") {
serviceName := i.sink[4:]
service, err := client.GetService(serviceName)
prefix, name := parseSink(i.sink)
if prefix == "" {
// URI target
uri, err := apis.ParseURL(name)
if err != nil {
return nil, err
}
return &duckv1beta1.Destination{
Ref: &v1.ObjectReference{
Kind: service.Kind,
APIVersion: service.APIVersion,
Name: service.Name,
Namespace: service.Namespace,
},
}, nil
return &duckv1beta1.Destination{URI: uri}, nil
}
typ, ok := SinkPrefixes[prefix]
if !ok {
return nil, fmt.Errorf("Not supported sink type: %s", i.sink)
}
obj, err := client.Resource(typ).Namespace(namespace).Get(name, metav1.GetOptions{})
if err != nil {
return nil, err
}

return nil, fmt.Errorf("Not supported sink type: %s", i.sink)
return &duckv1beta1.Destination{
Ref: &v1.ObjectReference{
Kind: obj.GetKind(),
APIVersion: obj.GetAPIVersion(),
Name: obj.GetName(),
Namespace: namespace,
},
}, nil

}

// parseSink takes the string given by the user into the prefix and the name of
// the object. If the user put a URI instead, the prefix is empty and the name
// is the whole URI.
func parseSink(sink string) (string, string) {
parts := strings.SplitN(sink, ":", 2)
if len(parts) == 1 {
return "svc", parts[0]
} else if parts[0] == "http" || parts[0] == "https" {
return "", sink
} else {
return parts[0], parts[1]
}
}
81 changes: 81 additions & 0 deletions pkg/kn/commands/flags/sink_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// 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 (
"testing"

"gotest.tools/assert"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
dynamic_fake "knative.dev/client/pkg/dynamic/fake"
eventing_v1alpha1 "knative.dev/eventing/pkg/apis/eventing/v1alpha1"
"knative.dev/pkg/apis"
duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1"
serving_v1alpha1 "knative.dev/serving/pkg/apis/serving/v1alpha1"
)

type resolveCase struct {
sink string
destination *duckv1beta1.Destination
errContents string
}

func TestResolve(t *testing.T) {
targetExampleCom, err := apis.ParseURL("http://target.example.com")
mysvc := &serving_v1alpha1.Service{
TypeMeta: metav1.TypeMeta{Kind: "Service", APIVersion: "serving.knative.dev/v1alpha1"},
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "default"},
}
defaultBroker := &eventing_v1alpha1.Broker{
TypeMeta: metav1.TypeMeta{Kind: "Broker", APIVersion: "eventing.knative.dev/v1alpha1"},
ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "default"},
}

assert.NilError(t, err)
cases := []resolveCase{
{"svc:mysvc", &duckv1beta1.Destination{
Ref: &v1.ObjectReference{Kind: "Service",
APIVersion: "serving.knative.dev/v1alpha1",
Name: "mysvc",
Namespace: "default"}}, ""},
{"service:mysvc", &duckv1beta1.Destination{
Ref: &v1.ObjectReference{Kind: "Service",
APIVersion: "serving.knative.dev/v1alpha1",
Name: "mysvc",
Namespace: "default"}}, ""},
{"svc:absent", nil, "\"absent\" not found"},
{"broker:default", &duckv1beta1.Destination{
Ref: &v1.ObjectReference{Kind: "Broker",
APIVersion: "eventing.knative.dev/v1alpha1",
Name: "default",
Namespace: "default",
}}, ""},
{"http://target.example.com", &duckv1beta1.Destination{
URI: targetExampleCom,
}, ""},
}
dynamicClient := dynamic_fake.CreateFakeKnDynamicClient("default", mysvc, defaultBroker)
for _, c := range cases {
i := &SinkFlags{c.sink}
result, err := i.ResolveSink(dynamicClient, "default")
if c.destination != nil {
assert.DeepEqual(t, result, c.destination)
assert.NilError(t, err)
} else {
assert.ErrorContains(t, err, c.errContents)
}
}
}
14 changes: 8 additions & 6 deletions pkg/kn/commands/source/apiserver/apiserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ import (

corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/tools/clientcmd"
kn_dynamic "knative.dev/client/pkg/dynamic"
"knative.dev/eventing/pkg/apis/sources/v1alpha1"
duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1"

knsource_v1alpha1 "knative.dev/client/pkg/eventing/sources/v1alpha1"
"knative.dev/client/pkg/kn/commands"
knserving_v1alpha1 "knative.dev/client/pkg/serving/v1alpha1"
)

const testNamespace = "default"
Expand Down Expand Up @@ -55,14 +55,14 @@ current-context: x
}
}

func executeAPIServerSourceCommand(apiServerSourceClient knsource_v1alpha1.KnAPIServerSourcesClient, servingClient knserving_v1alpha1.KnServingClient, args ...string) (string, error) {
func executeAPIServerSourceCommand(apiServerSourceClient knsource_v1alpha1.KnAPIServerSourcesClient, dynamicClient kn_dynamic.KnDynamicClient, args ...string) (string, error) {
knParams := &commands.KnParams{}
knParams.ClientConfig = blankConfig

output := new(bytes.Buffer)
knParams.Output = output
knParams.NewServingClient = func(namespace string) (knserving_v1alpha1.KnServingClient, error) {
return servingClient, nil
knParams.NewDynamicClient = func(namespace string) (kn_dynamic.KnDynamicClient, error) {
return dynamicClient, nil
}

cmd := NewAPIServerCommand(knParams)
Expand Down Expand Up @@ -92,8 +92,10 @@ func createAPIServerSource(name, resourceKind, resourceVersion, serviceAccount,

sink := &duckv1beta1.Destination{
Ref: &corev1.ObjectReference{
Kind: "Service",
Name: service,
Kind: "Service",
Name: service,
APIVersion: "serving.knative.dev/v1alpha1",
Namespace: "default",
}}

return knsource_v1alpha1.NewAPIServerSourceBuilder(name).
Expand Down
6 changes: 2 additions & 4 deletions pkg/kn/commands/source/apiserver/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,11 @@ func NewAPIServerCreateCommand(p *commands.KnParams) *cobra.Command {

namespace := apiSourceClient.Namespace()

// create Serving client for resolving service sink
servingClient, err := p.NewServingClient(namespace)
dynamicClient, err := p.NewDynamicClient(namespace)
if err != nil {
return err
}

objectRef, err := sinkFlags.ResolveSink(servingClient)
objectRef, err := sinkFlags.ResolveSink(dynamicClient, namespace)
if err != nil {
return fmt.Errorf(
"cannot create ApiServerSource '%s' in namespace '%s' "+
Expand Down
Loading