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  }