Skip to content

Commit fd38216

Browse files
authored
Add support for various 32-bit architectures (#7)
1 parent 55ea6d5 commit fd38216

File tree

8 files changed

+143
-48
lines changed

8 files changed

+143
-48
lines changed

.canon.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
canon-default:
2-
image_amd64: "ghcr.io/viamrobotics/canon:amd64"
3-
image_arm64: "ghcr.io/viamrobotics/canon:arm64"
2+
image_amd64: "amd64/debian"
3+
image_arm64: "arm64v8/debian"
4+
image_386: "i386/debian"
5+
image_arm: "arm32v7/debian"
6+
image_arm_v6: "arm32v5/debian"

.golangci.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ linters:
1212
- contextcheck
1313
- cyclop
1414
- deadcode
15+
- depguard
1516
- exhaustivestruct
1617
- exhaustruct
1718
- forbidigo

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ bin/canon: *.go go.mod go.sum canon_setup.sh
22
go build -tags osusergo,netgo -ldflags "-s -w" -o bin/canon .
33

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

77
lint: bin/golangci-lint
8+
go mod tidy
89
bin/golangci-lint run -v --fix
910

1011
clean:

README.md

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,16 @@ change based on the current project/directory, as well as with different argumen
7676

7777
Profiles are defined with the following fields:
7878

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

120+
### Listing active containers
121+
122+
Run: `canon list` to list all currently running canon containers.
123+
117124
### Terminating persistent containers
118125

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

122129
## Emulation
123130

config.go

Lines changed: 92 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,22 @@ import (
2222

2323
type Profile struct {
2424
name string
25-
Default bool `yaml:"default" mapstructure:"default"`
26-
Image string `yaml:"image" mapstructure:"image"`
27-
ImageAMD64 string `yaml:"image_amd64" mapstructure:"image_amd64"`
28-
ImageARM64 string `yaml:"image_arm64" mapstructure:"image_arm64"`
29-
Arch string `yaml:"arch" mapstructure:"arch"`
30-
MinimumDate time.Time `yaml:"minimum_date" mapstructure:"minimum_date"`
31-
UpdateInterval time.Duration `yaml:"update_interval" mapstructure:"update_interval"`
32-
Persistent bool `yaml:"persistent" mapstructure:"persistent"`
33-
SSH bool `yaml:"ssh" mapstructure:"ssh"`
34-
NetRC bool `yaml:"netrc" mapstructure:"netrc"`
35-
User string `yaml:"user" mapstructure:"user"`
36-
Group string `yaml:"group" mapstructure:"group"`
37-
Path string `yaml:"path" mapstructure:"path"`
25+
Default bool `mapstructure:"default" yaml:"default"`
26+
Image string `mapstructure:"image" yaml:"image"`
27+
ImageAMD64 string `mapstructure:"image_amd64" yaml:"image_amd64"`
28+
Image386 string `mapstructure:"image_386" yaml:"image_386"`
29+
ImageARM64 string `mapstructure:"image_arm64" yaml:"image_arm64"`
30+
ImageARM string `mapstructure:"image_arm" yaml:"image_arm"`
31+
ImageARMv6 string `mapstructure:"image_arm_v6" yaml:"image_arm_v6"`
32+
Arch string `mapstructure:"arch" yaml:"arch"`
33+
MinimumDate time.Time `mapstructure:"minimum_date" yaml:"minimum_date"`
34+
UpdateInterval time.Duration `mapstructure:"update_interval" yaml:"update_interval"`
35+
Persistent bool `mapstructure:"persistent" yaml:"persistent"`
36+
SSH bool `mapstructure:"ssh" yaml:"ssh"`
37+
NetRC bool `mapstructure:"netrc" yaml:"netrc"`
38+
User string `mapstructure:"user" yaml:"user"`
39+
Group string `mapstructure:"group" yaml:"group"`
40+
Path string `mapstructure:"path" yaml:"path"`
3841
}
3942

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

151154
flag.Parse()
155+
152156
// swap again in case a CLI arg would change arch
153157
swapArchImage(activeProfile)
154-
return nil
158+
return validateArch(activeProfile.Arch)
155159
}
156160

157161
func findProjectConfig() (string, error) {
@@ -247,8 +251,11 @@ func mergeProfile(in interface{}, out *Profile) error {
247251
if err := mapDecode(in, tempProf); err != nil {
248252
return err
249253
}
250-
if tempProf.ImageAMD64 != "" || tempProf.ImageARM64 != "" {
251-
out.Image = ""
254+
for _, img := range []string{tempProf.ImageAMD64, tempProf.ImageARM64, tempProf.ImageARM, tempProf.ImageARMv6, tempProf.Image386} {
255+
if img != "" {
256+
out.Image = ""
257+
break
258+
}
252259
}
253260
return mapDecode(in, out)
254261
}
@@ -380,15 +387,30 @@ func checkAll(args []string) bool {
380387

381388
func swapArchImage(profile *Profile) {
382389
// abort if image is overridden and not one of the swapable options
383-
if profile.Image != "" && profile.Image != profile.ImageAMD64 && profile.Image != profile.ImageARM64 {
390+
var canSwap bool
391+
for _, img := range []string{profile.ImageAMD64, profile.ImageARM64, profile.ImageARM, profile.ImageARMv6, profile.Image386} {
392+
if profile.Image == "" || img == profile.Image {
393+
canSwap = true
394+
break
395+
}
396+
}
397+
if !canSwap {
384398
return
385399
}
386400

387-
if profile.Arch == "amd64" && profile.ImageAMD64 != "" {
401+
switch profile.Arch {
402+
case "amd64":
388403
profile.Image = profile.ImageAMD64
389-
}
390-
if profile.Arch == "arm64" && profile.ImageARM64 != "" {
404+
case "arm64":
391405
profile.Image = profile.ImageARM64
406+
case "arm":
407+
profile.Image = profile.ImageARM
408+
case "arm/v6":
409+
profile.Image = profile.ImageARMv6
410+
case "386":
411+
profile.Image = profile.Image386
412+
default:
413+
profile.Image = ""
392414
}
393415
}
394416

@@ -412,3 +434,52 @@ func mapDecode(iface interface{}, p *Profile) error {
412434
}
413435
return dec.Decode(iface)
414436
}
437+
438+
func validateArch(arch string) error {
439+
switch arch {
440+
case "amd64":
441+
fallthrough
442+
case "arm64":
443+
fallthrough
444+
case "arm":
445+
fallthrough
446+
case "arm/v6":
447+
fallthrough
448+
case "386":
449+
return nil
450+
451+
case "armv7":
452+
fallthrough
453+
case "armv7l":
454+
fallthrough
455+
case "armhf":
456+
fallthrough
457+
case "arm/v7":
458+
return errors.New("Invalid architecture: " + arch + "; Use just \"arm\"")
459+
460+
case "armv6":
461+
fallthrough
462+
case "armv6l":
463+
fallthrough
464+
case "armel":
465+
return errors.New("Invalid architecture: " + arch + "; Use \"arm/v6\"")
466+
467+
case "x86_64":
468+
return errors.New("Invalid architecture: " + arch + "; Use \"amd64\"")
469+
470+
case "arm/v8":
471+
fallthrough
472+
case "aarch64":
473+
return errors.New("Invalid architecture: " + arch + "; Use \"arm64\"")
474+
475+
case "x86":
476+
fallthrough
477+
case "i386":
478+
fallthrough
479+
case "i686":
480+
return errors.New("Invalid architecture: " + arch + "; Use \"386\"")
481+
482+
default:
483+
return errors.New("Invalid architecture: " + arch)
484+
}
485+
}

go.mod

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/viamrobotics/canon
33
go 1.19
44

55
require (
6-
github.com/docker/docker v24.0.5+incompatible
6+
github.com/docker/docker v24.0.6+incompatible
77
github.com/mitchellh/mapstructure v1.5.0
88
github.com/moby/term v0.5.0
99
github.com/opencontainers/image-spec v1.0.2
@@ -25,10 +25,10 @@ require (
2525
github.com/pkg/errors v0.9.1 // indirect
2626
github.com/stretchr/testify v1.8.1 // indirect
2727
golang.org/x/mod v0.12.0 // indirect
28-
golang.org/x/net v0.12.0 // indirect
29-
golang.org/x/sys v0.10.0 // indirect
28+
golang.org/x/net v0.15.0 // indirect
29+
golang.org/x/sys v0.12.0 // indirect
3030
golang.org/x/time v0.3.0 // indirect
31-
golang.org/x/tools v0.11.0 // indirect
31+
golang.org/x/tools v0.13.0 // indirect
3232
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
3333
gotest.tools/v3 v3.4.0 // indirect
3434
)

go.sum

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
99
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1010
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
1111
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
12-
github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY=
13-
github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
12+
github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE=
13+
github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
1414
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
1515
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
1616
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
@@ -67,8 +67,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
6767
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
6868
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
6969
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
70-
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
71-
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
70+
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
71+
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
7272
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
7373
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
7474
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -78,8 +78,8 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
7878
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
7979
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
8080
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
81-
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
82-
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
81+
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
82+
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
8383
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
8484
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
8585
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
@@ -89,8 +89,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
8989
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
9090
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
9191
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
92-
golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8=
93-
golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
92+
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
93+
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
9494
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
9595
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
9696
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

update.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ func checkUpdate(curProfile *Profile, all, force bool) error {
160160
// we want defaults but NOT the defaults for images
161161
prof.ImageAMD64 = ""
162162
prof.ImageARM64 = ""
163+
prof.ImageARM = ""
164+
prof.ImageARMv6 = ""
165+
prof.Image386 = ""
163166
prof.Image = ""
164167

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

188-
// Dual arch profile
189-
switch {
190-
case profile.ImageAMD64 != "" && profile.ImageARM64 != "":
191+
// multi arch profiles
192+
if profile.ImageAMD64 != "" {
191193
imageCandidates = append(imageCandidates, ImageDef{Image: profile.ImageAMD64, Platform: "linux/amd64"})
194+
}
195+
if profile.ImageARM64 != "" {
192196
imageCandidates = append(imageCandidates, ImageDef{Image: profile.ImageARM64, Platform: "linux/arm64"})
193-
case profile.Image != "":
197+
}
198+
if profile.ImageARM != "" {
199+
imageCandidates = append(imageCandidates, ImageDef{Image: profile.ImageARM, Platform: "linux/arm"})
200+
}
201+
if profile.ImageARMv6 != "" {
202+
imageCandidates = append(imageCandidates, ImageDef{Image: profile.ImageARMv6, Platform: "linux/arm/v6"})
203+
}
204+
if profile.Image386 != "" {
205+
imageCandidates = append(imageCandidates, ImageDef{Image: profile.Image386, Platform: "linux/386"})
206+
}
207+
if profile.Image != "" {
194208
imageCandidates = append(imageCandidates, ImageDef{Image: profile.Image, Platform: "linux/" + profile.Arch})
195-
default:
196-
return images
197209
}
198210

199211
for _, i := range imageCandidates {

0 commit comments

Comments
 (0)