457 lines
16 KiB
Go
457 lines
16 KiB
Go
package main
|
||
|
||
import (
|
||
"bufio"
|
||
"errors"
|
||
"fmt"
|
||
"io/fs"
|
||
"os"
|
||
"path/filepath"
|
||
"slices"
|
||
"strings"
|
||
|
||
image2 "wdd.io/agent-common/image"
|
||
"wdd.io/agent-common/utils"
|
||
"wdd.io/agent-operator/image"
|
||
)
|
||
|
||
const OfflineDeployHarborHost = "harbor.wdd.io"
|
||
const PublicDeployHarborHost = "42.192.52.227"
|
||
const DirectPushDeployHarborHost = "36.134.71.138"
|
||
|
||
type ImageSyncEntity struct {
|
||
ProjectName string // 优先级3
|
||
ProjectFromDemoNameNotNull bool
|
||
ProjectVersion string // 优先级2
|
||
CmiiNameTagList []string // 优先级1 appName:tag的形式
|
||
FullNameImageList []string // 优先级1
|
||
DownloadFromOss bool // 下载镜像
|
||
CompressImageToGzip bool // 压缩镜像
|
||
UploadToDemoMinio bool // 上传镜像
|
||
ShouldDirectPushToHarbor bool // 直接推送到对方的主机 || 离线部署机 使用此部门
|
||
DirectHarborHost string // 目标Harbor仓库的Host,全名称带端口 不带http前缀
|
||
}
|
||
|
||
type ImageSyncResult struct {
|
||
ErrorPullImageList []string
|
||
ErrorGzipImageList []string
|
||
ErrorPushImageNameList []string
|
||
RealImageNameList []string
|
||
RealGzipFileNameList []string
|
||
AllCmiiImageNameList []string
|
||
}
|
||
|
||
// PullFromEntityAndSyncConditionally 根据ImageSyncEntity拉取特定的镜像,然后上传到特定的目标机器(或者上传的minio中)
|
||
func (sync ImageSyncEntity) PullFromEntityAndSyncConditionally() (imageSyncResult ImageSyncResult) {
|
||
|
||
var realCmiiImageList []string
|
||
var allCmiiImageNameList []string
|
||
|
||
var errorPullImageList []string
|
||
var allGzipFileNameList []string
|
||
var errorGzipImageList []string
|
||
var errorPushImageNameList []string
|
||
var gzipFolderFullPath string
|
||
|
||
if (sync.CmiiNameTagList == nil && sync.FullNameImageList == nil) || (len(sync.CmiiNameTagList) == 0 && len(sync.FullNameImageList) == 0) {
|
||
// 没有指定特定的镜像,那么根据 ProjectVersion 或者从DEMO拉取镜像
|
||
// pull images
|
||
// compress
|
||
if sync.ProjectVersion != "" {
|
||
|
||
// get version images
|
||
errorPullImageList, errorGzipImageList, allCmiiImageNameList, allGzipFileNameList = DownloadCompressUploadFromVersion(sync.ProjectVersion, sync.CompressImageToGzip, sync.UploadToDemoMinio)
|
||
|
||
gzipFolderFullPath = image.OfflineImageGzipFolderPrefix + sync.ProjectVersion
|
||
|
||
} else {
|
||
// get demo images
|
||
|
||
errorPullImageList, errorGzipImageList, allCmiiImageNameList, allGzipFileNameList = DownloadCompressUploadFromDemo(sync.ProjectName, sync.CompressImageToGzip, sync.UploadToDemoMinio)
|
||
gzipFolderFullPath = image.OfflineImageGzipFolderPrefix + sync.ProjectName
|
||
|
||
}
|
||
} else {
|
||
// 拉取特定的镜像
|
||
|
||
gzipFolderFullPath = image.OfflineImageGzipFolderPrefix + "tmp"
|
||
// 组装镜像名称
|
||
allCmiiImageNameList = concatAndUniformCmiiImage(sync.FullNameImageList, sync.CmiiNameTagList)
|
||
|
||
// DCU
|
||
errorPullImageList, errorGzipImageList, realCmiiImageList, allGzipFileNameList = DownloadCompressUpload(allCmiiImageNameList, sync.CompressImageToGzip, gzipFolderFullPath, sync.UploadToDemoMinio)
|
||
}
|
||
|
||
// 直接传输到目标Harbor仓库
|
||
if sync.ShouldDirectPushToHarbor {
|
||
if sync.DirectHarborHost == "" {
|
||
log.ErrorF("DirectHarborHost is null ! can't push to target harbor !")
|
||
}
|
||
// push to
|
||
errorPushImageNameList = image.TagFromListAndPushToCHarbor(realCmiiImageList, sync.DirectHarborHost)
|
||
}
|
||
|
||
// build result
|
||
imageSyncResult.AllCmiiImageNameList = allCmiiImageNameList
|
||
|
||
imageSyncResult.RealImageNameList = realCmiiImageList
|
||
imageSyncResult.ErrorPullImageList = errorPullImageList
|
||
|
||
imageSyncResult.RealGzipFileNameList = allGzipFileNameList
|
||
imageSyncResult.ErrorGzipImageList = errorGzipImageList
|
||
|
||
imageSyncResult.ErrorPushImageNameList = errorPushImageNameList
|
||
|
||
return imageSyncResult
|
||
}
|
||
|
||
func concatAndUniformCmiiImage(fullImageList []string, cmiiImageList []string) []string {
|
||
|
||
if cmiiImageList != nil || len(cmiiImageList) > 0 {
|
||
// cmiiImageList has content
|
||
if fullImageList == nil {
|
||
fullImageList = []string{}
|
||
}
|
||
|
||
for _, cmiiImage := range cmiiImageList {
|
||
fullImageList = append(fullImageList, image2.CmiiHarborPrefix+cmiiImage)
|
||
}
|
||
}
|
||
|
||
return fullImageList
|
||
}
|
||
|
||
// DownloadCompressUpload DCU 镜像同步的前半部分,通常在35.71 LapPro执行,无需Bastion Mode
|
||
func DownloadCompressUpload(fullNameList []string, shouldGzip bool, gzipFolderFullPath string, shouldOss bool) (errorPullImageList, errorGzipImageList, realCmiiImageName, allGzipFileNameList []string) {
|
||
|
||
// Download
|
||
log.Info("DOWNLOAD START !")
|
||
errorPullImageList = image.PullFromFullNameList(fullNameList)
|
||
// remove failed
|
||
fullNameList = slices.DeleteFunc(fullNameList, func(imageName string) bool {
|
||
return slices.Contains(errorPullImageList, imageName)
|
||
})
|
||
|
||
// Compress
|
||
if shouldGzip {
|
||
// mkdir folder
|
||
err := os.MkdirAll(gzipFolderFullPath, os.ModeDir)
|
||
if err != nil {
|
||
if !errors.Is(err, os.ErrExist) {
|
||
log.ErrorF("create folder error of %s", gzipFolderFullPath)
|
||
panic(err)
|
||
}
|
||
}
|
||
|
||
// 循环遍历压缩
|
||
log.Info("COMPRESS START")
|
||
|
||
for _, imageFullName := range fullNameList {
|
||
if !image.SaveToTarGZ(imageFullName, gzipFolderFullPath) {
|
||
errorGzipImageList = append(errorGzipImageList, imageFullName)
|
||
}
|
||
}
|
||
// remove failed
|
||
fullNameList = slices.DeleteFunc(fullNameList, func(imageName string) bool {
|
||
return slices.Contains(errorGzipImageList, imageName)
|
||
})
|
||
}
|
||
|
||
// Upload
|
||
if shouldOss {
|
||
//uploadGzipFileToDemoMinio()
|
||
// get gzip file name list
|
||
log.Info("UPLOAD OSS START !")
|
||
err := filepath.WalkDir(gzipFolderFullPath, func(path string, d fs.DirEntry, err error) error {
|
||
if err != nil {
|
||
log.ErrorF("error getting gzip file name list 1! %s", err.Error())
|
||
}
|
||
if !d.IsDir() {
|
||
allGzipFileNameList = append(allGzipFileNameList, d.Name())
|
||
}
|
||
return nil
|
||
})
|
||
if err != nil {
|
||
log.ErrorF("error getting gzip file name list 2! %s", err.Error())
|
||
}
|
||
|
||
// start to upload
|
||
// extract demo oss location suffix from gzipFolderFullPath
|
||
trimPrefix := strings.TrimPrefix(gzipFolderFullPath, image.OfflineImageGzipFolderPrefix)
|
||
bucketName := "cmlc-installation/" + trimPrefix
|
||
log.InfoF("gzip file location in demo oss is %s", DefaultDemoEndpoint+"/"+bucketName)
|
||
|
||
for _, gzipFileName := range allGzipFileNameList {
|
||
|
||
if !DefaultCmiiMinioOperator.UploadToDemo(bucketName, gzipFolderFullPath, gzipFileName) {
|
||
log.ErrorF("upload of %s to demo oss error !", gzipFolderFullPath+gzipFileName)
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
return errorPullImageList, errorGzipImageList, fullNameList, allGzipFileNameList
|
||
}
|
||
|
||
// DownloadLoadTagPush DLTU procedure ImageSync的另外一般流程,需要支持 堡垒机(纯离线)的模式
|
||
// 2. Gzip文件目录,RKE MIDDLE CMII三个文件目录 - 约定目录
|
||
// 约定目录 /root/wdd/image/rke/ /root/wdd/image/middle/ /root/wdd/image/cmii/
|
||
// 3. 读取本机的IP地址 - 参数传递
|
||
// 4. OSS地址 - ossUrlPrefix传空 则使用默认值
|
||
// 5. ossFileName - 如果结尾为txt,则为文件的形式,如果为tar.gz,则为gzip文件夹的形式
|
||
func DownloadLoadTagPush(downloadFromOss bool, ossUrlPrefix, ossFileName, localGzipFolder string, targetHarborFullName string) []string {
|
||
|
||
separator := os.PathSeparator
|
||
if !strings.HasSuffix(localGzipFolder, string(separator)) {
|
||
localGzipFolder += string(separator)
|
||
}
|
||
|
||
// download
|
||
if downloadFromOss {
|
||
if !parseAndDownloadFromOss(ossUrlPrefix, ossFileName, localGzipFolder) {
|
||
log.ErrorF("download from oss error !")
|
||
return nil
|
||
}
|
||
}
|
||
// load
|
||
loadAllGzipImageFromLocalFolder(localGzipFolder)
|
||
|
||
// tag
|
||
// push
|
||
allFileInFolder, err := utils.ListAllFileInFolder(localGzipFolder)
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
for _, gzipFileName := range allFileInFolder {
|
||
log.DebugF("gzip file name is %s", gzipFileName)
|
||
imageFullName := image2.GzipFileNameToImageFullName(gzipFileName)
|
||
if imageFullName == "" {
|
||
log.ErrorF("gzip file %s to image full name error !", gzipFileName)
|
||
continue
|
||
}
|
||
targetImageFullName := image2.ImageNameToTargetImageFullName(imageFullName, targetHarborFullName)
|
||
|
||
// tag
|
||
image.TagFromSourceToTarget(imageFullName, targetImageFullName)
|
||
//push
|
||
pushResult := image.PushToOctopusKindHarbor(targetImageFullName)
|
||
defer pushResult.Close()
|
||
|
||
scanner := bufio.NewScanner(pushResult)
|
||
for scanner.Scan() {
|
||
}
|
||
fmt.Println()
|
||
fmt.Printf("%s to %s push success !", gzipFileName, targetImageFullName)
|
||
fmt.Println()
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func loadAllGzipImageFromLocalFolder(localGzipFolder string) {
|
||
image.LoadFromFolderPath(localGzipFolder)
|
||
}
|
||
|
||
func parseAndDownloadFromOss(ossUrlPrefix, ossFileName, localGzipFolder string) bool {
|
||
|
||
if ossUrlPrefix == "" {
|
||
ossUrlPrefix = DefaultOssUrlPrefix
|
||
}
|
||
log.InfoF("prepare to download from %s%s", ossUrlPrefix, ossFileName)
|
||
|
||
// get oss endpoint
|
||
// mc login
|
||
|
||
if !strings.HasPrefix(ossUrlPrefix, "/") {
|
||
ossUrlPrefix += "/"
|
||
}
|
||
|
||
if !DefaultCmiiMinioOperator.DemoMinioOperator.DownloadFileFromOssFullUrl(ossUrlPrefix+ossFileName, localGzipFolder) {
|
||
log.ErrorF("download %s from oss error !", ossUrlPrefix+ossFileName)
|
||
return false
|
||
}
|
||
|
||
if strings.HasSuffix(ossFileName, ".txt") {
|
||
// a list of files
|
||
// download all files in the txt file
|
||
result := utils.ReadLineFromFile(localGzipFolder + ossFileName)
|
||
for _, gzipFileName := range result {
|
||
DefaultCmiiMinioOperator.DemoMinioOperator.DownloadFileFromOssFullUrl(ossUrlPrefix+gzipFileName, localGzipFolder)
|
||
}
|
||
}
|
||
|
||
// 解析
|
||
return true
|
||
}
|
||
|
||
// DownloadCompressUploadFromDemo 获取DEMO环境的全部镜像
|
||
func DownloadCompressUploadFromDemo(projectName string, shouldGzip bool, shouldOss bool) (errorPullImageList, errorGzipImageList, realCmiiImageName, allGzipFileNameList []string) {
|
||
|
||
// generate a project folder
|
||
err := os.MkdirAll(image.OfflineImageGzipFolderPrefix+projectName, os.ModeDir)
|
||
if err != nil {
|
||
if !errors.Is(err, os.ErrExist) {
|
||
log.ErrorF("[Download_Compress_Upload_From_Demo] - create folder of %s error %s", image.OfflineImageGzipFolderPrefix+projectName, err.Error())
|
||
return errorPullImageList, errorGzipImageList, realCmiiImageName, allGzipFileNameList
|
||
}
|
||
}
|
||
|
||
// get demo image version map
|
||
allCmiiImageNameListFromDemo := buildAllCmiiImageNameListFromDemo(projectName)
|
||
|
||
// do work
|
||
// DCU
|
||
return DownloadCompressUpload(allCmiiImageNameListFromDemo, shouldGzip, image.OfflineImageGzipFolderPrefix+projectName, shouldOss)
|
||
}
|
||
|
||
func buildAllCmiiImageNameListFromDemo(projectName string) []string {
|
||
|
||
var realCmiiImageName []string
|
||
|
||
backendMap, frontendMap, srsMap := BackupAllCmiiDeploymentToMap(demo)
|
||
|
||
// save map to file
|
||
backendMapFile := image.OfflineImageGzipFolderPrefix + projectName + "-backend-app.json"
|
||
frontendMapFile := image.OfflineImageGzipFolderPrefix + projectName + "-frontend-app.json"
|
||
srsMapFile := image.OfflineImageGzipFolderPrefix + projectName + "-srs-app.json"
|
||
_ = os.Remove(backendMapFile)
|
||
_ = os.Remove(frontendMapFile)
|
||
_ = os.Remove(srsMapFile)
|
||
|
||
//utils.AppendContentToFile(
|
||
// utils.BeautifulPrintToString(backendMap),
|
||
// backendMapFile,
|
||
//)
|
||
//utils.AppendContentToFile(
|
||
// utils.BeautifulPrintToString(frontendMap),
|
||
// frontendMapFile,
|
||
//)
|
||
//utils.AppendContentToFile(
|
||
// utils.BeautifulPrintToString(srsMapFile),
|
||
// srsMapFile,
|
||
//)
|
||
|
||
realCmiiImageName = append(realCmiiImageName, image.CmiiImageMapToFullNameList(backendMap)...)
|
||
realCmiiImageName = append(realCmiiImageName, image.CmiiImageMapToFullNameList(frontendMap)...)
|
||
realCmiiImageName = append(realCmiiImageName, image.CmiiImageMapToFullNameList(srsMap)...)
|
||
|
||
utils.BeautifulPrintListWithTitle(realCmiiImageName, "Cmii Project Image => "+projectName)
|
||
|
||
return realCmiiImageName
|
||
}
|
||
|
||
// DownloadCompressUploadFromVersion 根据版本下载全部的CMII镜像
|
||
func DownloadCompressUploadFromVersion(cmiiVersion string, shouldGzip bool, shouldOss bool) (errorPullImageList, errorGzipImageList, realCmiiImageName, allGzipFileNameList []string) {
|
||
|
||
// generate a project folder
|
||
err := os.MkdirAll(image.OfflineImageGzipFolderPrefix+cmiiVersion, os.ModeDir)
|
||
if err != nil {
|
||
if !errors.Is(err, os.ErrExist) {
|
||
log.ErrorF("[Download_Compress_Upload_From_Demo] - create folder of %s error %s", image.OfflineImageGzipFolderPrefix+cmiiVersion, err.Error())
|
||
return errorPullImageList, errorGzipImageList, realCmiiImageName, allGzipFileNameList
|
||
}
|
||
}
|
||
|
||
// build all cmii image name list
|
||
realCmiiImageName = buildAllCmiiImageNameListFromVersion(cmiiVersion)
|
||
|
||
// do work
|
||
// DCU procedure
|
||
return DownloadCompressUpload(realCmiiImageName, shouldGzip, image.OfflineImageGzipFolderPrefix+cmiiVersion, shouldOss)
|
||
|
||
}
|
||
|
||
func buildAllCmiiImageNameListFromVersion(cmiiVersion string) []string {
|
||
|
||
var realCmiiImageName []string
|
||
|
||
backendMap := CmiiBackendAppMap
|
||
frontendMap := CmiiFrontendAppMap
|
||
|
||
for app := range backendMap {
|
||
backendMap[app] = cmiiVersion
|
||
}
|
||
for app := range frontendMap {
|
||
frontendMap[app] = cmiiVersion
|
||
}
|
||
|
||
realCmiiImageName = append(realCmiiImageName, image.CmiiImageMapToFullNameList(backendMap)...)
|
||
realCmiiImageName = append(realCmiiImageName, image.CmiiImageMapToFullNameList(frontendMap)...)
|
||
|
||
for key, value := range CmiiSrsAppMap {
|
||
var app *CmiiDeploymentInterface
|
||
if strings.Contains(value, "deployment") {
|
||
app = CmiiOperator.DeploymentOneInterface(demo, key)
|
||
if app != nil {
|
||
realCmiiImageName = append(realCmiiImageName, app.Image)
|
||
}
|
||
} else if strings.Contains(value, "state") {
|
||
app = CmiiOperator.StatefulSetOneInterface(demo, key)
|
||
if app != nil {
|
||
for _, imageName := range app.ContainerImageMap {
|
||
realCmiiImageName = append(realCmiiImageName, imageName)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
utils.BeautifulPrintListWithTitle(realCmiiImageName, "Cmii Version Image => "+cmiiVersion)
|
||
return realCmiiImageName
|
||
}
|
||
|
||
func DownloadCompressUploadDependency(shouldGzip bool, shouldOss bool, downloadMiddle bool, downloadRke bool) (errorPullImageList, errorGzipImageList, realCmiiImageName, allGzipFileNameList []string) {
|
||
err := os.MkdirAll(image.OfflineImageGzipFolderPrefix, os.ModeDir)
|
||
if err != nil {
|
||
if !errors.Is(err, os.ErrExist) {
|
||
log.ErrorF("[FetchDependencyRepos] - create folder of %s error %s", image.OfflineImageGzipFolderPrefix, err.Error())
|
||
}
|
||
}
|
||
|
||
if downloadMiddle {
|
||
|
||
gzipFolderPrefix := image.OfflineImageGzipFolderPrefix + "middle/"
|
||
|
||
// remove folder first
|
||
utils.RemoveFolderComplete(gzipFolderPrefix)
|
||
|
||
return DownloadCompressUpload(image.MiddlewareAmd64, shouldGzip, gzipFolderPrefix, shouldOss)
|
||
}
|
||
|
||
if downloadRke {
|
||
gzipFolderPrefix := image.OfflineImageGzipFolderPrefix + "rke/"
|
||
return DownloadCompressUpload(image.MiddlewareAmd64, shouldGzip, gzipFolderPrefix, shouldOss)
|
||
}
|
||
|
||
return errorPullImageList, errorGzipImageList, realCmiiImageName, allGzipFileNameList
|
||
}
|
||
|
||
func LoadSplitCmiiGzipImageToTargetHarbor(projectName, targetHarborHost string) (errorLoadImageNameList, errorPushImageNameList []string) {
|
||
|
||
// list folder
|
||
projectGzipFolder := image.OfflineImageGzipFolderPrefix + projectName
|
||
errorLoadImageNameList = append(errorLoadImageNameList, image.LoadFromFolderPath(projectGzipFolder)...)
|
||
// read from json
|
||
errorPushImageNameList = append(errorPushImageNameList, image.TagFromListAndPushToCHarbor(image.Cmii520DemoImageList, targetHarborHost)...)
|
||
|
||
// re-tag
|
||
// push
|
||
|
||
// todo clean host and harbor
|
||
// check harbor exits
|
||
|
||
return errorLoadImageNameList, errorPushImageNameList
|
||
}
|
||
|
||
func LoadSplitDepGzipImageToTargetHarbor(targetHarborHost string) (errorLoadImageNameList []string, errorPushImageNameList []string) {
|
||
|
||
//middle := image.OfflineImageGzipFolderPrefix + "middle/"
|
||
//rke := image.OfflineImageGzipFolderPrefix + "rke/"
|
||
//errorLoadImageNameList = append(errorLoadImageNameList, ImageLoadFromFolderPath(middle)...)
|
||
//errorLoadImageNameList = append(errorLoadImageNameList, ImageLoadFromFolderPath(rke)...)
|
||
|
||
errorPushImageNameList = append(errorPushImageNameList, image.TagFromListAndPushToCHarbor(image.MiddlewareAmd64, targetHarborHost)...)
|
||
errorPushImageNameList = append(errorPushImageNameList, image.TagFromListAndPushToCHarbor(image.Rancher1204Amd64, targetHarborHost)...)
|
||
|
||
return errorLoadImageNameList, errorPushImageNameList
|
||
|
||
}
|