1
2
3
4 package statusreaders
5
6 import (
7 "context"
8 "encoding/json"
9 "errors"
10 "fmt"
11 "sort"
12
13 apierrors "k8s.io/apimachinery/pkg/api/errors"
14 "k8s.io/apimachinery/pkg/api/meta"
15 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
16 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
17 "k8s.io/apimachinery/pkg/labels"
18 "k8s.io/apimachinery/pkg/runtime/schema"
19 "k8s.io/apimachinery/pkg/types"
20 "sigs.k8s.io/cli-utils/pkg/kstatus/polling/engine"
21 "sigs.k8s.io/cli-utils/pkg/kstatus/polling/event"
22 "sigs.k8s.io/cli-utils/pkg/kstatus/status"
23 "sigs.k8s.io/cli-utils/pkg/object"
24 )
25
26
27
28
29
30
31
32
33 type baseStatusReader struct {
34
35
36 mapper meta.RESTMapper
37
38
39
40
41
42 resourceStatusReader resourceTypeStatusReader
43 }
44
45
46
47 type resourceTypeStatusReader interface {
48 Supports(gk schema.GroupKind) bool
49 ReadStatusForObject(ctx context.Context, reader engine.ClusterReader, object *unstructured.Unstructured) (*event.ResourceStatus, error)
50 }
51
52 func (b *baseStatusReader) Supports(gk schema.GroupKind) bool {
53 return b.resourceStatusReader.Supports(gk)
54 }
55
56
57
58 func (b *baseStatusReader) ReadStatus(ctx context.Context, reader engine.ClusterReader, identifier object.ObjMetadata) (*event.ResourceStatus, error) {
59 object, err := b.lookupResource(ctx, reader, identifier)
60 if err != nil {
61 return errIdentifierToResourceStatus(err, identifier)
62 }
63 return b.resourceStatusReader.ReadStatusForObject(ctx, reader, object)
64 }
65
66
67
68 func (b *baseStatusReader) ReadStatusForObject(ctx context.Context, reader engine.ClusterReader, object *unstructured.Unstructured) (*event.ResourceStatus, error) {
69 return b.resourceStatusReader.ReadStatusForObject(ctx, reader, object)
70 }
71
72
73
74
75
76 func (b *baseStatusReader) lookupResource(ctx context.Context, reader engine.ClusterReader, identifier object.ObjMetadata) (*unstructured.Unstructured, error) {
77 GVK, err := gvk(identifier.GroupKind, b.mapper)
78 if err != nil {
79 return nil, err
80 }
81
82 var u unstructured.Unstructured
83 u.SetGroupVersionKind(GVK)
84 key := types.NamespacedName{
85 Name: identifier.Name,
86 Namespace: identifier.Namespace,
87 }
88 err = reader.Get(ctx, key, &u)
89 if err != nil {
90 return nil, err
91 }
92 return &u, nil
93 }
94
95
96
97
98 type statusForGenResourcesFunc func(ctx context.Context, mapper meta.RESTMapper, reader engine.ClusterReader, statusReader resourceTypeStatusReader,
99 object *unstructured.Unstructured, gk schema.GroupKind, selectorPath ...string) (event.ResourceStatuses, error)
100
101
102
103
104 func statusForGeneratedResources(ctx context.Context, mapper meta.RESTMapper, reader engine.ClusterReader, statusReader resourceTypeStatusReader,
105 object *unstructured.Unstructured, gk schema.GroupKind, selectorPath ...string) (event.ResourceStatuses, error) {
106 selector, err := toSelector(object, selectorPath...)
107 if err != nil {
108 return event.ResourceStatuses{}, err
109 }
110
111 var objectList unstructured.UnstructuredList
112 gvk, err := gvk(gk, mapper)
113 if err != nil {
114 return event.ResourceStatuses{}, err
115 }
116 objectList.SetGroupVersionKind(gvk)
117 err = reader.ListNamespaceScoped(ctx, &objectList, object.GetNamespace(), selector)
118 if err != nil {
119 return event.ResourceStatuses{}, err
120 }
121
122 var resourceStatuses event.ResourceStatuses
123 for i := range objectList.Items {
124 generatedObject := objectList.Items[i]
125 resourceStatus, err := statusReader.ReadStatusForObject(ctx, reader, &generatedObject)
126 if err != nil {
127 return event.ResourceStatuses{}, err
128 }
129 resourceStatuses = append(resourceStatuses, resourceStatus)
130 }
131 sort.Sort(resourceStatuses)
132 return resourceStatuses, nil
133 }
134
135
136 func gvk(gk schema.GroupKind, mapper meta.RESTMapper) (schema.GroupVersionKind, error) {
137 mapping, err := mapper.RESTMapping(gk)
138 if err != nil {
139 return schema.GroupVersionKind{}, err
140 }
141 return mapping.GroupVersionKind, nil
142 }
143
144 func toSelector(resource *unstructured.Unstructured, path ...string) (labels.Selector, error) {
145 selector, found, err := unstructured.NestedMap(resource.Object, path...)
146 if err != nil {
147 return nil, err
148 }
149 if !found {
150 return nil, fmt.Errorf("no selector found")
151 }
152 bytes, err := json.Marshal(selector)
153 if err != nil {
154 return nil, err
155 }
156 var s metav1.LabelSelector
157 err = json.Unmarshal(bytes, &s)
158 if err != nil {
159 return nil, err
160 }
161 return metav1.LabelSelectorAsSelector(&s)
162 }
163
164
165
166 func errResourceToResourceStatus(err error, resource *unstructured.Unstructured, genResources ...*event.ResourceStatus) (*event.ResourceStatus, error) {
167
168
169
170 if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
171 return nil, err
172 }
173 identifier := object.UnstructuredToObjMetadata(resource)
174 if apierrors.IsNotFound(err) {
175 return &event.ResourceStatus{
176 Identifier: identifier,
177 Status: status.NotFoundStatus,
178 Message: "Resource not found",
179 }, nil
180 }
181 return &event.ResourceStatus{
182 Identifier: identifier,
183 Status: status.UnknownStatus,
184 Resource: resource,
185 Error: err,
186 GeneratedResources: genResources,
187 }, nil
188 }
189
190
191
192 func errIdentifierToResourceStatus(err error, identifier object.ObjMetadata) (*event.ResourceStatus, error) {
193
194
195
196 if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
197 return nil, err
198 }
199 if apierrors.IsNotFound(err) {
200 return &event.ResourceStatus{
201 Identifier: identifier,
202 Status: status.NotFoundStatus,
203 Message: "Resource not found",
204 }, nil
205 }
206 return &event.ResourceStatus{
207 Identifier: identifier,
208 Status: status.UnknownStatus,
209 Error: err,
210 }, nil
211 }
212
View as plain text