Skip to content

Commit

Permalink
feat(docker): Add support for multi-platform build
Browse files Browse the repository at this point in the history
  • Loading branch information
MrCyjaneK committed Sep 12, 2021
1 parent f3e213f commit f6b74cd
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 32 deletions.
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"`
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, ";") {
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,
}
}

0 comments on commit f6b74cd

Please sign in to comment.