blob: 9c4165455445459f96921b55b99f5f8645ee48df [file] [log] [blame]
// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
)
import (
"istio.io/pkg/log"
"k8s.io/utils/env"
)
import (
testenv "github.com/apache/dubbo-go-pixiu/pkg/test/env"
"github.com/apache/dubbo-go-pixiu/pkg/util/sets"
)
// Types mirrored from https://github.com/docker/buildx/blob/master/bake/bake.go
type Group struct {
Targets []string `json:"targets" hcl:"targets"`
}
type BakeFile struct {
Target map[string]Target `json:"target,omitempty"`
Group map[string]Group `json:"group,omitempty"`
}
type Target struct {
Context *string `json:"context,omitempty" hcl:"context,optional"`
Dockerfile *string `json:"dockerfile,omitempty" hcl:"dockerfile,optional"`
DockerfileInline *string `json:"dockerfile-inline,omitempty" hcl:"dockerfile-inline,optional"`
Args map[string]string `json:"args,omitempty" hcl:"args,optional"`
Labels map[string]string `json:"labels,omitempty" hcl:"labels,optional"`
Tags []string `json:"tags,omitempty" hcl:"tags,optional"`
CacheFrom []string `json:"cache-from,omitempty" hcl:"cache-from,optional"`
CacheTo []string `json:"cache-to,omitempty" hcl:"cache-to,optional"`
Target *string `json:"target,omitempty" hcl:"target,optional"`
Secrets []string `json:"secret,omitempty" hcl:"secret,optional"`
SSH []string `json:"ssh,omitempty" hcl:"ssh,optional"`
Platforms []string `json:"platforms,omitempty" hcl:"platforms,optional"`
Outputs []string `json:"output,omitempty" hcl:"output,optional"`
Pull *bool `json:"pull,omitempty" hcl:"pull,optional"`
NoCache *bool `json:"no-cache,omitempty" hcl:"no-cache,optional"`
}
type Args struct {
Push bool
Save bool
Builder string
KindLoad bool
NoClobber bool
NoCache bool
Targets []string
Variants []string
Architectures []string
BaseVersion string
ProxyVersion string
IstioVersion string
Tags []string
Hubs []string
// Plan describes the build plan, read from file.
// This is a map of architecture -> plan, as the plan is arch specific.
Plan map[string]BuildPlan
}
func (a Args) PlanFor(arch string) BuildPlan {
return a.Plan[arch]
}
func (a Args) String() string {
var b strings.Builder
b.WriteString("Push: " + fmt.Sprint(a.Push) + "\n")
b.WriteString("Save: " + fmt.Sprint(a.Save) + "\n")
b.WriteString("NoClobber: " + fmt.Sprint(a.NoClobber) + "\n")
b.WriteString("NoCache: " + fmt.Sprint(a.NoCache) + "\n")
b.WriteString("Targets: " + fmt.Sprint(a.Targets) + "\n")
b.WriteString("Variants: " + fmt.Sprint(a.Variants) + "\n")
b.WriteString("Architectures: " + fmt.Sprint(a.Architectures) + "\n")
b.WriteString("BaseVersion: " + fmt.Sprint(a.BaseVersion) + "\n")
b.WriteString("ProxyVersion: " + fmt.Sprint(a.ProxyVersion) + "\n")
b.WriteString("IstioVersion: " + fmt.Sprint(a.IstioVersion) + "\n")
b.WriteString("Tags: " + fmt.Sprint(a.Tags) + "\n")
b.WriteString("Hubs: " + fmt.Sprint(a.Hubs) + "\n")
b.WriteString("Builder: " + fmt.Sprint(a.Builder) + "\n")
return b.String()
}
type ImagePlan struct {
// Name of the image. For example, "pilot"
Name string `json:"name"`
// Dockerfile path to build from
Dockerfile string `json:"dockerfile"`
// Files list files that are copied as-is into the image
Files []string `json:"files"`
// Targets list make targets that are ran and then copied into the image
Targets []string `json:"targets"`
// Base indicates if this is a base image or not
Base bool `json:"base"`
}
func (p ImagePlan) Dependencies() []string {
v := []string{p.Dockerfile}
v = append(v, p.Files...)
v = append(v, p.Targets...)
return v
}
type BuildPlan struct {
Images []ImagePlan `json:"images"`
}
func (p BuildPlan) Targets() []string {
tgts := sets.New("init") // All targets depend on init
for _, img := range p.Images {
tgts.InsertAll(img.Targets...)
}
return tgts.SortedList()
}
func (p BuildPlan) Find(n string) ImagePlan {
for _, i := range p.Images {
if i.Name == n {
return i
}
}
panic("couldn't find target " + n)
}
// Define variants, which control the base image of an image.
// Tags will have the variant append (like 1.0-distroless).
// The DefaultVariant is a special variant that has no explicit tag (like 1.0); it
// is not a unique variant though. Currently, it represents DebugVariant.
// If both DebugVariant and DefaultVariant are built, there will be a single build but multiple tags
const (
// PrimaryVariant is the variant that DefaultVariant actually builds
PrimaryVariant = DebugVariant
DefaultVariant = "default"
DebugVariant = "debug"
DistrolessVariant = "distroless"
)
const (
CraneBuilder = "crane"
DockerBuilder = "docker"
)
func DefaultArgs() Args {
// By default, we build all targets
var targets []string
_, nonBaseImages, err := ReadPlanTargets()
if err == nil {
targets = nonBaseImages
}
if legacy, f := os.LookupEnv("DOCKER_TARGETS"); f {
// Allow env var config. It is a string separated list like "docker.pilot docker.proxy"
targets = []string{}
for _, v := range strings.Split(legacy, " ") {
if v == "" {
continue
}
targets = append(targets, strings.TrimPrefix(v, "docker."))
}
}
pv, err := testenv.ReadProxySHA()
if err != nil {
log.Warnf("failed to read proxy sha")
pv = "unknown"
}
variants := []string{DefaultVariant}
if legacy, f := os.LookupEnv("DOCKER_BUILD_VARIANTS"); f {
variants = strings.Split(legacy, " ")
}
if os.Getenv("INCLUDE_UNTAGGED_DEFAULT") == "true" {
// This legacy env var was to workaround the old build logic not being very smart
// In the new builder, we automagically detect this. So just insert the 'default' variant
cur := sets.New(variants...)
cur.Insert(DefaultVariant)
variants = cur.SortedList()
}
arch := []string{"linux/amd64"}
if legacy, f := os.LookupEnv("DOCKER_ARCHITECTURES"); f {
arch = strings.Split(legacy, ",")
}
hub := []string{env.GetString("HUB", "localhost:5000")}
if hubs, f := os.LookupEnv("HUBS"); f {
hub = strings.Split(hubs, " ")
}
tag := []string{env.GetString("TAG", "latest")}
if tags, f := os.LookupEnv("TAGS"); f {
tag = strings.Split(tags, " ")
}
builder := DockerBuilder
if b, f := os.LookupEnv("ISTIO_DOCKER_BUILDER"); f {
builder = b
}
return Args{
Push: false,
Save: false,
NoCache: false,
Hubs: hub,
Tags: tag,
BaseVersion: fetchBaseVersion(),
IstioVersion: fetchIstioVersion(),
ProxyVersion: pv,
Architectures: arch,
Targets: targets,
Variants: variants,
Builder: builder,
}
}
var (
globalArgs = DefaultArgs()
version = false
)
var baseVersionRegexp = regexp.MustCompile(`BASE_VERSION \?= (.*)`)
func fetchBaseVersion() string {
if b, f := os.LookupEnv("BASE_VERSION"); f {
return b
}
b, err := os.ReadFile(filepath.Join(testenv.IstioSrc, "Makefile.core.mk"))
if err != nil {
log.Fatalf("failed to read file: %v", err)
return "unknown"
}
match := baseVersionRegexp.FindSubmatch(b)
if len(match) < 2 {
log.Fatalf("failed to find match")
return "unknown"
}
return string(match[1])
}
var istioVersionRegexp = regexp.MustCompile(`VERSION \?= (.*)`)
func fetchIstioVersion() string {
if b, f := os.LookupEnv("VERSION"); f {
return b
}
b, err := os.ReadFile(filepath.Join(testenv.IstioSrc, "Makefile.core.mk"))
if err != nil {
log.Fatalf("failed to read file: %v", err)
return "unknown"
}
match := istioVersionRegexp.FindSubmatch(b)
if len(match) < 2 {
log.Fatalf("failed to find match")
return "unknown"
}
return string(match[1])
}