system-agent/pkg/image/util.go

140 lines
4.3 KiB
Go

package image
import (
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/rancher/wharfie/pkg/credentialprovider/plugin"
"github.com/rancher/wharfie/pkg/registries"
"github.com/rancher/wharfie/pkg/tarfile"
"github.com/sirupsen/logrus"
)
const (
baseRancherDir string = "/var/lib/rancher/"
defaultImagesDir = baseRancherDir + "agent/images"
defaultImageCredentialProviderConfig = baseRancherDir + "credentialprovider/config.yaml"
defaultImageCredentialProviderBinDir = baseRancherDir + "credentialprovider/bin"
defaultAgentRegistriesFile string = "/etc/rancher/agent/registries.yaml"
rke2RegistriesFile string = "/etc/rancher/rke2/registries.yaml"
k3sRegistriesFile string = "/etc/rancher/k3s/registries.yaml"
)
type Utility struct {
imagesDir string
imageCredentialProviderConfig string
imageCredentialProviderBinDir string
agentRegistriesFile string
}
func NewUtility(imagesDir, imageCredentialProviderConfig, imageCredentialProviderBinDir, agentRegistriesFile string) *Utility {
var u Utility
if imagesDir != "" {
u.imagesDir = imagesDir
} else {
u.imagesDir = defaultImagesDir
}
if imageCredentialProviderConfig != "" {
u.imageCredentialProviderConfig = imageCredentialProviderConfig
} else {
u.imageCredentialProviderConfig = defaultImageCredentialProviderConfig
}
if imageCredentialProviderBinDir != "" {
u.imageCredentialProviderBinDir = imageCredentialProviderBinDir
} else {
u.imageCredentialProviderBinDir = defaultImageCredentialProviderBinDir
}
if agentRegistriesFile != "" {
u.agentRegistriesFile = agentRegistriesFile
} else {
u.agentRegistriesFile = defaultAgentRegistriesFile
}
logrus.Debugf("Instantiated new image utility with imagesDir: %s, imageCredentialProviderConfig: %s, imageCredentialProviderBinDir: %s, agentRegistriesFile: %s", u.imagesDir, u.imageCredentialProviderConfig, u.imageCredentialProviderBinDir, u.agentRegistriesFile)
return &u
}
func (u *Utility) Stage(destDir string, imgString string) error {
if err := os.MkdirAll(destDir, 0755); err != nil {
return err
}
var img v1.Image
image, err := name.ParseReference(imgString)
if err != nil {
return err
}
imagesDir, err := filepath.Abs(u.imagesDir)
if err != nil {
return err
}
i, err := tarfile.FindImage(imagesDir, image)
if err != nil && !errors.Is(err, tarfile.ErrNotFound) {
return err
}
img = i
if img == nil {
registry, err := registries.GetPrivateRegistries(u.findRegistriesYaml())
if err != nil {
return err
}
if _, err := os.Stat(u.imageCredentialProviderConfig); os.IsExist(err) {
logrus.Debugf("Image Credential Provider Configuration file %s existed, using plugins from directory %s", u.imageCredentialProviderConfig, u.imageCredentialProviderBinDir)
plugins, err := plugin.RegisterCredentialProviderPlugins(u.imageCredentialProviderConfig, u.imageCredentialProviderBinDir)
if err != nil {
return err
}
registry.DefaultKeychain = plugins
} else {
// The kubelet image credential provider plugin also falls back to checking legacy Docker credentials, so only
// explicitly set up the go-containerregistry DefaultKeychain if plugins are not configured.
// DefaultKeychain tries to read config from the home dir, and will error if HOME isn't set, so also gate on that.
if os.Getenv("HOME") != "" {
registry.DefaultKeychain = authn.DefaultKeychain
}
}
logrus.Infof("Pulling image %s", image.Name())
img, err = registry.Image(image,
remote.WithPlatform(v1.Platform{
Architecture: runtime.GOARCH,
OS: runtime.GOOS,
}),
)
if err != nil {
return fmt.Errorf("%v: failed to get image %s", err, image.Name())
}
}
return extractFiles(img, destDir)
}
func (u *Utility) findRegistriesYaml() string {
if _, err := os.Stat(u.agentRegistriesFile); err == nil {
return u.agentRegistriesFile
}
if _, err := os.Stat(rke2RegistriesFile); err == nil {
return rke2RegistriesFile
}
if _, err := os.Stat(k3sRegistriesFile); err == nil {
return k3sRegistriesFile
}
return ""
}