Skip to content

Commit

Permalink
Add support for various 32-bit architectures (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
Otterverse authored Oct 2, 2023
1 parent 55ea6d5 commit fd38216
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 48 deletions.
7 changes: 5 additions & 2 deletions .canon.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
canon-default:
image_amd64: "ghcr.io/viamrobotics/canon:amd64"
image_arm64: "ghcr.io/viamrobotics/canon:arm64"
image_amd64: "amd64/debian"
image_arm64: "arm64v8/debian"
image_386: "i386/debian"
image_arm: "arm32v7/debian"
image_arm_v6: "arm32v5/debian"
1 change: 1 addition & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ linters:
- contextcheck
- cyclop
- deadcode
- depguard
- exhaustivestruct
- exhaustruct
- forbidigo
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ bin/canon: *.go go.mod go.sum canon_setup.sh
go build -tags osusergo,netgo -ldflags "-s -w" -o bin/canon .

bin/golangci-lint:
GOBIN=`pwd`/bin go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
GOBIN=`pwd`/bin go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.54.2

lint: bin/golangci-lint
go mod tidy
bin/golangci-lint run -v --fix

clean:
Expand Down
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,16 @@ change based on the current project/directory, as well as with different argumen

Profiles are defined with the following fields:

* `arch` The architecture (only amd64 or arm64 supported currently) to run the image as.
* `arch` The architecture (`amd64`, `arm64`, `386`, `arm`, or `arm/v6`) to run the image as.
- Note the architecture does NOT have to match the host in most cases where emulation is set up. See [Emulation](#emulation) below
- Defaults to the detected current architecture.
* `image` The docker image used by this profile. Can be overriden by `-image`
- Note, this should NOT be defined if using the architecture-specific image options below. It will override them both.
* `image_amd64` The AMD64 specific image to use when that architecture is selected.
* `image_arm64` The ARM64 specific image to use when that architecture is selected.
- Note, this should NOT be defined if using the architecture-specific image options below. It will override them all.
* `image_amd64` The AMD64 (x86_64) specific image to use when that architecture is selected.
* `image_arm64` The ARM64 (aarch64) specific image to use when that architecture is selected.
* `image_386` The 386 (x86) specific image to use when that architecture is selected.
* `image_arm` The arm (armv7l/armhf) specific image to use when that architecture is selected.
* `image_arm_v6` The arm/v6 (armv6l) specific image to use when that architecture is selected.
* `minimum_date` If the created timestamp of the image is older then this, force an update of the image.
- This allows project maintainers to automatically notified canon (and canon users) when an update is needed for a project.
- Obtain with `docker inspect -f '{{ .Created }}' IMAGE_NAME`
Expand Down Expand Up @@ -114,10 +117,14 @@ opened in the same environment, and can make build/download caching inside the c
can be set with the "persistent" value set to true. In this mode, any canon executions that use that profile will be run in the same
container. Exiting a shell (or a command ending) will not terminate the container either.

### Listing active containers

Run: `canon list` to list all currently running canon containers.

### Terminating persistent containers

Run: `canon terminate` to terminate the container that would currently be used.
Optionally `-a` can be appended to terminate ALL canon-managed containers.
Run: `canon terminate` to terminate the container that would currently be used (what is shown from `canon config`.)
Optionally `-a` can be appended to terminate ALL canon-managed containers (everything shown by `canon list` above.)

## Emulation

Expand Down
113 changes: 92 additions & 21 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,22 @@ import (

type Profile struct {
name string
Default bool `yaml:"default" mapstructure:"default"`
Image string `yaml:"image" mapstructure:"image"`
ImageAMD64 string `yaml:"image_amd64" mapstructure:"image_amd64"`
ImageARM64 string `yaml:"image_arm64" mapstructure:"image_arm64"`
Arch string `yaml:"arch" mapstructure:"arch"`
MinimumDate time.Time `yaml:"minimum_date" mapstructure:"minimum_date"`
UpdateInterval time.Duration `yaml:"update_interval" mapstructure:"update_interval"`
Persistent bool `yaml:"persistent" mapstructure:"persistent"`
SSH bool `yaml:"ssh" mapstructure:"ssh"`
NetRC bool `yaml:"netrc" mapstructure:"netrc"`
User string `yaml:"user" mapstructure:"user"`
Group string `yaml:"group" mapstructure:"group"`
Path string `yaml:"path" mapstructure:"path"`
Default bool `mapstructure:"default" yaml:"default"`
Image string `mapstructure:"image" yaml:"image"`
ImageAMD64 string `mapstructure:"image_amd64" yaml:"image_amd64"`
Image386 string `mapstructure:"image_386" yaml:"image_386"`
ImageARM64 string `mapstructure:"image_arm64" yaml:"image_arm64"`
ImageARM string `mapstructure:"image_arm" yaml:"image_arm"`
ImageARMv6 string `mapstructure:"image_arm_v6" yaml:"image_arm_v6"`
Arch string `mapstructure:"arch" yaml:"arch"`
MinimumDate time.Time `mapstructure:"minimum_date" yaml:"minimum_date"`
UpdateInterval time.Duration `mapstructure:"update_interval" yaml:"update_interval"`
Persistent bool `mapstructure:"persistent" yaml:"persistent"`
SSH bool `mapstructure:"ssh" yaml:"ssh"`
NetRC bool `mapstructure:"netrc" yaml:"netrc"`
User string `mapstructure:"user" yaml:"user"`
Group string `mapstructure:"group" yaml:"group"`
Path string `mapstructure:"path" yaml:"path"`
}

var activeProfile = &Profile{}
Expand Down Expand Up @@ -142,16 +145,17 @@ func parseConfigs() error {
flag.StringVar(&cfgPath, "config", userCfgPath, "config file")
flag.StringVar(&profileName, "profile", defProfileName, "profile name")
flag.StringVar(&activeProfile.Image, "image", activeProfile.Image, "docker image name")
flag.StringVar(&activeProfile.Arch, "arch", activeProfile.Arch, "architecture (\"amd64\" or \"arm64\")")
flag.StringVar(&activeProfile.Arch, "arch", activeProfile.Arch, "architecture (\"amd64\", \"arm64\", \"386\", \"arm\", \"arm/v6\")")
flag.StringVar(&activeProfile.User, "user", activeProfile.User, "user to map to inside the canon environment")
flag.StringVar(&activeProfile.Group, "group", activeProfile.Group, "group to map to inside the canon environment")
flag.BoolVar(&activeProfile.SSH, "ssh", activeProfile.SSH, "mount ~/.ssh (read-only) and forward SSH_AUTH_SOCK to the canon environment")
flag.BoolVar(&activeProfile.NetRC, "netrc", activeProfile.NetRC, "mount ~/.netrc (read-only) in the canon environment")

flag.Parse()

// swap again in case a CLI arg would change arch
swapArchImage(activeProfile)
return nil
return validateArch(activeProfile.Arch)
}

func findProjectConfig() (string, error) {
Expand Down Expand Up @@ -247,8 +251,11 @@ func mergeProfile(in interface{}, out *Profile) error {
if err := mapDecode(in, tempProf); err != nil {
return err
}
if tempProf.ImageAMD64 != "" || tempProf.ImageARM64 != "" {
out.Image = ""
for _, img := range []string{tempProf.ImageAMD64, tempProf.ImageARM64, tempProf.ImageARM, tempProf.ImageARMv6, tempProf.Image386} {
if img != "" {
out.Image = ""
break
}
}
return mapDecode(in, out)
}
Expand Down Expand Up @@ -380,15 +387,30 @@ func checkAll(args []string) bool {

func swapArchImage(profile *Profile) {
// abort if image is overridden and not one of the swapable options
if profile.Image != "" && profile.Image != profile.ImageAMD64 && profile.Image != profile.ImageARM64 {
var canSwap bool
for _, img := range []string{profile.ImageAMD64, profile.ImageARM64, profile.ImageARM, profile.ImageARMv6, profile.Image386} {
if profile.Image == "" || img == profile.Image {
canSwap = true
break
}
}
if !canSwap {
return
}

if profile.Arch == "amd64" && profile.ImageAMD64 != "" {
switch profile.Arch {
case "amd64":
profile.Image = profile.ImageAMD64
}
if profile.Arch == "arm64" && profile.ImageARM64 != "" {
case "arm64":
profile.Image = profile.ImageARM64
case "arm":
profile.Image = profile.ImageARM
case "arm/v6":
profile.Image = profile.ImageARMv6
case "386":
profile.Image = profile.Image386
default:
profile.Image = ""
}
}

Expand All @@ -412,3 +434,52 @@ func mapDecode(iface interface{}, p *Profile) error {
}
return dec.Decode(iface)
}

func validateArch(arch string) error {
switch arch {
case "amd64":
fallthrough
case "arm64":
fallthrough
case "arm":
fallthrough
case "arm/v6":
fallthrough
case "386":
return nil

case "armv7":
fallthrough
case "armv7l":
fallthrough
case "armhf":
fallthrough
case "arm/v7":
return errors.New("Invalid architecture: " + arch + "; Use just \"arm\"")

case "armv6":
fallthrough
case "armv6l":
fallthrough
case "armel":
return errors.New("Invalid architecture: " + arch + "; Use \"arm/v6\"")

case "x86_64":
return errors.New("Invalid architecture: " + arch + "; Use \"amd64\"")

case "arm/v8":
fallthrough
case "aarch64":
return errors.New("Invalid architecture: " + arch + "; Use \"arm64\"")

case "x86":
fallthrough
case "i386":
fallthrough
case "i686":
return errors.New("Invalid architecture: " + arch + "; Use \"386\"")

default:
return errors.New("Invalid architecture: " + arch)
}
}
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/viamrobotics/canon
go 1.19

require (
github.com/docker/docker v24.0.5+incompatible
github.com/docker/docker v24.0.6+incompatible
github.com/mitchellh/mapstructure v1.5.0
github.com/moby/term v0.5.0
github.com/opencontainers/image-spec v1.0.2
Expand All @@ -25,10 +25,10 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/stretchr/testify v1.8.1 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.12.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.11.0 // indirect
golang.org/x/tools v0.13.0 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gotest.tools/v3 v3.4.0 // indirect
)
16 changes: 8 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY=
github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE=
github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
Expand Down Expand Up @@ -67,8 +67,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand All @@ -78,8 +78,8 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
Expand All @@ -89,8 +89,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8=
golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
24 changes: 18 additions & 6 deletions update.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ func checkUpdate(curProfile *Profile, all, force bool) error {
// we want defaults but NOT the defaults for images
prof.ImageAMD64 = ""
prof.ImageARM64 = ""
prof.ImageARM = ""
prof.ImageARMv6 = ""
prof.Image386 = ""
prof.Image = ""

err = mapDecode(iface, prof)
Expand All @@ -185,15 +188,24 @@ func checkUpdate(curProfile *Profile, all, force bool) error {
func checkImageDate(profile *Profile, checkData ImageCheckData, force bool) []ImageDef {
var imageCandidates, images []ImageDef

// Dual arch profile
switch {
case profile.ImageAMD64 != "" && profile.ImageARM64 != "":
// multi arch profiles
if profile.ImageAMD64 != "" {
imageCandidates = append(imageCandidates, ImageDef{Image: profile.ImageAMD64, Platform: "linux/amd64"})
}
if profile.ImageARM64 != "" {
imageCandidates = append(imageCandidates, ImageDef{Image: profile.ImageARM64, Platform: "linux/arm64"})
case profile.Image != "":
}
if profile.ImageARM != "" {
imageCandidates = append(imageCandidates, ImageDef{Image: profile.ImageARM, Platform: "linux/arm"})
}
if profile.ImageARMv6 != "" {
imageCandidates = append(imageCandidates, ImageDef{Image: profile.ImageARMv6, Platform: "linux/arm/v6"})
}
if profile.Image386 != "" {
imageCandidates = append(imageCandidates, ImageDef{Image: profile.Image386, Platform: "linux/386"})
}
if profile.Image != "" {
imageCandidates = append(imageCandidates, ImageDef{Image: profile.Image, Platform: "linux/" + profile.Arch})
default:
return images
}

for _, i := range imageCandidates {
Expand Down

0 comments on commit fd38216

Please sign in to comment.