Files
Olares/cli/pkg/images/load.go

247 lines
6.8 KiB
Go

package images
import (
"fmt"
"github.com/distribution/reference"
"os"
"path/filepath"
"strings"
"time"
"github.com/beclab/Olares/cli/pkg/common"
"github.com/beclab/Olares/cli/pkg/core/cache"
"github.com/beclab/Olares/cli/pkg/core/connector"
"github.com/beclab/Olares/cli/pkg/core/logger"
"github.com/beclab/Olares/cli/pkg/manifest"
"github.com/beclab/Olares/cli/pkg/utils"
"github.com/containerd/containerd/pkg/cri/labels"
)
const MAX_IMPORT_RETRY int = 5
type LoadImages struct {
common.KubeAction
manifest.ManifestAction
}
func (t *LoadImages) Execute(runtime connector.Runtime) (reserr error) {
var minikubepath = getMinikubePath(t.PipelineCache)
var minikubeprofile = t.KubeConf.Arg.MinikubeProfile
var containerManager = t.KubeConf.Cluster.Kubernetes.ContainerManager
var host = runtime.RemoteHost()
imageManifests, manifests := t.Manifest.GetImageList()
retry := func(f func() error, times int) (err error) {
for i := 0; i < times; i++ {
err = f()
if err == nil {
return nil
}
var dur = 5 + (i+1)*10
// fmt.Printf("import %s failed, wait for %d seconds(%d times)\n", err, dur, i+1)
logger.Errorf("import error %v, wait for %d seconds(%d times)", err, dur, i+1)
if (i + 1) < times {
time.Sleep(time.Duration(dur) * time.Second)
}
}
return
}
var mf = filterMinikubeImages(runtime.GetRunner(), host.GetOs(), minikubepath, manifests, minikubeprofile)
var missingImages []string
for _, imageRepoTag := range mf {
if imageRepoTag == "" {
continue
}
reserr = nil
if inspectImage(runtime.GetRunner(), containerManager, imageRepoTag) == nil {
logger.Infof("%s already exists", imageRepoTag)
continue
}
missingImages = append(missingImages, imageRepoTag)
}
for index, imageRepoTag := range missingImages {
var start = time.Now()
var imageHashTag = utils.MD5(imageRepoTag)
var imageFileName string
imagesDir := filepath.Join(t.BaseDir, imageManifests[index].Path)
var found = false
filepath.Walk(imagesDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
if !strings.HasPrefix(info.Name(), imageHashTag) ||
!HasSuffixI(info.Name(), ".tar.gz", ".tgz", ".tar") {
return nil
}
if strings.HasPrefix(info.Name(), imageHashTag) {
found = true
imageFileName = path
return filepath.SkipDir
}
return nil
})
if !found {
return fmt.Errorf("image %s not found in %s", imageRepoTag, imagesDir)
}
var imgFileName = filepath.Base(imageFileName)
var loadCmd string
var loadParm string
// unused
// if runtime.GetSystemInfo().GetFsType() == "zfs" {
// loadParm = "--snapshotter=zfs"
// }
if t.KubeConf.Arg.IsOlaresInContainer {
loadParm = "--no-unpack"
}
parsedRef, err := reference.ParseNormalizedNamed(imageRepoTag)
if err != nil {
logger.Warnf("parse image name %s error: %v, skip importing", imageRepoTag, err)
continue
}
if runtime.RemoteHost().GetOs() == common.Darwin {
loadCmd = fmt.Sprintf("%s -p %s ssh --native-ssh=false 'sudo ctr -n k8s.io i import --index-name %s -'", minikubepath, minikubeprofile, parsedRef)
if HasSuffixI(imgFileName, ".tar.gz", ".tgz") {
loadCmd = fmt.Sprintf("gunzip -c %s | ", imageFileName) + loadCmd
} else {
loadCmd = fmt.Sprintf("cat %s | ", imageFileName) + loadCmd
}
} else {
switch containerManager {
case "crio":
loadCmd = "ctr" // not implement
case "containerd":
if HasSuffixI(imgFileName, ".tar.gz", ".tgz") {
loadCmd = fmt.Sprintf("gunzip -c %s | ctr -n k8s.io images import --index-name %s %s -", imageFileName, parsedRef, loadParm)
} else {
loadCmd = fmt.Sprintf("ctr -n k8s.io images import %s %s", imageFileName, loadParm)
}
case "isula":
loadCmd = "isula" // not implement
default:
}
}
if err := retry(func() error {
if _, err := runtime.GetRunner().SudoCmd(loadCmd, false, false); err != nil {
return fmt.Errorf("%s(%s) error: %v", imageRepoTag, imgFileName, err)
} else {
logger.Infof("(%d/%d) imported image: %s, time: %s", index+1, len(missingImages), imageRepoTag, time.Since(start))
}
return nil
}, MAX_IMPORT_RETRY); err != nil {
reserr = fmt.Errorf("%s(%s) error: %v", imageRepoTag, imgFileName, err)
break
}
}
return
}
type PinImages struct {
common.KubeAction
manifest.ManifestAction
}
func (a *PinImages) Execute(runtime connector.Runtime) error {
_, manifests := a.Manifest.GetImageList()
if !runtime.GetSystemInfo().IsLinux() {
return nil
}
for _, ref := range manifests {
parsedRef, err := reference.ParseNormalizedNamed(ref)
if err != nil {
logger.Warnf("parse image name %s error: %v, skip pinning", ref, err)
continue
}
if _, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("ctr -n k8s.io i label %s %s=%s", parsedRef.String(), labels.PinnedImageLabelKey, labels.PinnedImageLabelValue), false, false); err != nil {
if strings.Contains(err.Error(), "DEPRECATION") {
continue
}
// tolerate cases where some images are not found
// e.g., like in the cloud environment and some images are not in the ami
logger.Warnf("pin image %s error: %v", parsedRef.String(), err)
}
}
return nil
}
func filterMinikubeImages(runner *connector.Runner, osType string, minikubepath string, imagesManifest []string, minikubeProfile string) []string {
if !strings.EqualFold(osType, common.Darwin) {
return imagesManifest
}
stdout, err := runner.Host.SudoCmd(fmt.Sprintf("%s -p %s image ls", minikubepath, minikubeProfile), false, false)
if err != nil {
return imagesManifest
}
injectedImages := strings.Split(stdout, "\n")
if injectedImages == nil || len(injectedImages) == 0 {
return imagesManifest
}
injectedImagesMap := make(map[string]string)
for _, injected := range injectedImages {
injectedImagesMap[injected] = injected
}
var mf []string
for _, im := range imagesManifest {
if _, ok := injectedImagesMap[im]; ok {
continue
}
mf = append(mf, im)
}
return mf
}
func getMinikubePath(pipelineCache *cache.Cache) string {
minikubepath, _ := pipelineCache.GetMustString(common.CacheCommandMinikubePath)
if minikubepath == "" {
minikubepath = common.CommandMinikube
}
return minikubepath
}
func inspectImage(runner *connector.Runner, containerManager, imageRepoTag string) error {
if runner.Host.GetOs() == common.Darwin {
return fmt.Errorf("skip inspect")
}
var inspectCmd string = "docker image inspect %s"
if runner.Host.GetOs() != common.Darwin {
switch containerManager {
case "crio": // not implement
inspectCmd = "ctr"
case "containerd":
inspectCmd = "crictl inspecti -q %s"
case "isula": // not implement
inspectCmd = "isula"
default:
}
}
var cmd = fmt.Sprintf(inspectCmd, imageRepoTag)
if _, err := runner.Host.SudoCmd(cmd, false, false); err != nil {
return fmt.Errorf("inspect %s error %v", imageRepoTag, err)
}
return nil
}