Files
ProjectOctopus/agent-operator/CmiiOperator.go
2024-07-04 10:58:01 +08:00

513 lines
18 KiB
Go
Executable File
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-deploy/d_app"
"wdd.io/agent-operator/image"
)
const OfflineDeployHarborHost = "harbor.wdd.io"
const PublicDeployHarborHost = "42.192.52.227"
const DirectPushDeployHarborHost = "chongqingcis-9b4a3da9.ecis.chongqing-1.cmecloud.cn"
type ImageSyncEntity struct {
ProjectName string // 优先级3 优先级最低 从DEMO拉取镜像
ProjectVersion string // 优先级2 高于ProjectName 优先拉取特定版本的镜像
CmiiNameTagList []string // 优先级1 appName:tag 会被转换为FullNameImageList
FullNameImageList []string // 优先级1 优先下载此类型
ShouldDownloadImage bool // 下载镜像 DCU中的D
ShouldCompressImageToGzip bool // 压缩镜像 DCU中的C
ShouldUploadToDemoMinio bool // 上传镜像 DCU中的U
ShouldDownloadFromOss bool // 下载镜像 DLTU中的D
ShouldUpdateImageTag bool // 更新镜像 DLTU中的U
ShouldDirectPushToHarbor bool // 直接推送到对方的主机 || 离线部署机
DirectHarborHost string // IP:Port or 域名:PORT 不带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 = C_DownloadCompressUploadFromVersion(sync.ProjectVersion, sync.ShouldCompressImageToGzip, sync.ShouldUploadToDemoMinio)
gzipFolderFullPath = image.OfflineImageGzipFolderPrefix + sync.ProjectVersion
} else {
// 获取DEMO的镜像
errorPullImageList, errorGzipImageList, allCmiiImageNameList, allGzipFileNameList = C_DownloadCompressUploadFromDemo(sync.ProjectName, sync.ShouldCompressImageToGzip, sync.ShouldUploadToDemoMinio)
gzipFolderFullPath = image.OfflineImageGzipFolderPrefix + sync.ProjectName
}
} else {
// 拉取特定的镜像
gzipFolderFullPath = image.OfflineImageGzipFolderPrefix + "tmp"
// 组装镜像名称
allCmiiImageNameList = concatAndUniformCmiiImage(sync.FullNameImageList, sync.CmiiNameTagList)
// DCU
errorPullImageList, errorGzipImageList, realCmiiImageList, allGzipFileNameList = A_DownloadCompressUpload(true, allCmiiImageNameList, sync.ShouldCompressImageToGzip, gzipFolderFullPath, sync.ShouldUploadToDemoMinio)
}
// 直接传输到目标Harbor仓库
if sync.ShouldDirectPushToHarbor {
if sync.DirectHarborHost == "" {
log.ErrorF("DirectHarborHost is null ! can't push to target harbor !")
}
// push to
errorPushImageNameList = image.TagFromListAndPushToCHarbor(allCmiiImageNameList, 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
}
// A_DownloadCompressUpload DCU 镜像同步的前半部分通常在35.71 LapPro执行无需Bastion Mode
func A_DownloadCompressUpload(downloadImage bool, fullNameList []string, shouldGzip bool, gzipFolderFullPath string, shouldOss bool) (errorPullImageList, errorGzipImageList, realCmiiImageName, allGzipFileFullNameList []string) {
// write to file
localGzipFileListTxt := gzipFolderFullPath + "all-gzip-image-file-name.txt"
// Download
log.Info("DOWNLOAD START !")
if downloadImage {
if fullNameList == nil || len(fullNameList) == 0 {
log.InfoF("no image name list !")
} else {
errorPullImageList = image.PullFromFullNameList(fullNameList)
}
}
// remove failed
fullNameList = slices.DeleteFunc(fullNameList, func(imageName string) bool {
return slices.Contains(errorPullImageList, imageName)
})
// Compress
if shouldGzip {
// remove file
_ = os.Remove(localGzipFileListTxt)
gzipFileAlready := make(map[string]bool)
if utils.FileOrFolderExists(gzipFolderFullPath) {
dir, _ := os.ReadDir(gzipFolderFullPath)
for _, entry := range dir {
if entry.IsDir() {
continue
}
gzipFileAlready[strings.TrimPrefix(entry.Name(), gzipFolderFullPath)] = true
}
}
// 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 {
// gzip image file already exists
gzipFileName := image2.ImageFullNameToGzipFileName(imageFullName)
gzipImageFileFullPath := gzipFolderFullPath + gzipFileName
_, ok := gzipFileAlready[gzipFileName]
if len(gzipFileAlready) > 0 && ok {
log.DebugF("gzip file %s already exists !", gzipFileName)
} else {
ok, gzipImageFileFullPath = image.SaveToGzipFile(imageFullName, gzipFolderFullPath)
if !ok {
errorGzipImageList = append(errorGzipImageList, imageFullName)
continue
}
}
// 压缩成功
allGzipFileFullNameList = append(allGzipFileFullNameList, gzipImageFileFullPath)
}
// remove failed
fullNameList = slices.DeleteFunc(fullNameList, func(imageName string) bool {
return slices.Contains(errorGzipImageList, imageName)
})
for _, gzipFileFullName := range allGzipFileFullNameList {
utils.AppendContentToFile(
strings.TrimPrefix(gzipFileFullName, gzipFolderFullPath)+"\n",
localGzipFileListTxt,
)
}
log.InfoF("all gzip file name list is %s", localGzipFileListTxt)
}
// 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)
// upload gzip file list txt to demo
if !DefaultCmiiMinioOperator.UploadToDemo(bucketNameWithPrefix, gzipFolderFullPath, strings.TrimPrefix(localGzipFileListTxt, gzipFolderFullPath)) {
log.ErrorF("upload of %s to demo oss error !", localGzipFileListTxt)
}
log.InfoF("upload all gzip file to demo oss !")
for _, gzipFileFullName := range allGzipFileFullNameList {
// SaveToGzipFile 返回的是全路径 归一化处理 gzip file name
gzipFileFullName = strings.TrimPrefix(gzipFileFullName, gzipFolderFullPath)
if !DefaultCmiiMinioOperator.UploadToDemo(bucketNameWithPrefix, gzipFolderFullPath, gzipFileFullName) {
log.ErrorF("upload of %s to demo oss error !", gzipFileFullName)
}
}
}
return errorPullImageList, errorGzipImageList, fullNameList, allGzipFileFullNameList
}
// A_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 A_DownloadLoadTagUpload(downloadFromOss bool, ossUrlPrefix, ossFileNameOrGzipFileListTxt, localGzipFolderOrGzipFile string, targetHarborFullName string) (targetImageFullNameList []string) {
// 支持单文件的形式
if !utils.IsDirOrFile(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, ossFileNameOrGzipFileListTxt, 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, targetImageFullName)
} 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
}
if !strings.HasSuffix(ossUrlPrefix, "/") {
ossUrlPrefix += "/"
}
log.InfoF("prepare to download from %s%s", ossUrlPrefix, ossFileName)
if !DefaultCmiiMinioOperator.DemoMinioOperator.DownloadFileFromOssFullUrl(ossUrlPrefix+ossFileName, localGzipFolder) {
log.ErrorF("download %s from oss error !", ossUrlPrefix+ossFileName)
return false
}
if strings.HasSuffix(ossFileName, ".txt") {
// download from gzip file list txt
// download all files in the txt file
result := utils.ReadAllContentFromFile(localGzipFolder + ossFileName)
for _, gzipFileName := range result {
DefaultCmiiMinioOperator.DemoMinioOperator.DownloadFileFromOssFullUrl(ossUrlPrefix+gzipFileName, localGzipFolder)
}
}
// 解析
return true
}
// C_DownloadCompressUploadFromDemo 获取DEMO环境的全部镜像
func C_DownloadCompressUploadFromDemo(projectName string, shouldGzip, 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 A_DownloadCompressUpload(true, 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
}
// C_DownloadCompressUploadFromVersion 根据版本下载全部的CMII镜像
func C_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 A_DownloadCompressUpload(true, realCmiiImageName, shouldGzip, image.OfflineImageGzipFolderPrefix+cmiiVersion, shouldOss)
}
func buildAllCmiiImageNameListFromVersion(cmiiVersion string) []string {
var realCmiiImageName []string
backendMap := d_app.CmiiBackendAppMap
frontendMap := d_app.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 d_app.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
}
// C_DownloadCompressUploadDependency DCU所有的依赖镜像
func C_DownloadCompressUploadDependency(shouldGzip bool, shouldOss bool, shouldDownload bool, isRKE bool) (errorPullImageList, errorGzipImageList, realCmiiImageName, allGzipFileNameList []string) {
log.Info("DCU for middle and rke!")
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())
}
}
var fulleImageNameList []string
var gzipFolderPrefix string
if isRKE {
log.Info("DCU for rke!")
fulleImageNameList = d_app.Rancher1204Amd64
gzipFolderPrefix = image.OfflineImageGzipFolderPrefix + "rke/"
} else {
log.Info("DCU for middle!")
fulleImageNameList = d_app.MiddlewareAmd64
gzipFolderPrefix = image.OfflineImageGzipFolderPrefix + "middle/"
}
return A_DownloadCompressUpload(shouldDownload, fulleImageNameList, shouldGzip, gzipFolderPrefix, shouldOss)
}
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(d_app.Cmii520DemoImageList, targetHarborHost)...)
// re-tag
// push
// todo clean host and harbor
// check harbor exits
return errorLoadImageNameList, errorPushImageNameList
}
func LoadSplitDepGzipImageToTargetHarbor(targetHarborHost string) (errorLoadImageNameList []string, errorPushImageNameList []string) {
errorPushImageNameList = append(errorPushImageNameList, image.TagFromListAndPushToCHarbor(d_app.MiddlewareAmd64, targetHarborHost)...)
//errorPushImageNameList = append(errorPushImageNameList, image.TagFromListAndPushToCHarbor(d_app.Rancher1204Amd64, targetHarborHost)...)
return errorLoadImageNameList, errorPushImageNameList
}