github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/pkg/model/k8s_target.go (about)

     1  package model
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  
     7  	"github.com/tilt-dev/tilt/internal/sliceutils"
     8  	"github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1"
     9  )
    10  
    11  // Specifies how a Pod's state factors into determining whether a resource is ready
    12  type PodReadinessMode string
    13  
    14  // Pod readiness isn't applicable to this resource
    15  const PodReadinessNone PodReadinessMode = ""
    16  
    17  // Always wait for pods to become ready.
    18  const PodReadinessWait PodReadinessMode = "wait"
    19  
    20  // Don't even wait for pods to appear.
    21  const PodReadinessIgnore PodReadinessMode = "ignore"
    22  
    23  // wait until the pod has completed successfully
    24  const PodReadinessSucceeded PodReadinessMode = "succeeded"
    25  
    26  type K8sTarget struct {
    27  	// An apiserver-driven data model for applying Kubernetes YAML.
    28  	//
    29  	// This will eventually replace K8sTarget. We represent this as an embedded
    30  	// struct while we're migrating fields.
    31  	v1alpha1.KubernetesApplySpec
    32  
    33  	Name TargetName
    34  
    35  	PodReadinessMode PodReadinessMode
    36  
    37  	// Map configRef -> number of times we (expect to) inject it.
    38  	// NOTE(maia): currently this map is only for use in metrics, though someday
    39  	// we want a better way of mapping configRefs -> their injection point(s)
    40  	// (right now, Tiltfile and Engine have two different ways of finding a
    41  	// given image in a k8s entity.
    42  	refInjectCounts map[string]int
    43  
    44  	// zero+ links assoc'd with this resource (to be displayed in UIs,
    45  	// in addition to any port forwards/LB endpoints)
    46  	Links []Link
    47  
    48  	// pathDependencies are files required by this target.
    49  	//
    50  	// For Tiltfile-based, YAML-driven (i.e. `k8s_yaml()`) resources, this is
    51  	// NOT used because it's not sufficient to reload the YAML and re-deploy;
    52  	// there is a lot of post-Tiltfile-load logic for resource assembly, image
    53  	// locator injection, etc. As a result, these resources have their YAML
    54  	// files registered as "config files", which cause the Tiltfile to be
    55  	// re-evaluated.
    56  	pathDependencies []string
    57  
    58  	FileWatchIgnores []v1alpha1.IgnoreDef
    59  }
    60  
    61  func NewK8sTargetForTesting(yaml string) K8sTarget {
    62  	apply := v1alpha1.KubernetesApplySpec{
    63  		YAML: yaml,
    64  	}
    65  	return K8sTarget{KubernetesApplySpec: apply}
    66  }
    67  
    68  func (k8s K8sTarget) GetFileWatchIgnores() []v1alpha1.IgnoreDef {
    69  	return k8s.FileWatchIgnores
    70  }
    71  
    72  func (k8s K8sTarget) Empty() bool { return reflect.DeepEqual(k8s, K8sTarget{}) }
    73  
    74  func (k8s K8sTarget) DependencyIDs() []TargetID {
    75  	result := make([]TargetID, 0, len(k8s.ImageMaps))
    76  	for _, im := range k8s.ImageMaps {
    77  		result = append(result, TargetID{
    78  			Type: TargetTypeImage,
    79  			Name: TargetName(im),
    80  		})
    81  	}
    82  	return result
    83  }
    84  
    85  func (k8s K8sTarget) RefInjectCounts() map[string]int {
    86  	return k8s.refInjectCounts
    87  }
    88  
    89  func (k8s K8sTarget) Validate() error {
    90  	if k8s.ID().Empty() {
    91  		return fmt.Errorf("[Validate] K8s resources missing name:\n%s", k8s.YAML)
    92  	}
    93  
    94  	// TODO(milas): improve error message
    95  	if k8s.KubernetesApplySpec.YAML == "" && k8s.KubernetesApplySpec.ApplyCmd == nil {
    96  		return fmt.Errorf("[Validate] K8s resources %q missing YAML", k8s.Name)
    97  	}
    98  
    99  	return nil
   100  }
   101  
   102  func (k8s K8sTarget) ID() TargetID {
   103  	return TargetID{
   104  		Type: TargetTypeK8s,
   105  		Name: k8s.Name,
   106  	}
   107  }
   108  
   109  // Dependencies are files required by this target.
   110  //
   111  // Part of the WatchableTarget interface.
   112  func (k8s K8sTarget) Dependencies() []string {
   113  	// sorting/de-duping guaranteed by setter
   114  	return k8s.pathDependencies
   115  }
   116  
   117  // Track which images this depends on.
   118  func (k8s K8sTarget) WithImageDependencies(imageMapDeps []string) K8sTarget {
   119  	k8s.ImageMaps = sliceutils.Dedupe(imageMapDeps)
   120  	return k8s
   121  }
   122  
   123  // WithPathDependencies registers paths that this K8sTarget depends on.
   124  func (k8s K8sTarget) WithPathDependencies(paths []string) K8sTarget {
   125  	k8s.pathDependencies = sliceutils.DedupedAndSorted(paths)
   126  	return k8s
   127  }
   128  
   129  func (k8s K8sTarget) WithRefInjectCounts(ric map[string]int) K8sTarget {
   130  	k8s.refInjectCounts = ric
   131  	return k8s
   132  }
   133  
   134  func (k8s K8sTarget) WithIgnores(ignores []v1alpha1.IgnoreDef) K8sTarget {
   135  	k8s.FileWatchIgnores = ignores
   136  	return k8s
   137  }
   138  
   139  var _ TargetSpec = K8sTarget{}
   140  
   141  func FilterLiveUpdateOnly(imageMapDeps []string, imageTargets []ImageTarget) []string {
   142  	result := make([]string, 0, len(imageMapDeps))
   143  	isLiveUpdateOnly := make(map[string]bool, len(imageTargets))
   144  	for _, image := range imageTargets {
   145  		isLiveUpdateOnly[image.ImageMapName()] = image.IsLiveUpdateOnly
   146  	}
   147  	for _, im := range imageMapDeps {
   148  		if isLiveUpdateOnly[im] {
   149  			continue
   150  		}
   151  		result = append(result, im)
   152  	}
   153  	return result
   154  }