mirror of
https://github.com/mudler/luet.git
synced 2025-06-30 17:22:38 +00:00
156 lines
3.1 KiB
Go
156 lines
3.1 KiB
Go
package api
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/heroku/docker-registry-client/registry"
|
|
jww "github.com/spf13/jwalterweatherman"
|
|
)
|
|
|
|
const defaultRegistryBase = "https://registry-1.docker.io"
|
|
|
|
type DownloadOpts struct {
|
|
RegistryBase string
|
|
KeepLayers bool
|
|
UnpackMode string
|
|
}
|
|
|
|
func DownloadAndUnpackImage(sourceImage, output string, opts *DownloadOpts) error {
|
|
|
|
if opts.RegistryBase == "" {
|
|
opts.RegistryBase = defaultRegistryBase
|
|
}
|
|
|
|
var TempDir = os.Getenv("TEMP_LAYER_FOLDER")
|
|
if TempDir == "" {
|
|
TempDir = "layers"
|
|
}
|
|
err := os.MkdirAll(TempDir, os.ModePerm)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if opts.KeepLayers == false {
|
|
defer os.RemoveAll(TempDir)
|
|
}
|
|
|
|
if sourceImage != "" && strings.Contains(sourceImage, ":") {
|
|
parts := strings.Split(sourceImage, ":")
|
|
if parts[0] == "" || parts[1] == "" {
|
|
return fmt.Errorf("Bad usage. Image should be in this format: foo/my-image:latest")
|
|
}
|
|
}
|
|
|
|
tagPart := "latest"
|
|
repoPart := sourceImage
|
|
parts := strings.Split(sourceImage, ":")
|
|
if len(parts) > 1 {
|
|
repoPart = parts[0]
|
|
tagPart = parts[1]
|
|
}
|
|
|
|
jww.INFO.Println("Unpacking", repoPart, "tag", tagPart, "in", output)
|
|
os.MkdirAll(output, os.ModePerm)
|
|
username := "" // anonymous
|
|
password := "" // anonymous
|
|
hub, err := registry.New(opts.RegistryBase, username, password)
|
|
if err != nil {
|
|
jww.ERROR.Fatalln(err)
|
|
return err
|
|
}
|
|
manifest, err := hub.Manifest(repoPart, tagPart)
|
|
if err != nil {
|
|
jww.ERROR.Fatalln(err)
|
|
return err
|
|
}
|
|
layers := manifest.FSLayers
|
|
layers_sha := make([]string, 0)
|
|
for _, l := range layers {
|
|
jww.INFO.Println("Layer ", l)
|
|
// or obtain the digest from an existing manifest's FSLayer list
|
|
s := string(l.BlobSum)
|
|
i := strings.Index(s, ":")
|
|
enc := s[i+1:]
|
|
reader, err := hub.DownloadBlob(repoPart, l.BlobSum)
|
|
layers_sha = append(layers_sha, enc)
|
|
|
|
if reader != nil {
|
|
defer reader.Close()
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
where := path.Join(TempDir, enc)
|
|
err = os.MkdirAll(where, os.ModePerm)
|
|
if err != nil {
|
|
jww.ERROR.Println(err)
|
|
return err
|
|
}
|
|
|
|
out, err := os.Create(path.Join(where, "layer.tar"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer out.Close()
|
|
if _, err := io.Copy(out, reader); err != nil {
|
|
fmt.Println(err)
|
|
return err
|
|
}
|
|
}
|
|
|
|
jww.INFO.Println("Download complete")
|
|
|
|
export, err := CreateExport(TempDir)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return err
|
|
}
|
|
|
|
jww.INFO.Println("Unpacking...")
|
|
|
|
err = export.UnPackLayers(layers_sha, output, opts.UnpackMode)
|
|
if err != nil {
|
|
jww.INFO.Fatal(err)
|
|
return err
|
|
}
|
|
|
|
jww.INFO.Println("Done")
|
|
return nil
|
|
}
|
|
|
|
func CreateExport(layers string) (*Export, error) {
|
|
|
|
export := &Export{
|
|
Entries: map[string]*ExportedImage{},
|
|
Path: layers,
|
|
}
|
|
|
|
dirs, err := ioutil.ReadDir(export.Path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, dir := range dirs {
|
|
|
|
if !dir.IsDir() {
|
|
continue
|
|
}
|
|
|
|
entry := &ExportedImage{
|
|
Path: filepath.Join(export.Path, dir.Name()),
|
|
LayerTarPath: filepath.Join(export.Path, dir.Name(), "layer.tar"),
|
|
LayerDirPath: filepath.Join(export.Path, dir.Name(), "layer"),
|
|
}
|
|
|
|
export.Entries[dir.Name()] = entry
|
|
}
|
|
|
|
return export, err
|
|
}
|