create go program to build arch specific oci image, push it and build generic manifest list layer and push it
This commit is contained in:
parent
642327d8c1
commit
5a540072af
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
golang-multi-arch-builder
|
7
go.mod
Normal file
7
go.mod
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
module golang-multi-arch-builder
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
|
||||||
|
require github.com/sirupsen/logrus v1.8.1
|
||||||
|
|
||||||
|
require golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect
|
10
go.sum
Normal file
10
go.sum
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||||
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
86
image.go
Normal file
86
image.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Image struct {
|
||||||
|
name string
|
||||||
|
tag string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (image Image) String() string {
|
||||||
|
imageName := image.name
|
||||||
|
if image.tag != "" {
|
||||||
|
imageName = fmt.Sprintf("%s:%s", imageName, image.tag)
|
||||||
|
}
|
||||||
|
return imageName
|
||||||
|
}
|
||||||
|
|
||||||
|
type ManifestImage struct {
|
||||||
|
Image
|
||||||
|
layers Layers
|
||||||
|
buildDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewManifest(imageName string, imageTag string, buildDir string, platforms []string) ManifestImage {
|
||||||
|
return ManifestImage{
|
||||||
|
Image: Image{
|
||||||
|
imageName,
|
||||||
|
imageTag,
|
||||||
|
},
|
||||||
|
layers: NewLayers(imageName, platforms),
|
||||||
|
buildDir: buildDir,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateLayers build and push each layer image
|
||||||
|
func (manifest *ManifestImage) CreateLayers(registry Registry, buildOpt string) {
|
||||||
|
for _, layer := range manifest.layers {
|
||||||
|
layer.CreateLayer(registry, manifest.buildDir, buildOpt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateManifest produce manifest list of layers
|
||||||
|
func (manifest *ManifestImage) CreateManifest(registry Registry) {
|
||||||
|
const insecureRegistryOpt = "--insecure"
|
||||||
|
|
||||||
|
// $(OCI_CLI) manifest create $(OCI_OPT) $(REGISTRY_IP):5000/$(1) --amend docker.registry:5000/registry-ui:linux-armv6
|
||||||
|
var amend []string
|
||||||
|
for _, layer := range manifest.layers {
|
||||||
|
amend = append(amend, "--amend")
|
||||||
|
amend = append(amend, fmt.Sprintf("%s/%s", registry.String(), layer.Image.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if stdout, err := runOciCli("docker", "manifest create", insecureRegistryOpt, registry, manifest.Image, amend...); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
} else {
|
||||||
|
log.Debug(stdout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// $(OCI_CLI) manifest annotate --os linux --arch arm --variant v6 $(REGISTRY_IP):5000/$(1) $(REGISTRY_IP):5000/$(1):linux-armv6
|
||||||
|
for _, layer := range manifest.layers {
|
||||||
|
manifest.annotateManifest(registry, layer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// $(OCI_CLI) manifest push $(OCI_OPT) $(REGISTRY_IP):5000/$(1)
|
||||||
|
if stdout, err := runOciCli("docker", "manifest push", insecureRegistryOpt, registry, manifest.Image); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
} else {
|
||||||
|
log.Debug(stdout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manifest *ManifestImage) annotateManifest(registry Registry, layer Layer) {
|
||||||
|
stdout, err := runOciCli("docker", "manifest annotate",
|
||||||
|
fmt.Sprintf("--os %s --arch %s --variant %s", layer.os, layer.arch, layer.variant),
|
||||||
|
registry, manifest.Image,
|
||||||
|
fmt.Sprintf("%s/%s", registry.String(), layer.Image.String()),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("layer %s not annotated", layer.String())
|
||||||
|
} else {
|
||||||
|
log.Debug(stdout)
|
||||||
|
}
|
||||||
|
}
|
75
layers.go
Normal file
75
layers.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Layer struct {
|
||||||
|
Image
|
||||||
|
os string
|
||||||
|
arch string
|
||||||
|
variant string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (layer *Layer) String() string {
|
||||||
|
return fmt.Sprintf("%s : os %s, arch %s, variant %s", layer.Image.String(), layer.os, layer.arch, layer.variant)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (layer *Layer) CreateLayer(registry Registry, buildDir string, buildOpt string) {
|
||||||
|
if buildOpt != "" {
|
||||||
|
buildOpt = fmt.Sprintf("--platform %s/%s/%s %s -t", layer.os, layer.arch, layer.variant, buildOpt)
|
||||||
|
} else {
|
||||||
|
buildOpt = fmt.Sprintf("--platform %s/%s/%s -t", layer.os, layer.arch, layer.variant)
|
||||||
|
}
|
||||||
|
|
||||||
|
// $(OCI_CLI_BUILD) build --platform $(2) -t $(REGISTRY_IP):5000/$(1):$(3) .
|
||||||
|
if stdout, err := runOciCli("docker", "build", buildOpt, registry, layer.Image, buildDir); err != nil {
|
||||||
|
log.Fatalf("layer build step failed : %v\n", err)
|
||||||
|
} else {
|
||||||
|
log.Debug(stdout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// $(OCI_CLI) push $(REGISTRY_IP):5000/$(1):$(3)
|
||||||
|
if stdout, err := runOciCli("docker", "push", "", registry, layer.Image); err != nil {
|
||||||
|
log.Fatalf("layer push step failed : %v\n", err)
|
||||||
|
} else {
|
||||||
|
log.Debug(stdout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Layers []Layer
|
||||||
|
|
||||||
|
func NewLayers(imageName string, platforms []string) Layers {
|
||||||
|
var layers Layers
|
||||||
|
for _, platform := range platforms {
|
||||||
|
splitPlatform := strings.Split(platform, "/")
|
||||||
|
|
||||||
|
var layer Layer
|
||||||
|
if len(splitPlatform) < 3 {
|
||||||
|
layer = Layer{
|
||||||
|
Image: Image{
|
||||||
|
imageName,
|
||||||
|
fmt.Sprintf("%s-%s", splitPlatform[0], splitPlatform[1]),
|
||||||
|
},
|
||||||
|
os: splitPlatform[0],
|
||||||
|
arch: splitPlatform[1],
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
layer = Layer{
|
||||||
|
Image: Image{
|
||||||
|
imageName,
|
||||||
|
fmt.Sprintf("%s-%s%s", splitPlatform[0], splitPlatform[1], splitPlatform[2]),
|
||||||
|
},
|
||||||
|
os: splitPlatform[0],
|
||||||
|
arch: splitPlatform[1],
|
||||||
|
variant: splitPlatform[2],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
layers = append(layers, layer)
|
||||||
|
}
|
||||||
|
|
||||||
|
return layers
|
||||||
|
}
|
33
main.go
Normal file
33
main.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// init logging parameter
|
||||||
|
func init() {
|
||||||
|
log.SetLevel(log.DebugLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
myRegistry := Registry{
|
||||||
|
"docker.registry",
|
||||||
|
5000,
|
||||||
|
}
|
||||||
|
|
||||||
|
var manifests []ManifestImage
|
||||||
|
|
||||||
|
manifests = append(manifests,
|
||||||
|
NewManifest(
|
||||||
|
"registry-ui",
|
||||||
|
"latest",
|
||||||
|
"../rasp/registry/ui/",
|
||||||
|
[]string{"linux/arm/v6"},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, manifest := range manifests {
|
||||||
|
manifest.CreateLayers(myRegistry, "")
|
||||||
|
manifest.CreateManifest(myRegistry)
|
||||||
|
}
|
||||||
|
}
|
15
registry.go
Normal file
15
registry.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Registry struct {
|
||||||
|
hostname string
|
||||||
|
port int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (registry Registry) String() string {
|
||||||
|
if registry.port != 0 {
|
||||||
|
return fmt.Sprintf("%s:%d", registry.hostname, registry.port)
|
||||||
|
}
|
||||||
|
return registry.hostname
|
||||||
|
}
|
38
runner.go
Normal file
38
runner.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// runOciCli execute binary formatted like this `{ociCli} {ociVerb} {ociOpt} {registry:port/image} {args}`
|
||||||
|
func runOciCli(ociCli string, ociVerb string, ociCliOpt string, registry Registry, image Image, args ...string) (string, error) {
|
||||||
|
if reg := registry.String(); reg != "" {
|
||||||
|
args = append([]string{fmt.Sprintf("%s/%s", registry.String(), image.String())}, args...)
|
||||||
|
} else {
|
||||||
|
args = append([]string{image.String()}, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ociCliOpt != "" {
|
||||||
|
args = append(strings.Split(ociCliOpt, " "), args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return runCli(ociCli, ociVerb, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// runCli execute binary formatted like this `{bin} {verb} {args}`
|
||||||
|
func runCli(binary string, verb string, args ...string) (string, error) {
|
||||||
|
if verb != "" {
|
||||||
|
args = append(strings.Split(verb, " "), args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf(" $ %s %s\n", binary, strings.Join(args, " "))
|
||||||
|
out, err := exec.Command(binary, args...).Output()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Executing binary with argument : `%s %s %s` failed\n", binary, verb, strings.Join(args, " "))
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(out), nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user