github.com/grahambrereton-form3/tilt@v0.10.18/internal/store/runtime_state.go (about) 1 package store 2 3 import ( 4 "net/url" 5 "time" 6 7 "github.com/docker/distribution/reference" 8 v1 "k8s.io/api/core/v1" 9 "k8s.io/apimachinery/pkg/types" 10 11 "github.com/windmilleng/tilt/internal/container" 12 "github.com/windmilleng/tilt/internal/k8s" 13 "github.com/windmilleng/tilt/pkg/model" 14 ) 15 16 type RuntimeState interface { 17 RuntimeState() 18 HasEverBeenReady() bool 19 } 20 21 // Currently just a placeholder, as a LocalResource has no runtime state, only "build" 22 // state. In future, we may use this to store runtime state for long-running processes 23 // kicked off via a LocalResource. 24 type LocalRuntimeState struct { 25 HasSucceededAtLeastOnce bool 26 } 27 28 func (LocalRuntimeState) RuntimeState() {} 29 30 func (l LocalRuntimeState) HasEverBeenReady() bool { 31 return l.HasSucceededAtLeastOnce 32 } 33 34 var _ RuntimeState = LocalRuntimeState{} 35 36 type K8sRuntimeState struct { 37 // The ancestor that we match pods against to associate them with this manifest. 38 // If we deployed Pod YAML, this will be the Pod UID. 39 // In many cases, this will be a Deployment UID. 40 PodAncestorUID types.UID 41 42 Pods map[k8s.PodID]*Pod 43 LBs map[k8s.ServiceName]*url.URL 44 DeployedUIDSet UIDSet 45 46 LastReadyTime time.Time 47 } 48 49 func (K8sRuntimeState) RuntimeState() {} 50 51 var _ RuntimeState = K8sRuntimeState{} 52 53 func NewK8sRuntimeState(pods ...Pod) K8sRuntimeState { 54 podMap := make(map[k8s.PodID]*Pod, len(pods)) 55 for _, pod := range pods { 56 p := pod 57 podMap[p.PodID] = &p 58 } 59 return K8sRuntimeState{ 60 Pods: podMap, 61 LBs: make(map[k8s.ServiceName]*url.URL), 62 DeployedUIDSet: NewUIDSet(), 63 } 64 } 65 66 func (s K8sRuntimeState) HasEverBeenReady() bool { 67 return !s.LastReadyTime.IsZero() 68 } 69 70 func (s K8sRuntimeState) PodLen() int { 71 return len(s.Pods) 72 } 73 74 func (s K8sRuntimeState) ContainsID(id k8s.PodID) bool { 75 _, ok := s.Pods[id] 76 return ok 77 } 78 79 func (s K8sRuntimeState) PodList() []Pod { 80 pods := make([]Pod, 0, len(s.Pods)) 81 for _, pod := range s.Pods { 82 pods = append(pods, *pod) 83 } 84 return pods 85 } 86 87 // Get the "most recent pod" from the K8sRuntimeState. 88 // For most users, we believe there will be only one pod per manifest. 89 // So most of this time, this will return the only pod. 90 // And in other cases, it will return a reasonable, consistent default. 91 func (s K8sRuntimeState) MostRecentPod() Pod { 92 bestPod := Pod{} 93 found := false 94 95 for _, v := range s.Pods { 96 if !found || v.isAfter(bestPod) { 97 bestPod = *v 98 found = true 99 } 100 } 101 102 return bestPod 103 } 104 105 type Pod struct { 106 PodID k8s.PodID 107 Namespace k8s.Namespace 108 StartedAt time.Time 109 Status string 110 Phase v1.PodPhase 111 112 // Error messages from the pod state if it's in an error state. 113 StatusMessages []string 114 115 // Set when we get ready to replace a pod. We may do the update in-place. 116 UpdateStartTime time.Time 117 118 // If a pod is being deleted, Kubernetes marks it as Running 119 // until it actually gets removed. 120 Deleting bool 121 122 HasSynclet bool 123 124 // The log for the currently active pod, if any 125 CurrentLog model.Log `testdiff:"ignore"` 126 127 Containers []Container 128 129 // We want to show the user # of restarts since some baseline time 130 // i.e. Total Restarts - BaselineRestarts 131 BaselineRestarts int 132 } 133 134 type Container struct { 135 Name container.Name 136 ID container.ID 137 Ports []int32 138 Ready bool 139 ImageRef reference.Named 140 Restarts int 141 } 142 143 func (c Container) Empty() bool { 144 return c.Name == "" && c.ID == "" 145 } 146 147 func (p Pod) Empty() bool { 148 return p.PodID == "" 149 } 150 151 // A stable sort order for pods. 152 func (p Pod) isAfter(p2 Pod) bool { 153 if p.StartedAt.After(p2.StartedAt) { 154 return true 155 } else if p2.StartedAt.After(p.StartedAt) { 156 return false 157 } 158 return p.PodID > p2.PodID 159 } 160 161 func (p Pod) Log() model.Log { 162 return p.CurrentLog 163 } 164 165 func (p Pod) AllContainerPorts() []int32 { 166 result := make([]int32, 0) 167 for _, c := range p.Containers { 168 result = append(result, c.Ports...) 169 } 170 return result 171 } 172 173 func (p Pod) AllContainersReady() bool { 174 if len(p.Containers) == 0 { 175 return false 176 } 177 178 for _, c := range p.Containers { 179 if !c.Ready { 180 return false 181 } 182 } 183 return true 184 } 185 186 func (p Pod) VisibleContainerRestarts() int { 187 return p.AllContainerRestarts() - p.BaselineRestarts 188 } 189 190 func (p Pod) AllContainerRestarts() int { 191 result := 0 192 for _, c := range p.Containers { 193 result += c.Restarts 194 } 195 return result 196 } 197 198 type UIDSet map[types.UID]bool 199 200 func NewUIDSet() UIDSet { 201 return make(map[types.UID]bool) 202 } 203 204 func (s UIDSet) Add(uids ...types.UID) { 205 for _, uid := range uids { 206 s[uid] = true 207 } 208 } 209 210 func (s UIDSet) Contains(uid types.UID) bool { 211 return s[uid] 212 }