-
Notifications
You must be signed in to change notification settings - Fork 20
/
event.go
148 lines (124 loc) · 3.93 KB
/
event.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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package resolvers
import (
"context"
"sort"
"github.com/99designs/gqlgen/graphql"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/types"
"github.com/upbound/xgql/internal/auth"
"github.com/upbound/xgql/internal/graph/model"
)
const (
errListEvents = "cannot list events"
errGetInvolved = "cannot get involved resource"
errModelInvolved = "cannot model involved resource"
)
type events struct {
clients ClientCache
}
func (r *events) Resolve(ctx context.Context, obj *corev1.ObjectReference) (*model.EventConnection, error) {
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
creds, _ := auth.FromContext(ctx)
c, err := r.clients.Get(creds)
if err != nil {
graphql.AddError(ctx, errors.Wrap(err, errGetClient))
return nil, nil
}
in := &corev1.EventList{}
if err := c.List(ctx, in); err != nil {
graphql.AddError(ctx, errors.Wrap(err, errListEvents))
return nil, nil
}
// If no involved object was supplied we want to fetch all events. This may
// include Kubernetes events that don't pertain to Crossplane.
if obj == nil {
out := &model.EventConnection{
Nodes: make([]model.Event, 0, len(in.Items)),
TotalCount: len(in.Items),
}
for i := range in.Items {
out.Nodes = append(out.Nodes, model.GetEvent(&in.Items[i]))
}
sort.Stable(sort.Reverse(out))
return out, nil
}
out := &model.EventConnection{
Nodes: make([]model.Event, 0),
}
// NOTE(negz): The cache implementation we use has only basic support for
// field selectors, so we just filter our results here. Using the cache's
// rudimentary field selector support would require us to predeclare a set
// of event fields to index at cache load time, and even then we could only
// filter lists by a single field.
for i := range in.Items {
e := &in.Items[i] // To avoid taking the address of the range var.
// This event does not pertain to the involved object.
if !involves(e, obj) {
continue
}
out.Nodes = append(out.Nodes, model.GetEvent(e))
out.TotalCount++
}
sort.Stable(sort.Reverse(out))
return out, nil
}
func involves(e *corev1.Event, ref *corev1.ObjectReference) bool {
// The supplied object won't always have a UID, but the the event's object
// reference should. This test should be sufficient for most resolvers; the
// following logic is mostly for the case in which we're looking up events
// for a ReferenceID (which doesn't include the UID).
if ref.UID != "" {
return ref.UID == e.InvolvedObject.UID
}
// Note that because we don't know the supplied object's UID from here-on
// out we can't be sure whether we're returning events for the supplied
// object, or a different object with the same group, kind, namespace, and
// name from some time in the past.
got := ref
want := e.InvolvedObject
switch {
case got.APIVersion != want.APIVersion:
return false
case got.Kind != want.Kind:
return false
case got.Namespace != want.Namespace:
return false
case got.Name != want.Name:
return false
default:
return true
}
}
type event struct {
clients ClientCache
}
func (r *event) InvolvedObject(ctx context.Context, obj *model.Event) (model.KubernetesResource, error) {
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
creds, _ := auth.FromContext(ctx)
c, err := r.clients.Get(creds)
if err != nil {
graphql.AddError(ctx, errors.Wrap(err, errGetClient))
return nil, nil
}
u := &unstructured.Unstructured{}
u.SetAPIVersion(obj.InvolvedObjectRef.APIVersion)
u.SetKind(obj.InvolvedObjectRef.Kind)
nn := types.NamespacedName{
Namespace: obj.InvolvedObjectRef.Namespace,
Name: obj.InvolvedObjectRef.Name,
}
if err := c.Get(ctx, nn, u); err != nil {
graphql.AddError(ctx, errors.Wrap(err, errGetInvolved))
return nil, nil
}
out, err := model.GetKubernetesResource(u)
if err != nil {
graphql.AddError(ctx, errors.Wrap(err, errModelInvolved))
return nil, nil
}
return out, nil
}