github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/container/reference.go (about) 1 package container 2 3 import ( 4 "context" 5 "fmt" 6 "path" 7 8 "github.com/distribution/reference" 9 "github.com/pkg/errors" 10 11 "github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1" 12 ) 13 14 // RefSet describes the references for a given image: 15 // 1. ConfigurationRef: ref as specified in the Tiltfile 16 // 2. LocalRef(): ref as used outside of the cluster (for Docker etc.) 17 // 3. ClusterRef(): ref as used inside the cluster (in k8s YAML etc.). Often equivalent to 18 // LocalRef, but in some cases they diverge: e.g. when using a local registry with KIND, 19 // the image localhost:1234/my-image (localRef) is referenced in the YAML as 20 // http://registry/my-image (clusterRef). 21 type RefSet struct { 22 // Ref as specified in Tiltfile; used to match a DockerBuild with 23 // corresponding k8s YAML. May contain tags, etc. (Also used as 24 // user-facing name for this image.) 25 ConfigurationRef RefSelector 26 27 // (Optional) registry to prepend to ConfigurationRef to yield ref to use in update and deploy 28 registry *v1alpha1.RegistryHosting 29 } 30 31 func NewRefSet(confRef RefSelector, reg *v1alpha1.RegistryHosting) (RefSet, error) { 32 r := RefSet{ 33 ConfigurationRef: confRef, 34 registry: reg, 35 } 36 return r, r.Validate() 37 } 38 39 func MustSimpleRefSet(ref RefSelector) RefSet { 40 r := RefSet{ 41 ConfigurationRef: ref, 42 } 43 if err := r.Validate(); err != nil { 44 panic(err) 45 } 46 return r 47 } 48 49 func RefSetFromImageMap(spec v1alpha1.ImageMapSpec, cluster *v1alpha1.Cluster) (RefSet, error) { 50 selector, err := SelectorFromImageMap(spec) 51 if err != nil { 52 return RefSet{}, fmt.Errorf("validating image: %v", err) 53 } 54 55 reg, err := RegistryFromCluster(cluster) 56 if err != nil { 57 return RefSet{}, fmt.Errorf("determining registry: %v", err) 58 } 59 60 refs, err := NewRefSet(selector, reg) 61 if err != nil { 62 return RefSet{}, fmt.Errorf("applying image %s to registry %s: %v", spec.Selector, reg, err) 63 } 64 return refs, nil 65 } 66 67 func (rs RefSet) WithoutRegistry() RefSet { 68 return MustSimpleRefSet(rs.ConfigurationRef) 69 } 70 71 func (rs RefSet) Registry() *v1alpha1.RegistryHosting { 72 if rs.registry == nil { 73 return nil 74 } 75 return rs.registry.DeepCopy() 76 } 77 78 func (rs RefSet) MustWithRegistry(reg *v1alpha1.RegistryHosting) RefSet { 79 rs.registry = reg 80 err := rs.Validate() 81 if err != nil { 82 panic(err) 83 } 84 return rs 85 } 86 87 func (rs RefSet) Validate() error { 88 if rs.registry != nil { 89 err := rs.registry.Validate(context.TODO()) 90 if err != nil { 91 return errors.Wrapf(err.ToAggregate(), "validating new RefSet with configuration ref %q", rs.ConfigurationRef) 92 } 93 } 94 _, err := ReplaceRegistryForLocalRef(rs.ConfigurationRef, rs.registry) 95 if err != nil { 96 return errors.Wrapf(err, "validating new RefSet with configuration ref %q", rs.ConfigurationRef) 97 } 98 99 _, err = ReplaceRegistryForContainerRuntimeRef(rs.ConfigurationRef, rs.registry) 100 if err != nil { 101 return errors.Wrapf(err, "validating new RefSet with configuration ref %q", rs.ConfigurationRef) 102 } 103 104 return nil 105 } 106 107 // LocalRef returns the ref by which this image is referenced from outside the cluster 108 // (e.g. by `docker build`, `docker push`, etc.) 109 func (rs RefSet) LocalRef() reference.Named { 110 if IsEmptyRegistry(rs.registry) { 111 return rs.ConfigurationRef.AsNamedOnly() 112 } 113 ref, err := ReplaceRegistryForLocalRef(rs.ConfigurationRef, rs.registry) 114 if err != nil { 115 // Validation should have caught this before now :-/ 116 panic(fmt.Sprintf("ERROR deriving LocalRef: %v", err)) 117 } 118 119 return ref 120 } 121 122 // ClusterRef returns the ref by which this image will be pulled by 123 // the container runtime in the cluster. 124 // 125 // For example, the registry host (that the user/Tilt *push* to) might be 126 // something like `localhost:1234/foo`, referring to an exposed port from the 127 // registry Docker container. However, when the container runtime (itself 128 // generally running within a Docker container), won't see it on localhost, 129 // and will instead use a reference like `registry:5000/foo`. 130 // 131 // If HostFromContainerRuntime is not set on the registry for the RefSet, the 132 // Host will be used instead. This is common in cases where both the user and 133 // the container runtime refer to the registry in the same way. 134 // 135 // Note that this is specific to the container runtime, which might have its 136 // own config for the host. The local registry specification allows an 137 // additional "ClusterFromClusterNetwork" value, which describes a generic way 138 // for access from within the cluster network (e.g. via cluster provided DNS). 139 // Within Tilt, this value is NOT used for business logic, so sometimes "cluster 140 // ref" is used to refer to the container runtime ref. The API types, however, 141 // include both values and are labeled accurately. 142 // 143 // TODO(milas): Rename to ContainerRuntimeRef() 144 func (rs RefSet) ClusterRef() reference.Named { 145 if IsEmptyRegistry(rs.registry) { 146 return rs.LocalRef() 147 } 148 ref, err := ReplaceRegistryForContainerRuntimeRef(rs.ConfigurationRef, rs.registry) 149 if err != nil { 150 // Validation should have caught this before now :-/ 151 panic(fmt.Sprintf("ERROR deriving ClusterRef: %v", err)) 152 } 153 return ref 154 } 155 156 // AddTagSuffix tags the references for build/deploy. 157 // 158 // In most cases, we will use the tag given as-is. 159 // 160 // If we're in the mode where we're pushing to a single image name (for ECR), we'll 161 // tag it with [escaped-original-name]-[suffix]. 162 func (rs RefSet) AddTagSuffix(suffix string) (TaggedRefs, error) { 163 tag := suffix 164 if rs.registry != nil && rs.registry.SingleName != "" { 165 tag = fmt.Sprintf("%s-%s", escapeName(path.Base(rs.ConfigurationRef.RefFamiliarName())), tag) 166 } 167 168 localTagged, err := reference.WithTag(rs.LocalRef(), tag) 169 if err != nil { 170 return TaggedRefs{}, errors.Wrapf(err, "tagging localRef %s as %s", rs.LocalRef().String(), tag) 171 } 172 173 // TODO(maia): maybe TaggedRef should behave like RefSet, where clusterRef is optional 174 // and if not set, the accessor returns LocalRef instead 175 clusterTagged, err := reference.WithTag(rs.ClusterRef(), tag) 176 if err != nil { 177 return TaggedRefs{}, errors.Wrapf(err, "tagging clusterRef %s as %s", rs.ClusterRef().String(), tag) 178 } 179 return TaggedRefs{ 180 LocalRef: localTagged, 181 ClusterRef: clusterTagged, 182 }, nil 183 } 184 185 // TaggedRefs yielded by an image build 186 type TaggedRefs struct { 187 // LocalRef is the image name + tag as referenced from outside cluster 188 // (e.g. by the user or Tilt when pushing images). 189 LocalRef reference.NamedTagged 190 // ClusterRef is the image name + tag as referenced from the 191 // container runtime on the cluster. 192 // 193 // TODO(milas): Rename to ContainerRuntimeRef 194 ClusterRef reference.NamedTagged 195 }