istio

Форк
0
316 строк · 10.0 Кб
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
	"context"
19
	"fmt"
20
	"os"
21
	"os/exec"
22
	"path/filepath"
23
	"runtime"
24
	"strings"
25
	"time"
26

27
	"github.com/spf13/cobra"
28
	"sigs.k8s.io/yaml"
29

30
	"istio.io/istio/pkg/log"
31
	testenv "istio.io/istio/pkg/test/env"
32
	"istio.io/istio/pkg/tracing"
33
	"istio.io/istio/pkg/util/sets"
34
	pkgversion "istio.io/istio/pkg/version"
35
)
36

37
func main() {
38
	rootCmd.Flags().StringSliceVar(&globalArgs.Hubs, "hub", globalArgs.Hubs, "docker hub(s)")
39
	rootCmd.Flags().StringSliceVar(&globalArgs.Tags, "tag", globalArgs.Tags, "docker tag(s)")
40

41
	rootCmd.Flags().StringVar(&globalArgs.BaseVersion, "base-version", globalArgs.BaseVersion, "base version to use")
42
	rootCmd.Flags().StringVar(&globalArgs.BaseImageRegistry, "image-base-registry", globalArgs.BaseImageRegistry, "base image registry to use")
43
	rootCmd.Flags().StringVar(&globalArgs.ProxyVersion, "proxy-version", globalArgs.ProxyVersion, "proxy version to use")
44
	rootCmd.Flags().StringVar(&globalArgs.ZtunnelVersion, "ztunnel-version", globalArgs.ZtunnelVersion, "ztunnel version to use")
45
	rootCmd.Flags().StringVar(&globalArgs.IstioVersion, "istio-version", globalArgs.IstioVersion, "istio version to use")
46

47
	rootCmd.Flags().StringSliceVar(&globalArgs.Targets, "targets", globalArgs.Targets, "targets to build")
48
	rootCmd.Flags().StringSliceVar(&globalArgs.Variants, "variants", globalArgs.Variants, "variants to build")
49
	rootCmd.Flags().StringSliceVar(&globalArgs.Architectures, "architectures", globalArgs.Architectures, "architectures to build")
50
	rootCmd.Flags().BoolVar(&globalArgs.Push, "push", globalArgs.Push, "push targets to registry")
51
	rootCmd.Flags().BoolVar(&globalArgs.Save, "save", globalArgs.Save, "save targets to tar.gz")
52
	rootCmd.Flags().BoolVar(&globalArgs.NoCache, "no-cache", globalArgs.NoCache, "disable caching")
53
	rootCmd.Flags().BoolVar(&globalArgs.NoClobber, "no-clobber", globalArgs.NoClobber, "do not allow pushing images that already exist")
54
	rootCmd.Flags().StringVar(&globalArgs.Builder, "builder", globalArgs.Builder, "type of builder to use. options are crane or docker")
55
	rootCmd.Flags().BoolVar(&version, "version", version, "show build version")
56

57
	rootCmd.Flags().BoolVar(&globalArgs.SupportsEmulation, "qemu", globalArgs.SupportsEmulation, "if enable, allows building images that require emulation")
58

59
	if err := rootCmd.Execute(); err != nil {
60
		os.Exit(-1)
61
	}
62
}
63

64
var privilegedHubs = sets.New[string](
65
	"docker.io/istio",
66
	"istio",
67
	"gcr.io/istio-release",
68
	"gcr.io/istio-testing",
69
)
70

71
var rootCmd = &cobra.Command{
72
	SilenceUsage: true,
73
	Short:        "Builds Istio docker images",
74
	RunE: func(cmd *cobra.Command, _ []string) error {
75
		t0 := time.Now()
76
		defer func() {
77
			log.WithLabels("runtime", time.Since(t0)).Infof("build complete")
78
		}()
79
		ctx, shutdown, err := tracing.InitializeFullBinary("docker-builder")
80
		if err != nil {
81
			return err
82
		}
83
		defer shutdown()
84
		if version {
85
			fmt.Println(pkgversion.Info.GitRevision)
86
			os.Exit(0)
87
		}
88
		log.Infof("Args: %s", globalArgs)
89
		if err := ValidateArgs(globalArgs); err != nil {
90
			return err
91
		}
92

93
		args, err := ReadPlan(ctx, globalArgs)
94
		if err != nil {
95
			return fmt.Errorf("plan: %v", err)
96
		}
97

98
		// The Istio image builder has two building modes - one utilizing docker, and one manually constructing
99
		// images using the go-containerregistry (crane) libraries.
100
		// The crane builder is much faster but less tested.
101
		// Neither builder is doing standard logic; see each builder for details.
102
		if args.Builder == CraneBuilder {
103
			return RunCrane(ctx, args)
104
		}
105

106
		return RunDocker(args)
107
	},
108
}
109

110
func ValidateArgs(a Args) error {
111
	if len(a.Targets) == 0 {
112
		return fmt.Errorf("no targets specified")
113
	}
114
	if a.Push && a.Save {
115
		// TODO(https://github.com/moby/buildkit/issues/1555) support both
116
		return fmt.Errorf("--push and --save are mutually exclusive")
117
	}
118
	_, inCI := os.LookupEnv("CI")
119
	if a.Push && len(privilegedHubs.Intersection(sets.New(a.Hubs...))) > 0 && !inCI {
120
		// Safety check against developer error. If they have a legitimate use case, they can set CI var
121
		return fmt.Errorf("pushing to official registry only supported in CI")
122
	}
123
	if !sets.New(DockerBuilder, CraneBuilder).Contains(a.Builder) {
124
		return fmt.Errorf("unknown builder %v", a.Builder)
125
	}
126

127
	if a.Builder == CraneBuilder && a.Save {
128
		return fmt.Errorf("crane builder does not support save")
129
	}
130
	if a.Builder == CraneBuilder && a.NoClobber {
131
		return fmt.Errorf("crane builder does not support no-clobber")
132
	}
133
	if a.Builder == CraneBuilder && a.NoCache {
134
		return fmt.Errorf("crane builder does not support no-cache")
135
	}
136
	if a.Builder == CraneBuilder && !a.Push {
137
		return fmt.Errorf("crane builder only supports pushing")
138
	}
139
	return nil
140
}
141

142
func ReadPlanTargets() ([]string, []string, error) {
143
	by, err := os.ReadFile(filepath.Join(testenv.IstioSrc, "tools", "docker.yaml"))
144
	if err != nil {
145
		return nil, nil, err
146
	}
147
	plan := BuildPlan{}
148
	if err := yaml.Unmarshal(by, &plan); err != nil {
149
		return nil, nil, err
150
	}
151
	bases := sets.New[string]()
152
	nonBases := sets.New[string]()
153
	for _, i := range plan.Images {
154
		if i.Base {
155
			bases.Insert(i.Name)
156
		} else {
157
			nonBases.Insert(i.Name)
158
		}
159
	}
160
	return sets.SortedList(bases), sets.SortedList(nonBases), nil
161
}
162

163
var LocalArch = fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
164

165
func ReadPlan(ctx context.Context, a Args) (Args, error) {
166
	_, span := tracing.Start(ctx, "ReadPlan")
167
	defer span.End()
168
	by, err := os.ReadFile(filepath.Join(testenv.IstioSrc, "tools", "docker.yaml"))
169
	if err != nil {
170
		return a, err
171
	}
172
	a.Plan = map[string]BuildPlan{}
173
	for _, arch := range a.Architectures {
174
		plan := BuildPlan{}
175

176
		// We allow variables in the plan
177
		input := os.Expand(string(by), func(s string) string {
178
			data := archToEnvMap(arch)
179
			data["SIDECAR"] = "envoy"
180
			if _, f := os.LookupEnv("DEBUG_IMAGE"); f {
181
				data["RELEASE_MODE"] = "debug"
182
			} else {
183
				data["RELEASE_MODE"] = "release"
184
			}
185
			if r, f := data[s]; f {
186
				return r
187
			}
188

189
			// Fallback to env
190
			return os.Getenv(s)
191
		})
192
		if err := yaml.Unmarshal([]byte(input), &plan); err != nil {
193
			return a, err
194
		}
195

196
		// Check targets are valid
197
		tgt := sets.New(a.Targets...)
198
		known := sets.New[string]()
199
		for _, img := range plan.Images {
200
			known.Insert(img.Name)
201
		}
202
		if unknown := sets.SortedList(tgt.Difference(known)); len(unknown) > 0 {
203
			return a, fmt.Errorf("unknown targets: %v", unknown)
204
		}
205

206
		// Filter down to requested targets
207
		// This is not arch specific, so we can just let it run for each arch.
208
		desiredImages := []ImagePlan{}
209
		for _, i := range plan.Images {
210
			canBuild := !i.EmulationRequired || (arch == LocalArch)
211
			if tgt.Contains(i.Name) {
212
				if !canBuild {
213
					log.Infof("Skipping %s for %s as --qemu is not passed", i.Name, arch)
214
					continue
215
				}
216
				desiredImages = append(desiredImages, i)
217
			}
218
		}
219
		plan.Images = desiredImages
220

221
		a.Plan[arch] = plan
222
	}
223
	return a, nil
224
}
225

226
// VerboseCommand runs a command, outputting stderr and stdout
227
func VerboseCommand(name string, arg ...string) *exec.Cmd {
228
	log.Infof("Running command: %v %v", name, strings.Join(arg, " "))
229
	cmd := exec.Command(name, arg...)
230
	cmd.Stderr = os.Stderr
231
	cmd.Stdout = os.Stdout
232
	return cmd
233
}
234

235
func StandardEnv(args Args) []string {
236
	env := os.Environ()
237
	if len(sets.New(args.Targets...).Delete("proxyv2")) <= 1 {
238
		// If we are building multiple, it is faster to build all binaries in a single invocation
239
		// Otherwise, build just the single item. proxyv2 is special since it is always built separately with tag=agent.
240
		// Ideally we would just always build the targets we need but our Makefile is not that smart
241
		env = append(env, "BUILD_ALL=false")
242
	}
243

244
	env = append(env,
245
		// Build should already run in container, having multiple layers of docker causes issues
246
		"BUILD_WITH_CONTAINER=0",
247
	)
248
	return env
249
}
250

251
var SkipMake = os.Getenv("SKIP_MAKE")
252

253
// RunMake runs a make command for the repo, with standard environment variables set
254
func RunMake(ctx context.Context, args Args, arch string, c ...string) error {
255
	_, span := tracing.Start(ctx, "RunMake")
256
	defer span.End()
257
	if len(c) == 0 {
258
		log.Infof("nothing to make")
259
		return nil
260
	}
261
	if SkipMake == "true" {
262
		return nil
263
	}
264
	shortArgs := []string{}
265
	// Shorten output to avoid a ton of long redundant paths
266
	for _, cs := range c {
267
		shortArgs = append(shortArgs, filepath.Base(cs))
268
	}
269
	if len(c) == 0 {
270
		log.Infof("Nothing to make")
271
		return nil
272
	}
273
	log.Infof("Running make for %v: %v", arch, strings.Join(shortArgs, " "))
274
	env := StandardEnv(args)
275
	env = append(env, archToGoFlags(arch)...)
276
	makeArgs := []string{"--no-print-directory"}
277
	makeArgs = append(makeArgs, c...)
278
	cmd := exec.Command("make", makeArgs...)
279
	log.Infof("env: %v", archToGoFlags(arch))
280
	cmd.Env = env
281
	cmd.Stderr = os.Stderr
282
	cmd.Stdout = os.Stdout
283
	cmd.Dir = testenv.IstioSrc
284
	if err := cmd.Run(); err != nil {
285
		return err
286
	}
287
	return nil
288
}
289

290
func archToGoFlags(a string) []string {
291
	s := []string{}
292
	for k, v := range archToEnvMap(a) {
293
		s = append(s, k+"="+v)
294
	}
295
	return s
296
}
297

298
func archToEnvMap(a string) map[string]string {
299
	os, arch, _ := strings.Cut(a, "/")
300
	return map[string]string{
301
		"TARGET_OS":        os,
302
		"TARGET_ARCH":      arch,
303
		"TARGET_OUT":       filepath.Join(testenv.IstioSrc, "out", fmt.Sprintf("%s_%s", os, arch)),
304
		"TARGET_OUT_LINUX": filepath.Join(testenv.IstioSrc, "out", fmt.Sprintf("linux_%s", arch)),
305
	}
306
}
307

308
// RunCommand runs a command for the repo, with standard environment variables set
309
func RunCommand(args Args, c string, cargs ...string) error {
310
	cmd := VerboseCommand(c, cargs...)
311
	cmd.Env = StandardEnv(args)
312
	cmd.Stderr = os.Stderr
313
	cmd.Stdout = os.Stdout
314
	cmd.Dir = testenv.IstioSrc
315
	return cmd.Run()
316
}
317

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.