package executor import ( "context" "encoding/json" "fmt" "net" "os" "strconv" "strings" "github.com/mittwald/goharbor-client/v5/apiv2" "github.com/mittwald/goharbor-client/v5/apiv2/model" ) type HarborOperator struct { SourceHarborHost string `json:"sourceHarborHost,omitempty"` TargetHarborHost string `json:"targetHarborHost,omitempty"` HarborPort string `json:"harborPort,omitempty"` HarborAdminUser string `json:"harborAdminUser,omitempty"` HarborAdminPass string `json:"harborAdminPass,omitempty"` TargetHarborClient *apiv2.RESTClient SourceHarborClient *apiv2.RESTClient } // NewHarborOperator 返回一个带有默认HarborAdminPass的HarborOperator实例 func NewHarborOperator() *HarborOperator { return &HarborOperator{ HarborPort: "8033", HarborAdminUser: "admin", // 设置默认值 HarborAdminPass: "V2ryStr@ngPss", // 设置默认值 } } // HarborOperatorCache 饿汉式单例 var HarborOperatorCache = NewHarborOperator() var OctopusReplicationPolicyName = "octopus-sync-replication" func (hOp *HarborOperator) Exec(baseFuncName string, funcArgs ...string) (bool, []string) { // 参见 HarborFunctionEnum resultOk := true var resultLog []string switch baseFuncName { case "CREATE_PROJECT": resultOk, resultLog = hOp.CreateProjectExec(funcArgs) break case "LIST_PROJECT": resultOk, resultLog = hOp.ListProjectExec(funcArgs) break case "SYNC_PROJECT_BETWEEN_HARBOR": resultOk, resultLog = hOp.SyncProjectExec(funcArgs) break case "SYNC_STATUS_HARBOR": resultOk, resultLog = hOp.SyncStatusExec(funcArgs) break } return resultOk, resultLog } func (hOp *HarborOperator) CreateProjectExec(funcArgs []string) (bool, []string) { if hOp.TargetHarborClient == nil { ok, createClient := hOp.CheckAndBuildHarborClient(funcArgs[0], true) if !ok { return false, []string{ "[Harbor Create Project] - Error !", } } hOp.TargetHarborClient = createClient } client := hOp.TargetHarborClient // create project // 定义你想要创建的仓库(项目)的详细信息 log.Debug("[Harbor Create Project] - create project !") needToCreateProjectNameList := []string{"cmii", "rancher"} // 使用客户端创建项目 ctx := context.Background() for _, projectName := range needToCreateProjectNameList { log.DebugF("start to create proect => %s", projectName) projectReq := &model.ProjectReq{ ProjectName: projectName, // 仓库名称 Metadata: &model.ProjectMetadata{ Public: "true", // 是否是公开的 }, } exists, _ := client.ProjectExists(ctx, projectName) if !exists { err := client.NewProject(ctx, projectReq) if err != nil { errorLog := fmt.Sprintf("Error creating project %s: %s\n", projectName, err.Error()) return false, []string{errorLog} } } log.DebugF("[Harbor Create Project] - Project %s already exists ! continue ", projectName) } successLog := "[Harbor CreateProjectExec] - Project Create Success !" log.Info(successLog) return true, []string{successLog} } func (hOp *HarborOperator) CheckAndBuildHarborClient(targetHarborHost string, isTarget bool) (bool, *apiv2.RESTClient) { log.InfoF("[Harbor Client Create] - start to create harbor client %s", targetHarborHost) parseIP := net.ParseIP(targetHarborHost) if parseIP == nil { log.Error( fmt.Sprintf("[Harbor Client Create] - ip format is wrong! parseIP is => %s ", parseIP), ) return false, nil } if isTarget { hOp.TargetHarborHost = "http://" + targetHarborHost + ":" + hOp.HarborPort + "/api/" log.DebugF("[Harbor Client Create] - harbor host is => %s", hOp.TargetHarborHost) } else { hOp.SourceHarborHost = "http://" + targetHarborHost + ":" + hOp.HarborPort + "/api/" log.DebugF("[Harbor Client Create] - harbor host is => %s", hOp.SourceHarborHost) } // check connection var client *apiv2.RESTClient var err error if isTarget { client, err = apiv2.NewRESTClientForHost(hOp.TargetHarborHost, hOp.HarborAdminUser, hOp.HarborAdminPass, nil) } else { client, err = apiv2.NewRESTClientForHost(hOp.SourceHarborHost, hOp.HarborAdminUser, hOp.HarborAdminPass, nil) } if err != nil { errorLog := fmt.Sprintf("Error creating REST client: %s\n", err.Error()) log.Error(errorLog) return false, nil } return true, client } func (hOp *HarborOperator) ListProjectExec(funcArgs []string) (bool, []string) { if hOp.TargetHarborClient == nil { ok, createClient := hOp.CheckAndBuildHarborClient(funcArgs[0], true) if !ok { return false, []string{ "[Harbor Create Project ] - Error !", } } hOp.TargetHarborClient = createClient } client := hOp.TargetHarborClient // 使用客户端列出所有项目 ctx := context.Background() projects, err := client.ListProjects(ctx, "") if err != nil { fmt.Printf("Error listing projects: %v\n", err) os.Exit(1) } // 打印所有项目的信息 for _, project := range projects { fmt.Printf("Project ID: %d, Name: %s, Public: %v\n", project.ProjectID, project.Name, project.Metadata.Public) } marshal, _ := json.Marshal(projects) return true, []string{ fmt.Sprintf("List Projects of %s ", hOp.TargetHarborHost), string(marshal), } } func (hOp *HarborOperator) SyncProjectExec(funcArgs []string) (bool, []string) { if hOp.TargetHarborClient == nil { ok, createClient := hOp.CheckAndBuildHarborClient(funcArgs[0], true) if !ok { return false, []string{ "[Harbor Sync Project ] - Error !", } } hOp.TargetHarborClient = createClient } targetClient := hOp.TargetHarborClient if hOp.SourceHarborClient == nil { ok, createClient := hOp.CheckAndBuildHarborClient(funcArgs[1], false) if !ok { return false, []string{ "[Harbor Sync Project ] - Error !", } } hOp.SourceHarborClient = createClient } needToSynchronizedProject := funcArgs[2] log.InfoF("[Harbor Sync Project ] - start to sync harbor project => %s", needToSynchronizedProject) log.DebugF("[Harbor Sync Project ] - start to check projects all exists!") ctx := context.Background() // check both source and target harbor project exists needToCreateProjectNameList := []string{"rancher", "cmii"} for _, projectName := range needToCreateProjectNameList { syncNotExistHarborProjectError := []string{ "[Harbor Sync Project ] - project not exists !", } exists, _ := targetClient.ProjectExists(ctx, projectName) if !exists { return false, append(syncNotExistHarborProjectError, "targetClient") } } OctopusSourceHarborName := "octopus-source" // add source registry to destination harbor log.InfoF("[Harbor Sync Project ] - start to create source harbor endpoints => %s", hOp.SourceHarborHost) log.Debug("[Harbor Sync Project ] - start to delete exist replication policy !") policies, _ := targetClient.ListReplicationPolicies(ctx) if policies != nil { for _, policy := range policies { if policy.Name == OctopusReplicationPolicyName { err := targetClient.DeleteReplicationPolicyByID(ctx, policy.ID) if err != nil { log.ErrorF("[Harbor Sync Project ] - delete exists replication policy failed ! => %v ", policy) } } } } log.Debug("[Harbor Sync Project ] - start to delete exist exist harbor endpoints !") exitRegistry, _ := targetClient.GetRegistryByName(ctx, OctopusSourceHarborName) if exitRegistry != nil { log.Debug("[Harbor Sync Project ] - source endpoints already exists ! delete it !") err := targetClient.DeleteRegistryByID(ctx, exitRegistry.ID) if err != nil { log.ErrorF("[Harbor Sync Project ] - source endpoints delete failed ! => %v ", exitRegistry) } } octopusSourceRegistry := &model.Registry{ Credential: &model.RegistryCredential{ AccessKey: "admin", AccessSecret: "V2ryStr@ngPss", Type: "basic", }, Insecure: true, ID: 7, Name: OctopusSourceHarborName, // 源 Harbor 实例的注册表 ID,通常为 0 Type: "harbor", URL: strings.Split(hOp.SourceHarborHost, "/api")[0], } err := targetClient.NewRegistry(ctx, octopusSourceRegistry) if err != nil { return false, []string{ "[Harbor Sync Project ] - source endpoints create failed !", } } // get the real one for it's ID realOctopusSourceRegistry, err := targetClient.GetRegistryByName(ctx, OctopusSourceHarborName) if err != nil { return false, []string{ "[Harbor Sync Project ] - source endpoints get failed !", } } // 创建复制策略 octopusReplicationPolicy := &model.ReplicationPolicy{ CopyByChunk: nil, Deletion: false, Description: "", DestNamespace: "", // 可以指定目标 Harbor 中的特定项目,如果为空,则使用源项目名称 DestNamespaceReplaceCount: nil, SrcRegistry: &model.Registry{ ID: realOctopusSourceRegistry.ID, }, DestRegistry: &model.Registry{ ID: 0, }, Enabled: true, Filters: []*model.ReplicationFilter{ { Type: "name", Value: needToSynchronizedProject + "/**", }, }, ID: 0, Name: OctopusReplicationPolicyName, Override: true, ReplicateDeletion: false, Speed: nil, Trigger: &model.ReplicationTrigger{ Type: "manual", // 可以是 "manual", "scheduled", 或 "event_based" // 如果是 "scheduled",还需要设置 Cron 表达式 // TriggerSettings: &model.TriggerSettings{Cron: "0 * * * *"}, }, } // 在源 Harbor 中创建复制策略 log.InfoF("[Harbor Sync Project ] - Start To Sync Project => %s !", needToSynchronizedProject) err = targetClient.NewReplicationPolicy(ctx, octopusReplicationPolicy.DestRegistry, octopusReplicationPolicy.SrcRegistry, octopusReplicationPolicy.Deletion, octopusReplicationPolicy.Override, octopusReplicationPolicy.Enabled, octopusReplicationPolicy.Filters, octopusReplicationPolicy.Trigger, octopusReplicationPolicy.DestNamespace, octopusReplicationPolicy.Name, octopusReplicationPolicy.Name) if err != nil { syncErrorMessage := fmt.Sprintf("[Harbor Sync Project ] - Sync Project [ %s ] Failed ! Error is => %s\n", needToSynchronizedProject, err.Error()) log.Error(syncErrorMessage) return false, []string{ syncErrorMessage, } } realOctopusReplicationPolicy, err := targetClient.GetReplicationPolicyByName(ctx, OctopusReplicationPolicyName) if err != nil { return false, []string{ "[Harbor Sync Project ] - failed to get the realOctopusReplicationPolicy!", err.Error(), } } err = targetClient.TriggerReplicationExecution(ctx, &model.StartReplicationExecution{ PolicyID: realOctopusReplicationPolicy.ID, }) if err != nil { return false, []string{ "[ Harbor Sync Project ] - failed to start the harbor sync execution !", err.Error(), } } return true, []string{ fmt.Sprintf("[ Harbor Sync Project ] - sync project [ %s ] started !", needToSynchronizedProject), } } func (hOp *HarborOperator) SyncStatusExec(funcArgs []string) (bool, []string) { if hOp.TargetHarborClient == nil { ok, createClient := hOp.CheckAndBuildHarborClient(funcArgs[0], true) if !ok { return false, []string{ "[ Sync Status ] - Error !", } } hOp.TargetHarborClient = createClient } targetClient := hOp.TargetHarborClient ctx := context.Background() // check replication policy exists replicationPolicy, err := targetClient.GetReplicationPolicyByName(ctx, OctopusReplicationPolicyName) if err != nil { return false, []string{ "[ Sync Status ] - get replication error !", err.Error(), } } // list execution status replicationExecutions, err := targetClient.ListReplicationExecutions(ctx, &replicationPolicy.ID, nil, nil) if err != nil { return false, []string{ "[ Sync Status ] - replication has no sync work !", err.Error(), } } // find the newest one only have one here for _, execution := range replicationExecutions { if !strings.HasPrefix(execution.Status, "Succeed") { bytes, _ := json.Marshal(execution) log.InfoF("[sync status]- status are => %v", string(bytes)) // report status return false, []string{ fmt.Sprintf("[sync status] - not complete ! progress is => %s %", strconv.FormatFloat(float64(execution.Succeed)/float64(execution.Total), 'f', 1, 64)), } } } return true, []string{ "[sync status]- sync completed !", } } func (hOp *HarborOperator) Command(baseFuncName string, funcArgs ...string) []string { return nil }