istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tools/docker-builder/types.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package main 16 17 import ( 18 "fmt" 19 "os" 20 "path/filepath" 21 "regexp" 22 "strconv" 23 "strings" 24 25 "istio.io/istio/pkg/env" 26 "istio.io/istio/pkg/log" 27 testenv "istio.io/istio/pkg/test/env" 28 "istio.io/istio/pkg/util/sets" 29 ) 30 31 // Types mirrored from https://github.com/docker/buildx/blob/master/bake/bake.go 32 type Group struct { 33 Targets []string `json:"targets" hcl:"targets"` 34 } 35 36 type BakeFile struct { 37 Target map[string]Target `json:"target,omitempty"` 38 Group map[string]Group `json:"group,omitempty"` 39 } 40 41 type Target struct { 42 Context *string `json:"context,omitempty" hcl:"context,optional"` 43 Dockerfile *string `json:"dockerfile,omitempty" hcl:"dockerfile,optional"` 44 DockerfileInline *string `json:"dockerfile-inline,omitempty" hcl:"dockerfile-inline,optional"` 45 Args map[string]string `json:"args,omitempty" hcl:"args,optional"` 46 Labels map[string]string `json:"labels,omitempty" hcl:"labels,optional"` 47 Tags []string `json:"tags,omitempty" hcl:"tags,optional"` 48 CacheFrom []string `json:"cache-from,omitempty" hcl:"cache-from,optional"` 49 CacheTo []string `json:"cache-to,omitempty" hcl:"cache-to,optional"` 50 Target *string `json:"target,omitempty" hcl:"target,optional"` 51 Secrets []string `json:"secret,omitempty" hcl:"secret,optional"` 52 SSH []string `json:"ssh,omitempty" hcl:"ssh,optional"` 53 Platforms []string `json:"platforms,omitempty" hcl:"platforms,optional"` 54 Outputs []string `json:"output,omitempty" hcl:"output,optional"` 55 Pull *bool `json:"pull,omitempty" hcl:"pull,optional"` 56 NoCache *bool `json:"no-cache,omitempty" hcl:"no-cache,optional"` 57 } 58 59 type Args struct { 60 Push bool 61 Save bool 62 Builder string 63 SupportsEmulation bool 64 NoClobber bool 65 NoCache bool 66 Targets []string 67 Variants []string 68 Architectures []string 69 BaseVersion string 70 BaseImageRegistry string 71 ProxyVersion string 72 ZtunnelVersion string 73 IstioVersion string 74 Tags []string 75 Hubs []string 76 // Suffix on artifacts, used for multi-arch images where we cannot use manifests 77 suffix string 78 79 // Plan describes the build plan, read from file. 80 // This is a map of architecture -> plan, as the plan is arch specific. 81 Plan map[string]BuildPlan 82 } 83 84 func (a Args) PlanFor(arch string) BuildPlan { 85 return a.Plan[arch] 86 } 87 88 func (a Args) String() string { 89 var b strings.Builder 90 b.WriteString("Push: " + fmt.Sprint(a.Push) + "\n") 91 b.WriteString("Save: " + fmt.Sprint(a.Save) + "\n") 92 b.WriteString("NoClobber: " + fmt.Sprint(a.NoClobber) + "\n") 93 b.WriteString("NoCache: " + fmt.Sprint(a.NoCache) + "\n") 94 b.WriteString("Targets: " + fmt.Sprint(a.Targets) + "\n") 95 b.WriteString("Variants: " + fmt.Sprint(a.Variants) + "\n") 96 b.WriteString("Architectures: " + fmt.Sprint(a.Architectures) + "\n") 97 b.WriteString("BaseVersion: " + fmt.Sprint(a.BaseVersion) + "\n") 98 b.WriteString("BaseImageRegistry: " + fmt.Sprint(a.BaseImageRegistry) + "\n") 99 b.WriteString("ProxyVersion: " + fmt.Sprint(a.ProxyVersion) + "\n") 100 b.WriteString("ZtunnelVersion: " + fmt.Sprint(a.ZtunnelVersion) + "\n") 101 b.WriteString("IstioVersion: " + fmt.Sprint(a.IstioVersion) + "\n") 102 b.WriteString("Tags: " + fmt.Sprint(a.Tags) + "\n") 103 b.WriteString("Hubs: " + fmt.Sprint(a.Hubs) + "\n") 104 b.WriteString("Builder: " + fmt.Sprint(a.Builder) + "\n") 105 return b.String() 106 } 107 108 type ImagePlan struct { 109 // Name of the image. For example, "pilot" 110 Name string `json:"name"` 111 // Dockerfile path to build from 112 Dockerfile string `json:"dockerfile"` 113 // Files list files that are copied as-is into the image 114 Files []string `json:"files"` 115 // Targets list make targets that are ran and then copied into the image 116 Targets []string `json:"targets"` 117 // Base indicates if this is a base image or not 118 Base bool `json:"base"` 119 // EmulationRequired indicates if emulation is required when cross-compiling. It typically is not, 120 // as most building in Istio is done outside of docker. 121 // When this is set, cross-compile is disabled for components unless emulation is epxlicitly specified 122 EmulationRequired bool `json:"emulationRequired"` 123 } 124 125 func (p ImagePlan) Dependencies() []string { 126 v := []string{p.Dockerfile} 127 v = append(v, p.Files...) 128 v = append(v, p.Targets...) 129 return v 130 } 131 132 type BuildPlan struct { 133 Images []ImagePlan `json:"images"` 134 } 135 136 func (p BuildPlan) Targets() []string { 137 tgts := sets.New("init") // All targets depend on init 138 for _, img := range p.Images { 139 tgts.InsertAll(img.Targets...) 140 } 141 return sets.SortedList(tgts) 142 } 143 144 func (p BuildPlan) Find(n string) *ImagePlan { 145 for _, i := range p.Images { 146 if i.Name == n { 147 return &i 148 } 149 } 150 return nil 151 } 152 153 // Define variants, which control the base image of an image. 154 // Tags will have the variant append (like 1.0-distroless). 155 // The DefaultVariant is a special variant that has no explicit tag (like 1.0); it 156 // is not a unique variant though. Currently, it represents DebugVariant. 157 // If both DebugVariant and DefaultVariant are built, there will be a single build but multiple tags 158 const ( 159 // PrimaryVariant is the variant that DefaultVariant actually builds 160 PrimaryVariant = DebugVariant 161 162 DefaultVariant = "default" 163 DebugVariant = "debug" 164 DistrolessVariant = "distroless" 165 ) 166 167 const ( 168 CraneBuilder = "crane" 169 DockerBuilder = "docker" 170 ) 171 172 func DefaultArgs() Args { 173 // By default, we build all targets 174 var targets []string 175 _, nonBaseImages, err := ReadPlanTargets() 176 if err == nil { 177 targets = nonBaseImages 178 } 179 if legacy, f := os.LookupEnv("DOCKER_TARGETS"); f { 180 // Allow env var config. It is a string separated list like "docker.pilot docker.proxy" 181 targets = []string{} 182 for _, v := range strings.Split(legacy, " ") { 183 if v == "" { 184 continue 185 } 186 targets = append(targets, strings.TrimPrefix(v, "docker.")) 187 } 188 } 189 pv, err := testenv.ReadDepsSHA("PROXY_REPO_SHA") 190 if err != nil { 191 log.Warnf("failed to read proxy sha") 192 pv = "unknown" 193 } 194 zv, err := testenv.ReadDepsSHA("ZTUNNEL_REPO_SHA") 195 if err != nil { 196 log.Warnf("failed to read ztunnel sha") 197 zv = "unknown" 198 } 199 variants := []string{DefaultVariant} 200 if legacy, f := os.LookupEnv("DOCKER_BUILD_VARIANTS"); f { 201 variants = strings.Split(legacy, " ") 202 } 203 204 if os.Getenv("INCLUDE_UNTAGGED_DEFAULT") == "true" { 205 // This legacy env var was to workaround the old build logic not being very smart 206 // In the new builder, we automagically detect this. So just insert the 'default' variant 207 cur := sets.New(variants...) 208 cur.Insert(DefaultVariant) 209 variants = sets.SortedList(cur) 210 } 211 212 arch := []string{"linux/amd64"} 213 if legacy, f := os.LookupEnv("DOCKER_ARCHITECTURES"); f { 214 arch = strings.Split(legacy, ",") 215 } 216 217 hub := []string{env.Register("HUB", "localhost:5000", "").Get()} 218 if hubs, f := os.LookupEnv("HUBS"); f { 219 hub = strings.Split(hubs, " ") 220 } 221 tag := []string{env.Register("TAG", "latest", "").Get()} 222 if tags, f := os.LookupEnv("TAGS"); f { 223 tag = strings.Split(tags, " ") 224 } 225 226 builder := DockerBuilder 227 if b, f := os.LookupEnv("ISTIO_DOCKER_BUILDER"); f { 228 builder = b 229 } 230 231 // TODO: consider automatically detecting Qemu support 232 qemu := false 233 if b, f := os.LookupEnv("ISTIO_DOCKER_QEMU"); f { 234 q, err := strconv.ParseBool(b) 235 if err == nil { 236 qemu = q 237 } 238 } 239 240 return Args{ 241 Push: false, 242 Save: false, 243 NoCache: false, 244 Hubs: hub, 245 Tags: tag, 246 BaseVersion: fetchBaseVersion(), 247 BaseImageRegistry: fetchIstioBaseReg(), 248 IstioVersion: fetchIstioVersion(), 249 ProxyVersion: pv, 250 ZtunnelVersion: zv, 251 Architectures: arch, 252 Targets: targets, 253 Variants: variants, 254 Builder: builder, 255 SupportsEmulation: qemu, 256 } 257 } 258 259 var ( 260 globalArgs = DefaultArgs() 261 version = false 262 ) 263 264 var baseVersionRegexp = regexp.MustCompile(`BASE_VERSION \?= (.*)`) 265 266 func fetchBaseVersion() string { 267 if b, f := os.LookupEnv("BASE_VERSION"); f { 268 return b 269 } 270 b, err := os.ReadFile(filepath.Join(testenv.IstioSrc, "Makefile.core.mk")) 271 if err != nil { 272 log.Fatalf("failed to read file: %v", err) 273 return "unknown" 274 } 275 match := baseVersionRegexp.FindSubmatch(b) 276 if len(match) < 2 { 277 log.Fatalf("failed to find match") 278 return "unknown" 279 } 280 return string(match[1]) 281 } 282 283 var istioVersionRegexp = regexp.MustCompile(`VERSION \?= (.*)`) 284 285 func fetchIstioVersion() string { 286 if b, f := os.LookupEnv("VERSION"); f { 287 return b 288 } 289 b, err := os.ReadFile(filepath.Join(testenv.IstioSrc, "Makefile.core.mk")) 290 if err != nil { 291 log.Fatalf("failed to read file: %v", err) 292 return "unknown" 293 } 294 match := istioVersionRegexp.FindSubmatch(b) 295 if len(match) < 2 { 296 log.Fatalf("failed to find match") 297 return "unknown" 298 } 299 return string(match[1]) 300 } 301 302 var istioBaseRegRegexp = regexp.MustCompile(`ISTIO_BASE_REGISTRY \?= (.*)`) 303 304 func fetchIstioBaseReg() string { 305 if b, f := os.LookupEnv("ISTIO_BASE_REGISTRY"); f { 306 return b 307 } 308 b, err := os.ReadFile(filepath.Join(testenv.IstioSrc, "Makefile.core.mk")) 309 if err != nil { 310 log.Fatalf("failed to read file: %v", err) 311 return "unknown" 312 } 313 match := istioBaseRegRegexp.FindSubmatch(b) 314 if len(match) < 2 { 315 log.Fatalf("failed to find match") 316 return "unknown" 317 } 318 return string(match[1]) 319 }