247 lines
6.8 KiB
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
|
|
}
|