github.com/grahambrereton-form3/tilt@v0.10.18/pkg/model/image_target.go (about) 1 package model 2 3 import ( 4 "fmt" 5 "path/filepath" 6 "reflect" 7 "sort" 8 9 "github.com/docker/distribution/reference" 10 11 "github.com/windmilleng/tilt/internal/container" 12 "github.com/windmilleng/tilt/internal/sliceutils" 13 ) 14 15 type ImageTarget struct { 16 ConfigurationRef container.RefSelector 17 DeploymentRef reference.Named 18 BuildDetails BuildDetails 19 MatchInEnvVars bool 20 21 // User-supplied command to run when the container runs 22 // (i.e. overrides k8s yaml "command", container ENTRYPOINT, etc.) 23 OverrideCmd Cmd 24 25 cachePaths []string 26 27 // TODO(nick): It might eventually make sense to represent 28 // Tiltfile as a separate nodes in the build graph, rather 29 // than duplicating it in each ImageTarget. 30 tiltFilename string 31 dockerignores []Dockerignore 32 repos []LocalGitRepo 33 dependencyIDs []TargetID 34 } 35 36 func NewImageTarget(ref container.RefSelector) ImageTarget { 37 return ImageTarget{ConfigurationRef: ref, DeploymentRef: ref.AsNamedOnly()} 38 } 39 40 func ImageID(ref container.RefSelector) TargetID { 41 name := TargetName("") 42 if !ref.Empty() { 43 name = TargetName(ref.String()) 44 } 45 return TargetID{ 46 Type: TargetTypeImage, 47 Name: name, 48 } 49 } 50 51 func (i ImageTarget) ID() TargetID { 52 return ImageID(i.ConfigurationRef) 53 } 54 55 func (i ImageTarget) DependencyIDs() []TargetID { 56 return i.dependencyIDs 57 } 58 59 func (i ImageTarget) WithDependencyIDs(ids []TargetID) ImageTarget { 60 i.dependencyIDs = DedupeTargetIDs(ids) 61 return i 62 } 63 64 func (i ImageTarget) Validate() error { 65 if i.ConfigurationRef.Empty() { 66 return fmt.Errorf("[Validate] Image target missing image ref: %+v", i.BuildDetails) 67 } 68 69 switch bd := i.BuildDetails.(type) { 70 case DockerBuild: 71 if bd.BuildPath == "" { 72 return fmt.Errorf("[Validate] Image %q missing build path", i.ConfigurationRef) 73 } 74 case FastBuild: 75 if bd.BaseDockerfile == "" { 76 return fmt.Errorf("[Validate] Image %q missing base dockerfile", i.ConfigurationRef) 77 } 78 79 for _, mnt := range bd.Syncs { 80 if !filepath.IsAbs(mnt.LocalPath) { 81 return fmt.Errorf( 82 "[Validate] Image %q: mount must be an absolute path (got: %s)", i.ConfigurationRef, mnt.LocalPath) 83 } 84 } 85 case CustomBuild: 86 if bd.Command == "" { 87 return fmt.Errorf( 88 "[Validate] CustomBuild command must not be empty", 89 ) 90 } 91 default: 92 return fmt.Errorf("[Validate] Image %q has neither DockerBuildInfo nor FastBuildInfo", i.ConfigurationRef) 93 } 94 95 return nil 96 } 97 98 type BuildDetails interface { 99 buildDetails() 100 } 101 102 func (i ImageTarget) DockerBuildInfo() DockerBuild { 103 ret, _ := i.BuildDetails.(DockerBuild) 104 return ret 105 } 106 107 func (i ImageTarget) IsDockerBuild() bool { 108 _, ok := i.BuildDetails.(DockerBuild) 109 return ok 110 } 111 112 func (i ImageTarget) AnyLiveUpdateInfo() LiveUpdate { 113 switch details := i.BuildDetails.(type) { 114 case DockerBuild: 115 return details.LiveUpdate 116 case CustomBuild: 117 return details.LiveUpdate 118 default: 119 return LiveUpdate{} 120 } 121 } 122 123 func (i ImageTarget) AnyFastBuildInfo() FastBuild { 124 switch details := i.BuildDetails.(type) { 125 case FastBuild: 126 return details 127 case DockerBuild: 128 return details.FastBuild 129 case CustomBuild: 130 return details.Fast 131 } 132 return FastBuild{} 133 } 134 135 // FastBuildInfo returns the TOP LEVEL BUILD DETAILS, if a FastBuild. 136 // Does not return nested FastBuild details. 137 func (i ImageTarget) TopFastBuildInfo() FastBuild { 138 ret, _ := i.BuildDetails.(FastBuild) 139 return ret 140 } 141 142 // IsFastBuild checks if the TOP LEVEL BUILD DETAILS are for a FastBuild. 143 // (If this target is a DockerBuild || CustomBuild with a nested FastBuild, returns FALSE.) 144 func (i ImageTarget) IsFastBuild() bool { 145 _, ok := i.BuildDetails.(FastBuild) 146 return ok 147 } 148 149 func (i ImageTarget) CustomBuildInfo() CustomBuild { 150 ret, _ := i.BuildDetails.(CustomBuild) 151 return ret 152 } 153 154 func (i ImageTarget) IsCustomBuild() bool { 155 _, ok := i.BuildDetails.(CustomBuild) 156 return ok 157 } 158 159 func (i ImageTarget) WithBuildDetails(details BuildDetails) ImageTarget { 160 i.BuildDetails = details 161 return i 162 } 163 164 func (i ImageTarget) WithCachePaths(paths []string) ImageTarget { 165 i.cachePaths = append(append([]string{}, i.cachePaths...), paths...) 166 sort.Strings(i.cachePaths) 167 return i 168 } 169 170 func (i ImageTarget) CachePaths() []string { 171 return i.cachePaths 172 } 173 174 func (i ImageTarget) WithRepos(repos []LocalGitRepo) ImageTarget { 175 i.repos = append(append([]LocalGitRepo{}, i.repos...), repos...) 176 return i 177 } 178 179 func (i ImageTarget) WithDockerignores(dockerignores []Dockerignore) ImageTarget { 180 i.dockerignores = append(append([]Dockerignore{}, i.dockerignores...), dockerignores...) 181 return i 182 } 183 184 func (i ImageTarget) WithOverrideCommand(cmd Cmd) ImageTarget { 185 i.OverrideCmd = cmd 186 return i 187 } 188 189 func (i ImageTarget) Dockerignores() []Dockerignore { 190 return append([]Dockerignore{}, i.dockerignores...) 191 } 192 193 func (i ImageTarget) LocalPaths() []string { 194 switch bd := i.BuildDetails.(type) { 195 case DockerBuild: 196 return []string{bd.BuildPath} 197 case FastBuild: 198 result := make([]string, len(bd.Syncs)) 199 for i, mount := range bd.Syncs { 200 result[i] = mount.LocalPath 201 } 202 return result 203 case CustomBuild: 204 return append([]string(nil), bd.Deps...) 205 } 206 return nil 207 } 208 209 func (i ImageTarget) LocalRepos() []LocalGitRepo { 210 return i.repos 211 } 212 213 func (i ImageTarget) IgnoredLocalDirectories() []string { 214 return nil 215 } 216 217 func (i ImageTarget) TiltFilename() string { 218 return i.tiltFilename 219 } 220 221 func (i ImageTarget) WithTiltFilename(f string) ImageTarget { 222 i.tiltFilename = f 223 return i 224 } 225 226 // TODO(nick): This method should be deleted. We should just de-dupe and sort LocalPaths once 227 // when we create it, rather than have a duplicate method that does the "right" thing. 228 func (i ImageTarget) Dependencies() []string { 229 return sliceutils.DedupedAndSorted(i.LocalPaths()) 230 } 231 232 func ImageTargetsByID(iTargets []ImageTarget) map[TargetID]ImageTarget { 233 result := make(map[TargetID]ImageTarget, len(iTargets)) 234 for _, target := range iTargets { 235 result[target.ID()] = target 236 } 237 return result 238 } 239 240 type DockerBuild struct { 241 Dockerfile string 242 BuildPath string // the absolute path to the files 243 BuildArgs DockerBuildArgs 244 FastBuild FastBuild // Optionally, can use FastBuild to update this build in place. 245 LiveUpdate LiveUpdate // Optionally, can use LiveUpdate to update this build in place. 246 TargetStage DockerBuildTarget 247 } 248 249 func (DockerBuild) buildDetails() {} 250 251 type DockerBuildTarget string 252 253 func (s DockerBuildTarget) String() string { return string(s) } 254 255 type FastBuild struct { 256 BaseDockerfile string 257 Syncs []Sync 258 Runs []Run 259 Entrypoint Cmd 260 261 // A HotReload container image knows how to automatically 262 // reload any changes in the container. No need to restart it. 263 HotReload bool 264 } 265 266 func (FastBuild) buildDetails() {} 267 func (fb FastBuild) Empty() bool { return reflect.DeepEqual(fb, FastBuild{}) } 268 269 type CustomBuild struct { 270 Command string 271 // Deps is a list of file paths that are dependencies of this command. 272 Deps []string 273 274 // Optional: tag we expect the image to be built with (we use this to check that 275 // the expected image+tag has been created). 276 // If empty, we create an expected tag at the beginning of CustomBuild (and 277 // export $EXPECTED_REF=name:expected_tag ) 278 Tag string 279 280 Fast FastBuild 281 LiveUpdate LiveUpdate // Optionally, can use LiveUpdate to update this build in place. 282 DisablePush bool 283 } 284 285 func (CustomBuild) buildDetails() {} 286 287 func (cb CustomBuild) WithTag(t string) CustomBuild { 288 cb.Tag = t 289 return cb 290 } 291 292 var _ TargetSpec = ImageTarget{}