Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(docker): Add support for multi-platform build #563

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ require (
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/narqo/go-badge v0.0.0-20190124110329-d9415e4e1e9f
github.com/opencontainers/image-spec v1.0.1
github.com/shirou/gopsutil v3.20.10+incompatible
github.com/spf13/cobra v1.1.1
github.com/spf13/viper v1.7.0
Expand Down
1 change: 1 addition & 0 deletions pb/api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ message Job {
string sshPrivateKey = 21;
bool sshClone = 22;
string branch = 23;
string platform = 24;
}

message Command {
Expand Down
1 change: 1 addition & 0 deletions server/core/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type (
Image string `json:"image"`
Env string `json:"env"`
Mount string `json:"mount"`
Platform string `json:"platform"`
StartTime *time.Time `json:"startTime"`
EndTime *time.Time `json:"endTime"`
Status string `gorm:"not null;size:20;default:'queued'" json:"status"` // queued | running | passing | failing
Expand Down
1 change: 1 addition & 0 deletions server/core/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type (
Provider Provider `json:"-"`
EnvVariables []EnvVariable `json:"-"`
Mounts []*Mount `json:"mounts"`
Platforms string `json:"platforms"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be an array?

Suggested change
Platforms string `json:"platforms"`
Platforms []string `json:"platforms"`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah! That will make things easier

Perms Perms `json:"perms"`
Timestamp
}
Expand Down
1 change: 1 addition & 0 deletions server/scheduler/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ func (s *scheduler) startJob(job *core.Job, worker *core.Worker) {
Mount: strings.Split(job.Mount, ","),
SshPrivateKey: job.Build.Repository.SSHPrivateKey,
SshClone: job.Build.Repository.UseSSH,
Platform: job.Platform,
}

s.mu.Lock()
Expand Down
50 changes: 26 additions & 24 deletions server/store/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,31 +318,33 @@ func (s buildStore) TriggerBuild(opts core.TriggerBuildOpts) ([]*core.Job, error
}

var jobs []*core.Job
for _, j := range pjobs {
commands, err := protojson.Marshal(j.Commands)
if err != nil {
return nil, err
}

job := &core.Job{
Image: j.Image,
Commands: string(commands),
Env: strings.Join(j.Env, " "),
Mount: strings.Join(mnts, ","),
Stage: j.Stage,
BuildID: build.ID,
Cache: strings.Join(j.Cache, ","),
}
if err := s.jobs.Create(job); err != nil {
return nil, err
}
job, err = s.jobs.Find(job.ID)
if err != nil {
return nil, err
for platform := range strings.Split(repo.Platforms, ";") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for platform := range strings.Split(repo.Platforms, ";") {
for _,platform := range repo.Platforms {

for _, j := range pjobs {
commands, err := protojson.Marshal(j.Commands)
if err != nil {
return nil, err
}

job := &core.Job{
Image: j.Image,
Commands: string(commands),
Env: strings.Join(j.Env, " "),
Mount: strings.Join(mnts, ","),
Stage: j.Stage,
BuildID: build.ID,
Cache: strings.Join(j.Cache, ","),
Platform: strings.Split(repo.Platforms, ";")[platform],
}
if err := s.jobs.Create(job); err != nil {
return nil, err
}
job, err = s.jobs.Find(job.ID)
if err != nil {
return nil, err
}

jobs = append(jobs, job)
}

jobs = append(jobs, job)
}

return jobs, nil
}
4 changes: 2 additions & 2 deletions worker/app/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,8 @@ func (s *Server) StartJob(job *pb.Job, stream pb.API_StartJobServer) error {
}
logch <- []byte(yellow("done\r\n"))

logch <- []byte(yellow(fmt.Sprintf("==> Pulling image %s... ", image)))
if err := docker.PullImage(image, s.config.Registry); err != nil {
logch <- []byte(yellow(fmt.Sprintf("==> Pulling image %s (%s)... ", image, job.Platform)))
if err := docker.PullImage(image, job.Platform, s.config.Registry); err != nil {
logch <- []byte(fmt.Sprintf("%s\r\n", err.Error()))
} else {
logch <- []byte(yellow("done\r\n"))
Expand Down
9 changes: 5 additions & 4 deletions worker/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ func RunContainer(name, image string, job *api.Job, config *config.Config, env [
defer close(logch)
var shell string

resp, err := createContainer(cli, name, image, dir, []string{"/bin/bash"}, env, job.GetMount())
resp, err := createContainer(cli, name, image, dir, []string{"/bin/bash"}, env, job.GetMount(), job.Platform)
if err != nil {
logch <- []byte(fmt.Sprintf("%s\r\n", err.Error()))
return err
}
if !isContainerRunning(cli, resp.ID) {
if err := startContainer(cli, resp.ID); err != nil {
resp, err = createContainer(cli, name, image, dir, []string{"/bin/sh"}, env, job.GetMount())
resp, err = createContainer(cli, name, image, dir, []string{"/bin/sh"}, env, job.GetMount(), job.Platform)
if err != nil {
logch <- []byte(fmt.Sprintf("%s\r\n", err.Error()))
return err
Expand Down Expand Up @@ -244,7 +244,7 @@ func startContainer(cli *client.Client, id string) error {
}

// CreateContainer creates new Docker container.
func createContainer(cli *client.Client, name, image, dir string, cmd []string, env []string, mountdir []string) (container.ContainerCreateCreatedBody, error) {
func createContainer(cli *client.Client, name, image, dir string, cmd []string, env []string, mountdir []string, platform string) (container.ContainerCreateCreatedBody, error) {
if id, exists := ContainerExists(name); exists {
if err := cli.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{Force: true}); err != nil {
return container.ContainerCreateCreatedBody{}, err
Expand All @@ -266,6 +266,7 @@ func createContainer(cli *client.Client, name, image, dir string, cmd []string,
})
}

p := getPlatform(platform)
return cli.ContainerCreate(context.Background(), &container.Config{
Image: image,
Cmd: cmd,
Expand All @@ -274,7 +275,7 @@ func createContainer(cli *client.Client, name, image, dir string, cmd []string,
WorkingDir: "/build",
}, &container.HostConfig{
Mounts: mounts,
}, nil, nil, name)
}, nil, &p, name)
}

// IsContainerRunning returns true if container is running.
Expand Down
6 changes: 4 additions & 2 deletions worker/docker/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,16 @@ func PushImage(tag string) (io.ReadCloser, error) {
}

// PullImage pulls image from the registry.
func PullImage(image string, config *config.Registry) error {
func PullImage(image string, platform string, config *config.Registry) error {
ctx := context.Background()
cli, err := client.NewClientWithOpts()
if err != nil {
panic(err)
}

opts := types.ImagePullOptions{}
opts := types.ImagePullOptions{
Platform: platform,
}

if cfg.Username != "" && cfg.Password != "" {
authConfig := types.AuthConfig{Username: cfg.Username, Password: cfg.Password}
Expand Down
29 changes: 29 additions & 0 deletions worker/docker/platform.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package docker

import (
"runtime"
"strings"

v1 "github.com/opencontainers/image-spec/specs-go/v1"
)

func getPlatform(platform string) v1.Platform {
s := strings.Split(platform, "/")
if len(s) == 2 {
return v1.Platform{
OS: s[0],
Architecture: s[1],
}
}
if len(s) == 3 {
return v1.Platform{
OS: s[0],
Architecture: s[1],
Variant: s[2],
}
}
return v1.Platform{
OS: runtime.GOOS,
Architecture: runtime.GOARCH,
}
}