Files
ProjectOctopus/agent-operator/CmiiOperator.go
2024-04-28 11:56:22 +08:00

464 lines
16 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"errors"
"os"
"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 != "" {
// 获取特定版本的镜像
errorPullImageList, errorGzipImageList, allCmiiImageNameList, allGzipFileNameList = DownloadCompressUploadFromVersion(sync.ProjectVersion, sync.CompressImageToGzip, sync.UploadToDemoMinio)
gzipFolderFullPath = image.OfflineImageGzipFolderPrefix + sync.ProjectVersion
} else {
// 获取DEMO的镜像
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 {
ok, gzipImageFileFullPath := image.SaveToGzipFile(imageFullName, gzipFolderFullPath)
if !ok {
errorGzipImageList = append(errorGzipImageList, imageFullName)
continue
}
// 压缩成功
allGzipFileNameList = append(allGzipFileNameList, gzipImageFileFullPath)
}
// 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 !")
// start to upload
// extract demo oss location suffix from gzipFolderFullPath
trimPrefix := strings.TrimPrefix(gzipFolderFullPath, image.OfflineImageGzipFolderPrefix)
bucketNameWithPrefix := "cmlc-installation/" + trimPrefix
log.InfoF("gzip file location in demo oss is %s", DefaultDemoEndpoint+"/"+bucketNameWithPrefix)
for _, gzipFileName := range allGzipFileNameList {
// SaveToGzipFile 返回的是全路径 归一化处理 gzip file name
gzipFileName = strings.TrimPrefix(gzipFileName, gzipFolderFullPath)
if !DefaultCmiiMinioOperator.UploadToDemo(bucketNameWithPrefix, gzipFolderFullPath, gzipFileName) {
log.ErrorF("upload of %s to demo oss error !", gzipFolderFullPath+gzipFileName)
}
}
}
return errorPullImageList, errorGzipImageList, fullNameList, allGzipFileNameList
}
// DownloadLoadTagUpload 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 DownloadLoadTagUpload(downloadFromOss bool, ossUrlPrefix, ossFileName, localGzipFolderOrGzipFile string, targetHarborFullName string) (targetImageFullNameList []string) {
// 支持单文件的形式
if !utils.IsFileOrDir(localGzipFolderOrGzipFile) {
// 单个压缩文件 肯定是离线的形式
if !strings.HasSuffix(localGzipFolderOrGzipFile, ".tar.gz") {
log.ErrorF("local gzip file %s is not a .tar.gz file !", localGzipFolderOrGzipFile)
return nil
}
// load
image.LoadFromGzipFilePath(localGzipFolderOrGzipFile)
} else {
separator := os.PathSeparator
if !strings.HasSuffix(localGzipFolderOrGzipFile, string(separator)) {
localGzipFolderOrGzipFile += string(separator)
}
// download
if downloadFromOss {
if !parseAndDownloadFromOss(ossUrlPrefix, ossFileName, localGzipFolderOrGzipFile) {
log.ErrorF("download from oss error !")
return nil
}
}
// load
loadAllGzipImageFromLocalFolder(localGzipFolderOrGzipFile)
}
// tag
// push
allFileInFolder, err := utils.ListAllFileInFolder(localGzipFolderOrGzipFile)
if err != nil {
return nil
}
for _, gzipFileName := range allFileInFolder {
// 过滤非.tar.gz结尾的文件
if !strings.HasSuffix(gzipFileName, ".tar.gz") {
continue
}
log.DebugF("gzip file name is %s", gzipFileName)
// gzip to image full name 拿到镜像的原始名称
imageFullName := image2.GzipFileNameToImageFullName(gzipFileName)
if imageFullName == "" {
log.ErrorF("gzip file %s to image full name error !", gzipFileName)
continue
}
// tag 拿到目标名称 然后重新Tag
targetImageFullName := image2.ImageNameToTargetImageFullName(imageFullName, targetHarborFullName)
image.TagFromSourceToTarget(imageFullName, targetImageFullName)
// uploadToHarbor 上传到目标Harbor
if image.UploadToHarbor(targetImageFullName) {
targetImageFullNameList = append(targetImageFullNameList, targetHarborFullName)
} else {
log.ErrorF("upload to harbor error of %s", targetImageFullName)
}
}
return targetImageFullNameList
}
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 = DefaultCmiiOperator.DeploymentOneInterface(demo, key)
if app != nil {
realCmiiImageName = append(realCmiiImageName, app.Image)
}
} else if strings.Contains(value, "state") {
app = DefaultCmiiOperator.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
}