Skip to content

Commit 22ae3cf

Browse files
committed
feat(multiarch): add support for target platform in container backends
Build each image for specified platforms list using actual emulated builder in container backend. Signed-off-by: Timofey Kirillov <timofey.kirillov@flant.com>
1 parent d430d7c commit 22ae3cf

31 files changed

+521
-359
lines changed

cmd/werf/ci_env/ci_env.go

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818
"github.com/werf/logboek"
1919
"github.com/werf/logboek/pkg/level"
2020
"github.com/werf/werf/cmd/werf/common"
21-
"github.com/werf/werf/pkg/container_backend/thirdparty/platformutil"
2221
"github.com/werf/werf/pkg/docker"
2322
"github.com/werf/werf/pkg/docker_registry"
2423
"github.com/werf/werf/pkg/git_repo"
@@ -118,17 +117,15 @@ func runCIEnv(cmd *cobra.Command, args []string) error {
118117
return err
119118
}
120119

121-
var platform string
122-
if len(commonCmdData.GetPlatform()) > 0 {
123-
platforms, err := platformutil.NormalizeUserParams(commonCmdData.GetPlatform())
124-
if err != nil {
125-
return fmt.Errorf("unable to normalize platforms params %v: %w", commonCmdData.GetPlatform(), err)
126-
}
127-
platform = platforms[0]
128-
}
129120
// FIXME(multiarch): do not initialize platform in backend here
130121
// FIXME(multiarch): why docker initialization here? what if buildah backend enabled?
131-
if err := docker.Init(ctx, dockerConfig, *commonCmdData.LogVerbose, *commonCmdData.LogDebug, platform); err != nil {
122+
opts := docker.InitOptions{
123+
DockerConfigDir: dockerConfig,
124+
ClaimPlatforms: commonCmdData.GetPlatform(),
125+
Verbose: *commonCmdData.LogVerbose,
126+
Debug: *commonCmdData.LogDebug,
127+
}
128+
if err := docker.Init(ctx, opts); err != nil {
132129
return fmt.Errorf("docker init failed in dir %q: %w", dockerConfig, err)
133130
}
134131

cmd/werf/common/container_backend.go

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"github.com/werf/werf/pkg/buildah"
1111
"github.com/werf/werf/pkg/buildah/thirdparty"
1212
"github.com/werf/werf/pkg/container_backend"
13-
"github.com/werf/werf/pkg/container_backend/thirdparty/platformutil"
1413
"github.com/werf/werf/pkg/docker"
1514
"github.com/werf/werf/pkg/util"
1615
"github.com/werf/werf/pkg/werf"
@@ -100,24 +99,14 @@ func InitProcessContainerBackend(ctx context.Context, cmdData *CmdData) (contain
10099

101100
insecure := *cmdData.InsecureRegistry || *cmdData.SkipTlsVerifyRegistry
102101

103-
// FIXME(multiarch): rework container backend platform initialization, specify platform only when running build operation
104-
var platform string
105-
if len(cmdData.GetPlatform()) > 0 {
106-
platforms, err := platformutil.NormalizeUserParams(cmdData.GetPlatform())
107-
if err != nil {
108-
return nil, ctx, fmt.Errorf("unable to normalize platforms params %v: %w", cmdData.GetPlatform(), err)
109-
}
110-
platform = platforms[0]
111-
}
112-
113102
b, err := buildah.NewBuildah(*buildahMode, buildah.BuildahOpts{
114103
CommonBuildahOpts: buildah.CommonBuildahOpts{
115104
TmpDir: filepath.Join(werf.GetServiceDir(), "tmp", "buildah"),
116105
Insecure: insecure,
117106
Isolation: buildahIsolation,
118107
StorageDriver: storageDriver,
119108
},
120-
NativeModeOpts: buildah.NativeModeOpts{Platform: platform},
109+
NativeModeOpts: buildah.NativeModeOpts{},
121110
})
122111
if err != nil {
123112
return nil, ctx, fmt.Errorf("unable to get buildah client: %w", err)
@@ -136,13 +125,14 @@ func InitProcessContainerBackend(ctx context.Context, cmdData *CmdData) (contain
136125
}
137126

138127
func InitProcessDocker(ctx context.Context, cmdData *CmdData) (context.Context, error) {
139-
// FIXME(multiarch): rework container backend platform initialization, specify platform only when running build operation
140-
var platform string
141-
if len(cmdData.GetPlatform()) > 0 {
142-
platform = cmdData.GetPlatform()[0]
128+
opts := docker.InitOptions{
129+
DockerConfigDir: *cmdData.DockerConfig,
130+
ClaimPlatforms: cmdData.GetPlatform(),
131+
Verbose: *cmdData.LogVerbose,
132+
Debug: *cmdData.LogDebug,
143133
}
144134

145-
if err := docker.Init(ctx, *cmdData.DockerConfig, *cmdData.LogVerbose, *cmdData.LogDebug, platform); err != nil {
135+
if err := docker.Init(ctx, opts); err != nil {
146136
return ctx, fmt.Errorf("unable to init docker for buildah container backend: %w", err)
147137
}
148138

cmd/werf/kube_run/kube_run.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,13 +416,21 @@ func run(ctx context.Context, pod, secret, namespace string, werfConfig *config.
416416
}
417417
}
418418

419+
targetPlatforms, err := c.GetTargetPlatforms()
420+
if err != nil {
421+
return fmt.Errorf("invalid target platforms: %w", err)
422+
}
423+
if len(targetPlatforms) == 0 {
424+
targetPlatforms = []string{containerBackend.GetDefaultPlatform()}
425+
}
426+
419427
// FIXME(multiarch): specify multiarch manifest here
420-
if err := c.FetchLastImageStage(ctx, "", imageName); err != nil {
428+
if err := c.FetchLastImageStage(ctx, targetPlatforms[0], imageName); err != nil {
421429
return err
422430
}
423431

424432
// FIXME(multiarch): specify multiarch manifest here
425-
image = c.GetImageNameForLastImageStage("", imageName)
433+
image = c.GetImageNameForLastImageStage(targetPlatforms[0], imageName)
426434
return nil
427435
}); err != nil {
428436
return err

cmd/werf/run/run.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -402,11 +402,19 @@ func run(ctx context.Context, containerBackend container_backend.ContainerBacken
402402
}
403403
}
404404

405-
if err := c.FetchLastImageStage(ctx, "", imageName); err != nil {
405+
targetPlatforms, err := c.GetTargetPlatforms()
406+
if err != nil {
407+
return fmt.Errorf("invalid target platforms: %w", err)
408+
}
409+
if len(targetPlatforms) == 0 {
410+
targetPlatforms = []string{containerBackend.GetDefaultPlatform()}
411+
}
412+
413+
if err := c.FetchLastImageStage(ctx, targetPlatforms[0], imageName); err != nil {
406414
return err
407415
}
408416

409-
dockerImageName = c.GetImageNameForLastImageStage("", imageName)
417+
dockerImageName = c.GetImageNameForLastImageStage(targetPlatforms[0], imageName)
410418
return nil
411419
}); err != nil {
412420
return err

pkg/build/build_phase.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ func (phase *BuildPhase) findAndFetchStageFromSecondaryStagesStorage(ctx context
531531
err := logboek.Context(ctx).Default().LogProcess("Copy suitable stage from secondary %s", secondaryStagesStorage.String()).DoError(func() error {
532532
// Copy suitable stage from a secondary stages storage to the primary stages storage
533533
// while primary stages storage lock for this digest is held
534-
if copiedStageDesc, err := storageManager.CopySuitableByDigestStage(ctx, secondaryStageDesc, secondaryStagesStorage, storageManager.GetStagesStorage(), phase.Conveyor.ContainerBackend); err != nil {
534+
if copiedStageDesc, err := storageManager.CopySuitableByDigestStage(ctx, secondaryStageDesc, secondaryStagesStorage, storageManager.GetStagesStorage(), phase.Conveyor.ContainerBackend, img.TargetPlatform); err != nil {
535535
return fmt.Errorf("unable to copy suitable stage %s from %s to %s: %w", secondaryStageDesc.StageID.String(), secondaryStagesStorage.String(), storageManager.GetStagesStorage().String(), err)
536536
} else {
537537
i := phase.Conveyor.GetOrCreateStageImage(copiedStageDesc.Info.Name, phase.StagesIterator.GetPrevImage(img, stg), stg, img)
@@ -783,7 +783,9 @@ func (phase *BuildPhase) atomicBuildStageImage(ctx context.Context, img *image.I
783783
}
784784

785785
if err := logboek.Context(ctx).Streams().DoErrorWithTag(fmt.Sprintf("%s/%s", img.LogName(), stg.Name()), img.LogTagStyle(), func() error {
786-
return stageImage.Builder.Build(ctx, phase.ImageBuildOptions)
786+
opts := phase.ImageBuildOptions
787+
opts.TargetPlatform = img.TargetPlatform
788+
return stageImage.Builder.Build(ctx, opts)
787789
}); err != nil {
788790
return fmt.Errorf("failed to build image for stage %s with digest %s: %w", stg.Name(), stg.GetDigest(), err)
789791
}

pkg/build/conveyor.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,12 @@ func (c *Conveyor) GetImportServer(ctx context.Context, targetPlatform, imageNam
223223
if stageName != "" {
224224
importServerName += "/" + stageName
225225
}
226-
// FIXME(multiarch): in this place we should get our current platform from the container backend in the case when targetPlatform is empty
227-
if targetPlatform != "" && targetPlatform != "linux/amd64" {
228-
importServerName += "[" + targetPlatform + "]"
226+
227+
if targetPlatform == "" {
228+
panic("assertion: targetPlatform cannot be empty")
229229
}
230+
importServerName += fmt.Sprintf("[%s]", targetPlatform)
231+
230232
if srv, hasKey := c.importServers[importServerName]; hasKey {
231233
return srv, nil
232234
}
@@ -249,9 +251,9 @@ func (c *Conveyor) GetImportServer(ctx context.Context, targetPlatform, imageNam
249251
DoError(func() error {
250252
var tmpDir string
251253
if stageName == "" {
252-
tmpDir = filepath.Join(c.tmpDir, "import-server", imageName)
254+
tmpDir = filepath.Join(c.tmpDir, "import-server", imageName, targetPlatform)
253255
} else {
254-
tmpDir = filepath.Join(c.tmpDir, "import-server", fmt.Sprintf("%s-%s", imageName, stageName))
256+
tmpDir = filepath.Join(c.tmpDir, "import-server", fmt.Sprintf("%s-%s", imageName, stageName), targetPlatform)
255257
}
256258

257259
if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {
@@ -682,7 +684,7 @@ func (c *Conveyor) GetOrCreateStageImage(name string, prevStageImage *stage.Stag
682684
return stageImage
683685
}
684686

685-
i := container_backend.NewLegacyStageImage(extractLegacyStageImage(prevStageImage), name, c.ContainerBackend)
687+
i := container_backend.NewLegacyStageImage(extractLegacyStageImage(prevStageImage), name, c.ContainerBackend, img.TargetPlatform)
686688

687689
var baseImage string
688690
if stg != nil {
@@ -701,6 +703,10 @@ func (c *Conveyor) GetOrCreateStageImage(name string, prevStageImage *stage.Stag
701703
}
702704

703705
func (c *Conveyor) GetImage(targetPlatform, name string) *image.Image {
706+
if targetPlatform == "" {
707+
panic("assertion: targetPlatform should not be empty")
708+
}
709+
704710
for _, img := range c.imagesTree.GetImages() {
705711
if img.GetName() == name && img.TargetPlatform == targetPlatform {
706712
return img

pkg/build/image/dockerfile.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222
"github.com/werf/werf/pkg/util"
2323
)
2424

25-
func MapDockerfileConfigToImagesSets(ctx context.Context, dockerfileImageConfig *config.ImageFromDockerfile, opts CommonImageOptions) (ImagesSets, error) {
25+
func MapDockerfileConfigToImagesSets(ctx context.Context, dockerfileImageConfig *config.ImageFromDockerfile, targetPlatform string, opts CommonImageOptions) (ImagesSets, error) {
2626
if dockerfileImageConfig.Staged {
2727
relDockerfilePath := filepath.Join(dockerfileImageConfig.Context, dockerfileImageConfig.Dockerfile)
2828
dockerfileData, err := opts.GiterminismManager.FileReader().ReadDockerfile(ctx, relDockerfilePath)
@@ -44,10 +44,10 @@ func MapDockerfileConfigToImagesSets(ctx context.Context, dockerfileImageConfig
4444
return nil, fmt.Errorf("unable to parse dockerfile %s: %w", relDockerfilePath, err)
4545
}
4646

47-
return mapDockerfileToImagesSets(ctx, d, dockerfileImageConfig, opts)
47+
return mapDockerfileToImagesSets(ctx, d, dockerfileImageConfig, targetPlatform, opts)
4848
}
4949

50-
img, err := mapLegacyDockerfileToImage(ctx, dockerfileImageConfig, opts)
50+
img, err := mapLegacyDockerfileToImage(ctx, dockerfileImageConfig, targetPlatform, opts)
5151
if err != nil {
5252
return nil, err
5353
}
@@ -59,7 +59,7 @@ func MapDockerfileConfigToImagesSets(ctx context.Context, dockerfileImageConfig
5959
return ret, nil
6060
}
6161

62-
func mapDockerfileToImagesSets(ctx context.Context, cfg *dockerfile.Dockerfile, dockerfileImageConfig *config.ImageFromDockerfile, opts CommonImageOptions) (ImagesSets, error) {
62+
func mapDockerfileToImagesSets(ctx context.Context, cfg *dockerfile.Dockerfile, dockerfileImageConfig *config.ImageFromDockerfile, targetPlatform string, opts CommonImageOptions) (ImagesSets, error) {
6363
var ret ImagesSets
6464

6565
targetStage, err := cfg.GetTargetStage()
@@ -106,7 +106,7 @@ func mapDockerfileToImagesSets(ctx context.Context, cfg *dockerfile.Dockerfile,
106106
var img *Image
107107
var err error
108108
if baseStg := cfg.FindStage(stg.BaseName); baseStg != nil {
109-
img, err = NewImage(ctx, item.WerfImageName, StageAsBaseImage, ImageOptions{
109+
img, err = NewImage(ctx, targetPlatform, item.WerfImageName, StageAsBaseImage, ImageOptions{
110110
IsDockerfileImage: true,
111111
IsDockerfileTargetStage: item.IsTargetStage,
112112
DockerfileImageConfig: dockerfileImageConfig,
@@ -120,7 +120,7 @@ func mapDockerfileToImagesSets(ctx context.Context, cfg *dockerfile.Dockerfile,
120120

121121
appendQueue(baseStg.GetWerfImageName(), baseStg, item.Level+1)
122122
} else {
123-
img, err = NewImage(ctx, item.WerfImageName, ImageFromRegistryAsBaseImage, ImageOptions{
123+
img, err = NewImage(ctx, targetPlatform, item.WerfImageName, ImageFromRegistryAsBaseImage, ImageOptions{
124124
IsDockerfileImage: true,
125125
IsDockerfileTargetStage: item.IsTargetStage,
126126
DockerfileImageConfig: dockerfileImageConfig,
@@ -198,8 +198,8 @@ func mapDockerfileToImagesSets(ctx context.Context, cfg *dockerfile.Dockerfile,
198198
return ret, nil
199199
}
200200

201-
func mapLegacyDockerfileToImage(ctx context.Context, dockerfileImageConfig *config.ImageFromDockerfile, opts CommonImageOptions) (*Image, error) {
202-
img, err := NewImage(ctx, dockerfileImageConfig.Name, NoBaseImage, ImageOptions{
201+
func mapLegacyDockerfileToImage(ctx context.Context, dockerfileImageConfig *config.ImageFromDockerfile, targetPlatform string, opts CommonImageOptions) (*Image, error) {
202+
img, err := NewImage(ctx, targetPlatform, dockerfileImageConfig.Name, NoBaseImage, ImageOptions{
203203
CommonImageOptions: opts,
204204
IsDockerfileImage: true,
205205
IsDockerfileTargetStage: true,
@@ -258,7 +258,7 @@ func mapLegacyDockerfileToImage(ctx context.Context, dockerfileImageConfig *conf
258258
)
259259

260260
baseStageOptions := &stage.BaseStageOptions{
261-
TargetPlatform: opts.TargetPlatform,
261+
TargetPlatform: targetPlatform,
262262
ImageName: dockerfileImageConfig.Name,
263263
ProjectName: opts.ProjectName,
264264
}

pkg/build/image/image.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ type CommonImageOptions struct {
3838
ProjectName string
3939
ContainerWerfDir string
4040
TmpDir string
41-
TargetPlatform string
41+
42+
ForceTargetPlatformLogging bool
4243
}
4344

4445
type ImageOptions struct {
@@ -52,21 +53,25 @@ type ImageOptions struct {
5253
DockerfileExpanderFactory dockerfile.ExpanderFactory
5354
}
5455

55-
func NewImage(ctx context.Context, name string, baseImageType BaseImageType, opts ImageOptions) (*Image, error) {
56+
func NewImage(ctx context.Context, targetPlatform, name string, baseImageType BaseImageType, opts ImageOptions) (*Image, error) {
5657
switch baseImageType {
5758
case NoBaseImage, ImageFromRegistryAsBaseImage, StageAsBaseImage:
5859
default:
5960
panic(fmt.Sprintf("unknown opts.BaseImageType %q", baseImageType))
6061
}
6162

63+
if targetPlatform == "" {
64+
panic("assertion: targetPlatform cannot be empty")
65+
}
66+
6267
i := &Image{
6368
Name: name,
6469
CommonImageOptions: opts.CommonImageOptions,
6570
IsArtifact: opts.IsArtifact,
6671
IsDockerfileImage: opts.IsDockerfileImage,
6772
IsDockerfileTargetStage: opts.IsDockerfileTargetStage,
6873
DockerfileImageConfig: opts.DockerfileImageConfig,
69-
TargetPlatform: opts.TargetPlatform,
74+
TargetPlatform: targetPlatform,
7075

7176
baseImageType: baseImageType,
7277
baseImageReference: opts.BaseImageReference,
@@ -113,7 +118,11 @@ func (i *Image) LogName() string {
113118
}
114119

115120
func (i *Image) LogDetailedName() string {
116-
return logging.ImageLogProcessName(i.Name, i.IsArtifact, i.TargetPlatform)
121+
var targetPlatformForLog string
122+
if i.ForceTargetPlatformLogging || i.TargetPlatform != i.ContainerBackend.GetRuntimePlatform() {
123+
targetPlatformForLog = i.TargetPlatform
124+
}
125+
return logging.ImageLogProcessName(i.Name, i.IsArtifact, targetPlatformForLog)
117126
}
118127

119128
func (i *Image) LogProcessStyle() color.Style {

pkg/build/image/image_tree.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,12 @@ func (tree *ImagesTree) Calculate(ctx context.Context) error {
5252
return fmt.Errorf("invalid target platforms: %w", err)
5353
}
5454
if len(targetPlatforms) == 0 {
55-
targetPlatforms = []string{""}
55+
targetPlatforms = []string{tree.ContainerBackend.GetDefaultPlatform()}
5656
}
5757

58+
commonImageOpts := tree.CommonImageOptions
59+
commonImageOpts.ForceTargetPlatformLogging = (len(targetPlatforms) > 1)
60+
5861
builder := NewImagesSetsBuilder()
5962

6063
for _, iteration := range imageConfigSets {
@@ -80,18 +83,15 @@ func (tree *ImagesTree) Calculate(ctx context.Context) error {
8083
var err error
8184
var newImagesSets ImagesSets
8285

83-
commonOpts := tree.CommonImageOptions
84-
commonOpts.TargetPlatform = targetPlatform
85-
8686
switch imageConfig := imageConfigI.(type) {
8787
case config.StapelImageInterface:
88-
newImagesSets, err = MapStapelConfigToImagesSets(ctx, tree.werfConfig.Meta, imageConfig, commonOpts)
88+
newImagesSets, err = MapStapelConfigToImagesSets(ctx, tree.werfConfig.Meta, imageConfig, targetPlatform, commonImageOpts)
8989
if err != nil {
9090
return fmt.Errorf("unable to map stapel config to images sets: %w", err)
9191
}
9292

9393
case *config.ImageFromDockerfile:
94-
newImagesSets, err = MapDockerfileConfigToImagesSets(ctx, imageConfig, commonOpts)
94+
newImagesSets, err = MapDockerfileConfigToImagesSets(ctx, imageConfig, targetPlatform, commonImageOpts)
9595
if err != nil {
9696
return fmt.Errorf("unable to map dockerfile to images sets: %w", err)
9797
}

pkg/build/image/stapel.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import (
1212
"github.com/werf/werf/pkg/git_repo"
1313
)
1414

15-
func MapStapelConfigToImagesSets(ctx context.Context, metaConfig *config.Meta, stapelImageConfig config.StapelImageInterface, opts CommonImageOptions) (ImagesSets, error) {
16-
img, err := mapStapelConfigToImage(ctx, metaConfig, stapelImageConfig, opts)
15+
func MapStapelConfigToImagesSets(ctx context.Context, metaConfig *config.Meta, stapelImageConfig config.StapelImageInterface, targetPlatform string, opts CommonImageOptions) (ImagesSets, error) {
16+
img, err := mapStapelConfigToImage(ctx, metaConfig, stapelImageConfig, targetPlatform, opts)
1717
if err != nil {
1818
return nil, err
1919
}
@@ -25,7 +25,7 @@ func MapStapelConfigToImagesSets(ctx context.Context, metaConfig *config.Meta, s
2525
return ret, nil
2626
}
2727

28-
func mapStapelConfigToImage(ctx context.Context, metaConfig *config.Meta, stapelImageConfig config.StapelImageInterface, opts CommonImageOptions) (*Image, error) {
28+
func mapStapelConfigToImage(ctx context.Context, metaConfig *config.Meta, stapelImageConfig config.StapelImageInterface, targetPlatform string, opts CommonImageOptions) (*Image, error) {
2929
imageBaseConfig := stapelImageConfig.ImageBaseConfig()
3030
imageName := imageBaseConfig.Name
3131
imageArtifact := stapelImageConfig.IsArtifact()
@@ -46,7 +46,7 @@ func mapStapelConfigToImage(ctx context.Context, metaConfig *config.Meta, stapel
4646
imageOpts.BaseImageName = fromImageName
4747
}
4848

49-
image, err := NewImage(ctx, imageName, baseImageType, imageOpts)
49+
image, err := NewImage(ctx, targetPlatform, imageName, baseImageType, imageOpts)
5050
if err != nil {
5151
return nil, fmt.Errorf("unable to create image %q: %w", imageName, err)
5252
}

0 commit comments

Comments
 (0)