/
addressresolver.go
119 lines (108 loc) · 3.05 KB
/
addressresolver.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package k8s
import (
"context"
"fmt"
"net/url"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"knative.dev/pkg/apis"
duckv1 "knative.dev/pkg/apis/duck/v1"
"knative.dev/pkg/client/injection/ducks/duck/v1/addressable"
"knative.dev/pkg/controller"
"knative.dev/pkg/injection/clients/dynamicclient"
"knative.dev/pkg/kmeta"
"knative.dev/pkg/resolver"
"knative.dev/pkg/tracker"
)
// ReferenceAddressResolver will resolve the tracker.Reference to an url.URL, or
// return an error.
type ReferenceAddressResolver interface {
ResolveAddress(ref *tracker.Reference, uri *apis.URL) (*url.URL, error)
}
// CreateAddressResolver will create ReferenceAddressResolver, or return an
// error.
func CreateAddressResolver(kube Clients) ReferenceAddressResolver {
ctx := ctxWithDynamic(kube)
return &addressResolver{
kube: kube, ctx: addressable.WithDuck(ctx),
}
}
type addressResolver struct {
kube Clients
ctx context.Context
}
// ResolveAddress of a tracker.Reference with given uri (as apis.URL).
func (a *addressResolver) ResolveAddress(
ref *tracker.Reference,
uri *apis.URL,
) (*url.URL, error) {
gvr := a.toGVR(ref)
dest, err := a.toDestination(gvr, ref, uri)
if err != nil {
return nil, err
}
parent := toAccessor(ref)
tr := tracker.New(noopCallback, controller.GetTrackerLease(a.ctx))
r := resolver.NewURIResolverFromTracker(a.ctx, tr)
u, err := r.URIFromDestinationV1(a.ctx, *dest, parent)
if err != nil {
return nil, fmt.Errorf("%w: %w", ErrNotAddressable, err)
}
resolved := u.URL()
return resolved, nil
}
func (a *addressResolver) toDestination(
gvr schema.GroupVersionResource,
ref *tracker.Reference,
uri *apis.URL,
) (*duckv1.Destination, error) {
dest := &duckv1.Destination{
Ref: &duckv1.KReference{
Kind: ref.Kind,
Namespace: ref.Namespace,
Name: ref.Name,
APIVersion: ref.APIVersion,
},
URI: uri,
}
if ref.Selector != nil {
list, err := a.kube.Dynamic().Resource(gvr).
Namespace(ref.Namespace).List(a.ctx, metav1.ListOptions{
LabelSelector: ref.Selector.String(),
})
if err != nil {
return nil, fmt.Errorf("%w: %w", ErrNotFound, err)
}
count := len(list.Items)
if count == 0 {
return nil, ErrNotFound
}
if count > 1 {
return nil, fmt.Errorf("%w: %d", ErrMoreThenOneFound, count)
}
dest.Ref.Name = list.Items[0].GetName()
}
return dest, nil
}
func (a *addressResolver) toGVR(ref *tracker.Reference) schema.GroupVersionResource {
gvk := ref.GroupVersionKind()
gvr := apis.KindToResource(gvk)
return gvr
}
func toAccessor(ref *tracker.Reference) kmeta.Accessor {
return &unstructured.Unstructured{Object: map[string]interface{}{
"apiVersion": ref.APIVersion,
"kind": ref.Kind,
"metadata": map[string]interface{}{
"name": ref.Name,
"namespace": ref.Namespace,
},
}}
}
func ctxWithDynamic(kube Clients) context.Context {
return context.WithValue(kube.Context(), dynamicclient.Key{}, kube.Dynamic())
}
func noopCallback(_ types.NamespacedName) {
}