github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/controllers/core/liveupdate/resource.go (about) 1 package liveupdate 2 3 import ( 4 "time" 5 6 "github.com/tilt-dev/tilt/internal/controllers/apis/liveupdate" 7 "github.com/tilt-dev/tilt/internal/dockercompose" 8 "github.com/tilt-dev/tilt/internal/store/k8sconv" 9 "github.com/tilt-dev/tilt/pkg/apis" 10 "github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1" 11 ) 12 13 // Helper interface for live-updating different kinds of resources. 14 // 15 // This interface uses the language "pods", even though only Kubernetes 16 // has pods. For other kinds of orchestrator, just use whatever kind 17 // of workload primitive fits best. 18 type luResource interface { 19 // The time to start syncing changes from. 20 bestStartTime() time.Time 21 22 // An iterator for visiting each container. 23 visitSelectedContainers(visit func(pod v1alpha1.Pod, c v1alpha1.Container) bool) 24 } 25 26 type luK8sResource struct { 27 selector *v1alpha1.LiveUpdateKubernetesSelector 28 res *k8sconv.KubernetesResource 29 im *v1alpha1.ImageMap 30 } 31 32 func (r *luK8sResource) bestStartTime() time.Time { 33 if r.res.ApplyStatus != nil { 34 return r.res.ApplyStatus.LastApplyStartTime.Time 35 } 36 37 startTime := time.Time{} 38 for _, pod := range r.res.FilteredPods { 39 if startTime.IsZero() || (!pod.CreatedAt.IsZero() && pod.CreatedAt.Time.Before(startTime)) { 40 startTime = pod.CreatedAt.Time 41 } 42 } 43 return startTime 44 } 45 46 // Visit all selected containers. 47 func (r *luK8sResource) visitSelectedContainers( 48 visit func(pod v1alpha1.Pod, c v1alpha1.Container) bool) { 49 for _, pod := range r.res.FilteredPods { 50 for _, c := range pod.Containers { 51 if c.Name == "" { 52 // ignore any blatantly invalid containers 53 continue 54 } 55 if !liveupdate.KubernetesSelectorMatchesContainer(c, r.selector, r.im) { 56 continue 57 } 58 stop := visit(pod, c) 59 if stop { 60 return 61 } 62 } 63 } 64 } 65 66 // We model the DockerCompose resource as a single-container pod with a 67 // name equal to the container id. 68 type luDCResource struct { 69 selector *v1alpha1.LiveUpdateDockerComposeSelector 70 res *v1alpha1.DockerComposeService 71 } 72 73 func (r *luDCResource) bestStartTime() time.Time { 74 return r.res.Status.LastApplyStartTime.Time 75 } 76 77 // Visit all selected containers. 78 func (r *luDCResource) visitSelectedContainers( 79 visit func(pod v1alpha1.Pod, c v1alpha1.Container) bool) { 80 cID := r.res.Status.ContainerID 81 state := r.res.Status.ContainerState 82 if cID != "" && state != nil { 83 // In DockerCompose, we leave the pod empty. 84 pod := v1alpha1.Pod{} 85 var waiting *v1alpha1.ContainerStateWaiting 86 var running *v1alpha1.ContainerStateRunning 87 var terminated *v1alpha1.ContainerStateTerminated 88 switch state.Status { 89 case dockercompose.ContainerStatusCreated, 90 dockercompose.ContainerStatusPaused, 91 dockercompose.ContainerStatusRestarting: 92 waiting = &v1alpha1.ContainerStateWaiting{Reason: state.Status} 93 case dockercompose.ContainerStatusRunning: 94 running = &v1alpha1.ContainerStateRunning{ 95 StartedAt: apis.NewTime(state.StartedAt.Time), 96 } 97 case dockercompose.ContainerStatusRemoving, 98 dockercompose.ContainerStatusExited, 99 dockercompose.ContainerStatusDead: 100 terminated = &v1alpha1.ContainerStateTerminated{ 101 ExitCode: state.ExitCode, 102 Reason: state.Status, 103 StartedAt: apis.NewTime(state.StartedAt.Time), 104 FinishedAt: apis.NewTime(state.FinishedAt.Time), 105 } 106 } 107 cName := r.res.Status.ContainerName 108 c := v1alpha1.Container{ 109 Name: cName, 110 ID: cID, 111 State: v1alpha1.ContainerState{ 112 Waiting: waiting, 113 Running: running, 114 Terminated: terminated, 115 }, 116 Ready: running != nil, 117 } 118 visit(pod, c) 119 } 120 }