From be814f6f797a03cbc3f0c6a5bac688ca16831ddc Mon Sep 17 00:00:00 2001 From: Angelos Kolaitis Date: Mon, 17 Jun 2024 19:17:37 +0300 Subject: [PATCH] Use a containerd plugin for side-loading images (#502) * containerd sideload images * add regctl under tools/ --- .../0001-side-load-images-plugin.patch | 167 ++++++++++++++++++ src/k8s/tools/go.mod | 1 + src/k8s/tools/go.sum | 2 + src/k8s/tools/regctl.sh | 8 + src/k8s/tools/tools.go | 1 + 5 files changed, 179 insertions(+) create mode 100644 build-scripts/components/containerd/patches/default/0001-side-load-images-plugin.patch create mode 100755 src/k8s/tools/regctl.sh diff --git a/build-scripts/components/containerd/patches/default/0001-side-load-images-plugin.patch b/build-scripts/components/containerd/patches/default/0001-side-load-images-plugin.patch new file mode 100644 index 000000000..80fbf43ee --- /dev/null +++ b/build-scripts/components/containerd/patches/default/0001-side-load-images-plugin.patch @@ -0,0 +1,167 @@ +From d6a1e25e517a981b90bb6e0df66b63c72bc61f45 Mon Sep 17 00:00:00 2001 +From: Angelos Kolaitis +Date: Sun, 16 Jun 2024 16:58:03 +0300 +Subject: [PATCH] side-load images plugin + +--- + cmd/containerd/builtins_custom.go | 6 ++ + custom_plugins/sideload.go | 134 ++++++++++++++++++++++++++++++ + 2 files changed, 140 insertions(+) + create mode 100644 cmd/containerd/builtins_custom.go + create mode 100644 custom_plugins/sideload.go + +diff --git a/cmd/containerd/builtins_custom.go b/cmd/containerd/builtins_custom.go +new file mode 100644 +index 000000000..581e42364 +--- /dev/null ++++ b/cmd/containerd/builtins_custom.go +@@ -0,0 +1,6 @@ ++package main ++ ++// register custom containerd plugins here ++import ( ++ _ "github.com/containerd/containerd/custom_plugins" ++) +diff --git a/custom_plugins/sideload.go b/custom_plugins/sideload.go +new file mode 100644 +index 000000000..4f2680a32 +--- /dev/null ++++ b/custom_plugins/sideload.go +@@ -0,0 +1,134 @@ ++package custom_plugins ++ ++import ( ++ "fmt" ++ "os" ++ "path/filepath" ++ "time" ++ ++ "github.com/containerd/containerd" ++ "github.com/containerd/containerd/log" ++ "github.com/containerd/containerd/platforms" ++ "github.com/containerd/containerd/plugin" ++) ++ ++const pluginName = "sideload-images" ++ ++var logger = log.L.WithField("plugin", pluginName) ++ ++type Config struct { ++ // Disabled is a toggle option to completely disable this plugin. ++ Disabled bool `toml:"enabled"` ++ ++ // Interval configures how frequently the plugin will look for new images found ++ // in the sources. If set to zero, images are only loaded during initial start. ++ Interval time.Duration `toml:"interval"` ++ ++ // Sources is a list of paths to look for .tar images. ++ // For example, `/var/snap/k8s/common/images` ++ Sources []string `toml:"sources"` ++ ++ // Namespace the images will be loaded into, e.g. "k8s.io" ++ Namespace string `toml:"namespace"` ++} ++ ++func (c *Config) SetDefaults() { ++ if c.Namespace == "" { ++ c.Namespace = "k8s.io" ++ } ++ if len(c.Sources) == 0 { ++ snapCommon := os.Getenv("SNAP_COMMON") ++ if snapCommon == "" { ++ snapCommon = "/var/snap/k8s/common" ++ } ++ c.Sources = []string{filepath.Join(snapCommon, "images")} ++ } ++} ++ ++func init() { ++ c := &Config{} ++ plugin.Register(&plugin.Registration{ ++ Type: plugin.ServicePlugin, ++ ID: pluginName, ++ Config: c, ++ InitFn: func(ic *plugin.InitContext) (interface{}, error) { ++ config := ic.Config.(*Config) ++ config.SetDefaults() ++ ++ logger.Debugf("Loaded config %#v", config) ++ ++ if config.Disabled { ++ return nil, fmt.Errorf("plugin disabled through config: %w", plugin.ErrSkipPlugin) ++ } ++ if len(config.Sources) == 0 { ++ return nil, fmt.Errorf("no sources configured: %w", plugin.ErrSkipPlugin) ++ } ++ ++ go func() { ++ // get a containerd client ++ var ( ++ cl *containerd.Client ++ err error ++ ) ++ for cl == nil { ++ select { ++ case <-ic.Context.Done(): ++ return ++ default: ++ } ++ ++ cl, err = containerd.New(ic.Address, containerd.WithDefaultNamespace(config.Namespace), containerd.WithTimeout(2*time.Second)) ++ if err != nil { ++ logger.Info("Failed to create containerd client") ++ } ++ } ++ ++ for { ++ nextDir: ++ for _, dir := range c.Sources { ++ logger := logger.WithField("dir", dir) ++ logger.Debug("Looking for images") ++ files, err := filepath.Glob(filepath.Join(dir, "*.tar")) ++ if err != nil { ++ logger.WithError(err).Warn("Failed to look for images") ++ continue nextDir ++ } ++ ++ nextFile: ++ for _, file := range files { ++ logger := logger.WithField("file", file) ++ r, err := os.Open(file) ++ if err != nil { ++ logger.WithError(err).Warn("Failed to open file") ++ continue nextFile ++ } ++ images, err := cl.Import(ic.Context, r, containerd.WithImportPlatform(platforms.Default())) ++ if err != nil { ++ logger.WithError(err).Error("Failed to import images") ++ } else { ++ logger.Infof("Imported %d images", len(images)) ++ os.Rename(file, file+".loaded") ++ } ++ if closeErr := r.Close(); closeErr != nil { ++ logger.WithError(closeErr).Error("Failed to close reader") ++ } ++ } ++ } ++ ++ // retry after interval, finish if interval is zero ++ if c.Interval == 0 { ++ logger.Info("Plugin terminating") ++ return ++ } ++ select { ++ case <-ic.Context.Done(): ++ return ++ case <-time.After(c.Interval): ++ } ++ } ++ }() ++ ++ return nil, nil ++ }, ++ }) ++} +-- +2.34.1 + diff --git a/src/k8s/tools/go.mod b/src/k8s/tools/go.mod index 02a4e8ebf..be2ee60cc 100644 --- a/src/k8s/tools/go.mod +++ b/src/k8s/tools/go.mod @@ -15,5 +15,6 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/ulikunitz/xz v0.5.12 // indirect golang.org/x/sys v0.20.0 // indirect + golang.org/x/term v0.20.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/src/k8s/tools/go.sum b/src/k8s/tools/go.sum index 42e110ebf..0f645d6c1 100644 --- a/src/k8s/tools/go.sum +++ b/src/k8s/tools/go.sum @@ -33,6 +33,8 @@ github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0o golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/src/k8s/tools/regctl.sh b/src/k8s/tools/regctl.sh new file mode 100755 index 000000000..a07b415d1 --- /dev/null +++ b/src/k8s/tools/regctl.sh @@ -0,0 +1,8 @@ +#!/bin/bash -eu + +# Run regctl +TOOLS_DIR="$(realpath `dirname "${0}"`)" +( + cd "${TOOLS_DIR}" + go run github.com/regclient/regclient/cmd/regctl "${@}" +) diff --git a/src/k8s/tools/tools.go b/src/k8s/tools/tools.go index 1c97d3164..3d7605906 100644 --- a/src/k8s/tools/tools.go +++ b/src/k8s/tools/tools.go @@ -1,5 +1,6 @@ package main import ( + _ "github.com/regclient/regclient/cmd/regctl" _ "github.com/regclient/regclient/cmd/regsync" )