Skip to content

Commit 171dac3

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

File tree

3 files changed

+358
-126
lines changed

3 files changed

+358
-126
lines changed

pkg/commands/flags/sink.go

Lines changed: 51 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,18 @@ 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+
duckv1 "knative.dev/pkg/apis/duck/v1"
3027
)
3128

29+
// SinkFlags holds information about given sink together with optional mappings
30+
// to allow ease of referencing the common types.
3231
type SinkFlags struct {
3332
Sink string
3433
SinkMappings map[string]schema.GroupVersionResource
@@ -42,26 +41,27 @@ func NewSinkFlag(mapping map[string]schema.GroupVersionResource) *SinkFlags {
4241
}
4342

4443
// 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
44+
// pass empty short flag name if you don't want to set one.
4645
func (i *SinkFlags) AddWithFlagName(cmd *cobra.Command, fname, short string) {
47-
flag := "--" + fname
46+
i.AddToFlagSet(cmd.Flags(), fname, short)
47+
}
48+
49+
// AddToFlagSet configures Sink flag with given flag name and a short flag name
50+
// pass empty short flag name if you don't want to set one
51+
func (i *SinkFlags) AddToFlagSet(fs *pflag.FlagSet, fname, short string) {
4852
if short == "" {
49-
cmd.Flags().StringVar(&i.Sink, fname, "", "")
53+
fs.StringVar(&i.Sink, fname, "", "")
5054
} else {
51-
cmd.Flags().StringVarP(&i.Sink, fname, short, "", "")
55+
fs.StringVarP(&i.Sink, fname, short, "", "")
5256
}
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."
57+
fs.Lookup(fname).Usage = sink.Usage(fname)
6258
// Use default mapping if empty
6359
if i.SinkMappings == nil {
64-
i.SinkMappings = defaultSinkMappings
60+
i.SinkMappings = make(map[string]schema.GroupVersionResource,
61+
len(sink.DefaultMappings))
62+
for k, v := range sink.DefaultMappings {
63+
i.SinkMappings[k] = v
64+
}
6565
}
6666
for _, p := range config.GlobalConfig.SinkMappings() {
6767
//user configuration might override the default configuration
@@ -75,123 +75,48 @@ func (i *SinkFlags) AddWithFlagName(cmd *cobra.Command, fname, short string) {
7575

7676
// Add configures Sink flag with name 'Sink' amd short name 's'
7777
func (i *SinkFlags) Add(cmd *cobra.Command) {
78-
i.AddWithFlagName(cmd, "sink", "s")
78+
i.AddWithFlagName(cmd, sink.DefaultFlagName, sink.DefaultFlagShorthand)
79+
}
80+
81+
// WithDefaultMappings will return a copy of SinkFlags with provided mappings
82+
// and the default ones.
83+
func (i *SinkFlags) WithDefaultMappings() *SinkFlags {
84+
sf := &SinkFlags{
85+
Sink: i.Sink,
86+
SinkMappings: make(map[string]schema.GroupVersionResource,
87+
len(i.SinkMappings)+len(sink.DefaultMappings)),
88+
}
89+
for k, v := range sink.DefaultMappings {
90+
sf.SinkMappings[k] = v
91+
}
92+
for k, v := range i.SinkMappings {
93+
sf.SinkMappings[k] = v
94+
}
95+
return sf
7996
}
8097

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-
},
98+
// Parse returns the sink reference, which may refer to URL or to Kubernetes
99+
// resource. The namespace given should be the current namespace withing the
100+
// context.
101+
func (i *SinkFlags) Parse(namespace string) (*sink.Reference, error) {
102+
// Use default mapping if empty
103+
sf := i.WithDefaultMappings()
104+
return sink.Parse(sf.Sink, namespace, sf.SinkMappings)
99105
}
100106

101107
// ResolveSink returns the Destination referred to by the flags in the acceptor.
102108
// It validates that any object the user is referring to exists.
103109
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{})
110+
s, err := i.Parse(namespace)
152111
if err != nil {
153112
return nil, err
154113
}
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], ""
181-
}
114+
return s.Resolve(ctx, knclient)
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
}

pkg/flags/sink/commandline.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
Copyright 2024 The Knative Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package sink
18+
19+
import (
20+
"strings"
21+
)
22+
23+
var (
24+
// DefaultFlagName is a default command-line flag name.
25+
DefaultFlagName = "sink"
26+
// DefaultFlagShorthand is a default command-line flag shorthand.
27+
DefaultFlagShorthand = "s"
28+
)
29+
30+
// Usage returns a usage text which can be used to define sink-like flag.
31+
func Usage(fname string) string {
32+
flag := "--" + fname
33+
return "Addressable sink for events. " +
34+
"You can specify a broker, channel, Knative service, Kubernetes service or URI. " +
35+
"Examples: '" + flag + " broker:nest' for a broker 'nest', " +
36+
"'" + flag + " channel:pipe' for a channel 'pipe', " +
37+
"'" + flag + " ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', " +
38+
"'" + flag + " https://event.receiver.uri' for an HTTP URI, " +
39+
"'" + flag + " ksvc:receiver' or simply '" + flag + " receiver' for a Knative service 'receiver' in the current namespace. " +
40+
"'" + flag + " svc:receiver:mynamespace' for a Kubernetes service 'receiver' in the 'mynamespace' namespace. " +
41+
"'" + flag + " special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. " +
42+
"If a prefix is not provided, it is considered as a Knative service in the current namespace."
43+
}
44+
45+
// parseSink takes the string given by the user into the prefix, name and namespace of
46+
// the object. If the user put a URI instead, the prefix is empty and the name
47+
// is the whole URI.
48+
func parseSink(sink string) (string, string, string) {
49+
parts := strings.SplitN(sink, ":", 3)
50+
switch {
51+
case len(parts) == 1:
52+
return knativeServiceShorthand, parts[0], ""
53+
case parts[0] == "http" || parts[0] == "https":
54+
return "", sink, ""
55+
case len(parts) == 3:
56+
return parts[0], parts[1], parts[2]
57+
default:
58+
return parts[0], parts[1], ""
59+
}
60+
}

0 commit comments

Comments
 (0)