Skip to content

Commit b41d2b2

Browse files
committed
Extract reuseable part of sink flag
1 parent fd0126d commit b41d2b2

File tree

8 files changed

+561
-211
lines changed

8 files changed

+561
-211
lines changed

docs/cmd/kn_source_ping_create.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ kn source ping create NAME --sink SINK
1111
```
1212
1313
# Create a Ping source 'my-ping' which fires every two minutes and sends '{ value: "hello" }' to service 'mysvc' as a cloudevent
14-
kn source ping create my-ping --schedule "*/2 * * * *" --data '{ value: "hello" }' --sink ksvc:mysvc
14+
kn source ping create my-ping --schedule "*/2 * * * *" --data '{ value: "hello" }' --sink mysvc
1515
```
1616

1717
### Options

pkg/commands/flags/sink.go

Lines changed: 58 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,19 @@ package flags
1616

1717
import (
1818
"context"
19-
"fmt"
20-
"strings"
2119

2220
"github.com/spf13/cobra"
23-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
"github.com/spf13/pflag"
2422
"k8s.io/apimachinery/pkg/runtime/schema"
2523
"knative.dev/client/pkg/config"
26-
"knative.dev/pkg/apis"
27-
duckv1 "knative.dev/pkg/apis/duck/v1"
28-
2924
clientdynamic "knative.dev/client/pkg/dynamic"
25+
"knative.dev/client/pkg/flags/sink"
26+
"knative.dev/client/pkg/util/errors"
27+
duckv1 "knative.dev/pkg/apis/duck/v1"
3028
)
3129

30+
// SinkFlags holds information about given sink together with optional mappings
31+
// to allow ease of referencing the common types.
3232
type SinkFlags struct {
3333
Sink string
3434
SinkMappings map[string]schema.GroupVersionResource
@@ -42,156 +42,81 @@ func NewSinkFlag(mapping map[string]schema.GroupVersionResource) *SinkFlags {
4242
}
4343

4444
// AddWithFlagName configures Sink flag with given flag name and a short flag name
45-
// pass empty short flag name if you don't want to set one
45+
// pass empty short flag name if you don't want to set one.
4646
func (i *SinkFlags) AddWithFlagName(cmd *cobra.Command, fname, short string) {
47-
flag := "--" + fname
47+
i.AddToFlagSet(cmd.Flags(), fname, short)
48+
}
49+
50+
// AddToFlagSet configures Sink flag with given flag name and a short flag name
51+
// pass empty short flag name if you don't want to set one
52+
func (i *SinkFlags) AddToFlagSet(fs *pflag.FlagSet, fname, short string) {
4853
if short == "" {
49-
cmd.Flags().StringVar(&i.Sink, fname, "", "")
54+
fs.StringVar(&i.Sink, fname, "", "")
5055
} else {
51-
cmd.Flags().StringVarP(&i.Sink, fname, short, "", "")
56+
fs.StringVarP(&i.Sink, fname, short, "", "")
5257
}
53-
cmd.Flag(fname).Usage = "Addressable sink for events. " +
54-
"You can specify a broker, channel, Knative service or URI. " +
55-
"Examples: '" + flag + " broker:nest' for a broker 'nest', " +
56-
"'" + flag + " channel:pipe' for a channel 'pipe', " +
57-
"'" + flag + " ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', " +
58-
"'" + flag + " https://event.receiver.uri' for an HTTP URI, " +
59-
"'" + flag + " ksvc:receiver' or simply '" + flag + " receiver' for a Knative service 'receiver' in the current namespace. " +
60-
"'" + flag + " special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. " +
61-
"If a prefix is not provided, it is considered as a Knative service in the current namespace."
62-
// Use default mapping if empty
63-
if i.SinkMappings == nil {
64-
i.SinkMappings = defaultSinkMappings
58+
fs.Lookup(fname).Usage = sink.Usage(fname)
59+
}
60+
61+
// Add configures Sink flag with name 'Sink' amd short name 's'
62+
func (i *SinkFlags) Add(cmd *cobra.Command) {
63+
i.AddWithFlagName(cmd, sink.DefaultFlagName, sink.DefaultFlagShorthand)
64+
}
65+
66+
// WithDefaultMappings will return a copy of SinkFlags with provided mappings
67+
// and the default ones.
68+
func (i *SinkFlags) WithDefaultMappings() *SinkFlags {
69+
sf := &SinkFlags{
70+
Sink: i.Sink,
71+
SinkMappings: make(map[string]schema.GroupVersionResource,
72+
len(i.SinkMappings)+len(sink.DefaultMappings)),
73+
}
74+
for k, v := range sink.DefaultMappings {
75+
sf.SinkMappings[k] = v
76+
}
77+
for k, v := range i.SinkMappings {
78+
sf.SinkMappings[k] = v
6579
}
6680
for _, p := range config.GlobalConfig.SinkMappings() {
67-
//user configuration might override the default configuration
68-
i.SinkMappings[p.Prefix] = schema.GroupVersionResource{
81+
// user configuration might override the default configuration
82+
sf.SinkMappings[p.Prefix] = schema.GroupVersionResource{
6983
Resource: p.Resource,
7084
Group: p.Group,
7185
Version: p.Version,
7286
}
7387
}
88+
return sf
7489
}
7590

76-
// Add configures Sink flag with name 'Sink' amd short name 's'
77-
func (i *SinkFlags) Add(cmd *cobra.Command) {
78-
i.AddWithFlagName(cmd, "sink", "s")
79-
}
80-
81-
// SinkPrefixes maps prefixes used for sinks to their GroupVersionResources.
82-
var defaultSinkMappings = map[string]schema.GroupVersionResource{
83-
"broker": {
84-
Resource: "brokers",
85-
Group: "eventing.knative.dev",
86-
Version: "v1",
87-
},
88-
// Shorthand alias for service
89-
"ksvc": {
90-
Resource: "services",
91-
Group: "serving.knative.dev",
92-
Version: "v1",
93-
},
94-
"channel": {
95-
Resource: "channels",
96-
Group: "messaging.knative.dev",
97-
Version: "v1",
98-
},
91+
// Parse returns the sink reference, which may refer to URL or to Kubernetes
92+
// resource. The namespace given should be the current namespace withing the
93+
// context.
94+
func (i *SinkFlags) Parse(namespace string) (*sink.Reference, error) {
95+
// Use default mapping if empty
96+
sf := i.WithDefaultMappings()
97+
return sink.Parse(sf.Sink, namespace, sf.SinkMappings)
9998
}
10099

101100
// ResolveSink returns the Destination referred to by the flags in the acceptor.
102101
// It validates that any object the user is referring to exists.
103102
func (i *SinkFlags) ResolveSink(ctx context.Context, knclient clientdynamic.KnDynamicClient, namespace string) (*duckv1.Destination, error) {
104-
client := knclient.RawClient()
105-
if i.Sink == "" {
106-
return nil, nil
107-
}
108-
// Use default mapping if empty
109-
if i.SinkMappings == nil {
110-
i.SinkMappings = defaultSinkMappings
111-
}
112-
prefix, name, ns := parseSink(i.Sink)
113-
if prefix == "" {
114-
// URI target
115-
uri, err := apis.ParseURL(name)
116-
if err != nil {
117-
return nil, err
118-
}
119-
return &duckv1.Destination{URI: uri}, nil
120-
}
121-
gvr, ok := i.SinkMappings[prefix]
122-
if !ok {
123-
if prefix == "svc" || prefix == "service" {
124-
return nil, fmt.Errorf("unsupported Sink prefix: '%s', please use prefix 'ksvc' for knative service", prefix)
125-
}
126-
idx := strings.LastIndex(prefix, "/")
127-
var groupVersion string
128-
var kind string
129-
if idx != -1 && idx < len(prefix)-1 {
130-
groupVersion, kind = prefix[:idx], prefix[idx+1:]
131-
} else {
132-
kind = prefix
133-
}
134-
parsedVersion, err := schema.ParseGroupVersion(groupVersion)
135-
if err != nil {
136-
return nil, err
137-
}
138-
139-
// For the RAWclient the resource name must be in lower case plural form.
140-
// This is the best effort to sanitize the inputs, but the safest way is to provide
141-
// the appropriate form in user's input.
142-
if !strings.HasSuffix(kind, "s") {
143-
kind = kind + "s"
144-
}
145-
kind = strings.ToLower(kind)
146-
gvr = parsedVersion.WithResource(kind)
147-
}
148-
if ns != "" {
149-
namespace = ns
150-
}
151-
obj, err := client.Resource(gvr).Namespace(namespace).Get(ctx, name, metav1.GetOptions{})
103+
s, err := i.Parse(namespace)
152104
if err != nil {
153105
return nil, err
154106
}
155-
156-
destination := &duckv1.Destination{
157-
Ref: &duckv1.KReference{
158-
Kind: obj.GetKind(),
159-
APIVersion: obj.GetAPIVersion(),
160-
Name: obj.GetName(),
161-
Namespace: namespace,
162-
},
163-
}
164-
return destination, nil
165-
}
166-
167-
// parseSink takes the string given by the user into the prefix, name and namespace of
168-
// the object. If the user put a URI instead, the prefix is empty and the name
169-
// is the whole URI.
170-
func parseSink(sink string) (string, string, string) {
171-
parts := strings.SplitN(sink, ":", 3)
172-
switch {
173-
case len(parts) == 1:
174-
return "ksvc", parts[0], ""
175-
case parts[0] == "http" || parts[0] == "https":
176-
return "", sink, ""
177-
case len(parts) == 3:
178-
return parts[0], parts[1], parts[2]
179-
default:
180-
return parts[0], parts[1], ""
107+
var dest *duckv1.Destination
108+
dest, err = s.Resolve(ctx, knclient)
109+
if err != nil {
110+
// Returning original error that caused sink.ErrSinkIsInvalid as it is
111+
// directly presented to the end-user.
112+
return nil, errors.CauseOf(err, sink.ErrSinkIsInvalid)
181113
}
114+
return dest, nil
182115
}
183116

184117
// SinkToString prepares a Sink for list output
185-
func SinkToString(sink duckv1.Destination) string {
186-
if sink.Ref != nil {
187-
if sink.Ref.Kind == "Service" && strings.HasPrefix(sink.Ref.APIVersion, defaultSinkMappings["ksvc"].Group) {
188-
return fmt.Sprintf("ksvc:%s", sink.Ref.Name)
189-
} else {
190-
return fmt.Sprintf("%s:%s", strings.ToLower(sink.Ref.Kind), sink.Ref.Name)
191-
}
192-
}
193-
if sink.URI != nil {
194-
return sink.URI.String()
195-
}
196-
return ""
118+
// Deprecated: use (*sink.Reference).AsText instead.
119+
func SinkToString(dest duckv1.Destination) string {
120+
ref := sink.GuessFromDestination(dest)
121+
return ref.String()
197122
}

0 commit comments

Comments
 (0)