-
Notifications
You must be signed in to change notification settings - Fork 303
/
resource.go
192 lines (163 loc) · 5.13 KB
/
resource.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
package k8sconv
import (
"fmt"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"github.com/tilt-dev/tilt/internal/k8s"
"github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1"
)
// A KubernetesResource exposes a high-level status that summarizes
// the Pods we care about in a KubernetesDiscovery.
//
// If we have a KubernetesApply, KubernetesResource will use that
// to narrow down the list of pods to only the pods we care about
// for the current Apply.
//
// KubernetesResource is intended to be a non-stateful object (i.e., it is
// immutable and its status can be inferred from the state of child
// objects.)
//
// Long-term, this may become an explicit API server object, but
// for now it's intended to provide an API-server compatible
// layer around KubernetesDiscovery + KubernetesApply.
type KubernetesResource struct {
Discovery *v1alpha1.KubernetesDiscovery
ApplyStatus *v1alpha1.KubernetesApplyStatus
// A set of properties we use to determine which pods in Discovery
// belong to the current Apply.
ApplyFilter *KubernetesApplyFilter
// A set of pods that belong to the current Discovery
// and the current ApplyStatus (if available).
//
// Excludes pods that are being deleted
// or which belong to a previous apply.
FilteredPods []v1alpha1.Pod
}
func NewKubernetesResource(discovery *v1alpha1.KubernetesDiscovery, status *v1alpha1.KubernetesApplyStatus) (*KubernetesResource, error) {
var filter *KubernetesApplyFilter
var err error
if status != nil {
filter, err = NewKubernetesApplyFilter(status)
if err != nil {
return nil, err
}
}
var filteredPods []v1alpha1.Pod
if discovery != nil {
filteredPods = FilterPods(filter, discovery.Status.Pods)
}
return &KubernetesResource{
Discovery: discovery,
ApplyStatus: status,
ApplyFilter: filter,
FilteredPods: filteredPods,
}, nil
}
// Filter to determine whether a pod or resource belongs to the current
// KubernetesApply. Used to filter out pods from previous applys
// when looking at a KubernetesDiscovery object.
//
// Considered immutable once created.
type KubernetesApplyFilter struct {
// DeployedRefs are references to the objects that we deployed to a Kubernetes cluster.
DeployedRefs []v1.ObjectReference
// Hashes of the pod template specs that we deployed to a Kubernetes cluster.
PodTemplateSpecHashes []k8s.PodTemplateSpecHash
}
func NewKubernetesApplyFilter(status *v1alpha1.KubernetesApplyStatus) (*KubernetesApplyFilter, error) {
deployed, err := k8s.ParseYAMLFromString(status.ResultYAML)
if err != nil {
return nil, err
}
deployed = k8s.SortedEntities(deployed)
podTemplateSpecHashes := []k8s.PodTemplateSpecHash{}
for _, entity := range deployed {
if entity.UID() == "" {
return nil, fmt.Errorf("Resource missing uid: %s", entity.Name())
}
hs, err := k8s.ReadPodTemplateSpecHashes(entity)
if err != nil {
return nil, errors.Wrap(err, "reading pod template spec hashes")
}
podTemplateSpecHashes = append(podTemplateSpecHashes, hs...)
}
return &KubernetesApplyFilter{
DeployedRefs: k8s.ToRefList(deployed),
PodTemplateSpecHashes: podTemplateSpecHashes,
}, nil
}
func ContainsHash(filter *KubernetesApplyFilter, hash k8s.PodTemplateSpecHash) bool {
if filter == nil {
return false
}
for _, h := range filter.PodTemplateSpecHashes {
if h == hash {
return true
}
}
return false
}
func ContainsUID(filter *KubernetesApplyFilter, uid types.UID) bool {
if filter == nil {
return false
}
for _, ref := range filter.DeployedRefs {
if ref.UID == uid {
return true
}
}
return false
}
// Checks to see if the given pod is allowed by the current filter.
func HasOKPodTemplateSpecHash(pod *v1alpha1.Pod, filter *KubernetesApplyFilter) bool {
// if it doesn't have a label, just let it through - maybe it's from a CRD w/ no pod template spec
hash := k8s.PodTemplateSpecHash(pod.PodTemplateSpecHash)
if hash == "" {
return true
}
return ContainsHash(filter, hash)
}
// Filter out any pods that are being deleted.
// Only keep pods that belong in the current filter.
// If no filter is specified, return all pods.
func FilterPods(filter *KubernetesApplyFilter, pods []v1alpha1.Pod) []v1alpha1.Pod {
result := []v1alpha1.Pod{}
for _, pod := range pods {
// Ignore pods that are currently being deleted.
if pod.Deleting {
continue
}
// Ignore pods that have a stale pod template hash
if filter != nil && !HasOKPodTemplateSpecHash(&pod, filter) {
continue
}
// Ignore pods that were tracked by UID but
// aren't owned by a current Apply.
if filter != nil && pod.AncestorUID != "" && !ContainsUID(filter, types.UID(pod.AncestorUID)) {
continue
}
result = append(result, pod)
}
return result
}
func MostRecentPod(pod []v1alpha1.Pod) v1alpha1.Pod {
bestPod := v1alpha1.Pod{}
found := false
for _, v := range pod {
if !found || PodCompare(v, bestPod) {
bestPod = v
found = true
}
}
return bestPod
}
// PodCompare is a stable sort order for pods.
func PodCompare(p1 v1alpha1.Pod, p2 v1alpha1.Pod) bool {
if p1.CreatedAt.After(p2.CreatedAt.Time) {
return true
} else if p2.CreatedAt.After(p1.CreatedAt.Time) {
return false
}
return p1.Name > p2.Name
}