云效跨賬號(hào)遷移,你學(xué)會(huì)了嗎?
背 景
由于業(yè)務(wù)調(diào)整或組織架構(gòu)變更,原阿里云賬號(hào)將不再繼續(xù)使用。為了確保業(yè)務(wù)連續(xù)性和數(shù)據(jù)完整性,需要將源賬號(hào)下的所有云產(chǎn)品及其相關(guān)數(shù)據(jù)完整、安全地遷移到目標(biāo)賬號(hào)。由于云效(DevOps平臺(tái))無(wú)法直接通過(guò)實(shí)例劃撥至目標(biāo)賬號(hào),因此需要對(duì)云效中的數(shù)據(jù)進(jìn)行遷移。
創(chuàng)建企業(yè)
在目標(biāo)賬號(hào)上點(diǎn)擊
https://devops.console.aliyun.com/organizations/standard
此鏈接,進(jìn)入云效控制臺(tái)界面,點(diǎn)擊“新建組織”按鈕。
圖片
選擇“云效DevOps”點(diǎn)擊“立即開(kāi)啟”按鈕。
圖片
根據(jù)源賬號(hào)云效,填寫(xiě)“組織名稱(chēng)”和“研發(fā)組織規(guī)?!保顚?xiě)完成后點(diǎn)擊“立即創(chuàng)建”按鈕,完成云效企業(yè)的創(chuàng)建。
圖片
新建項(xiàng)目
進(jìn)入剛新建企業(yè)的控制臺(tái)后,點(diǎn)擊“項(xiàng)目協(xié)作 Projects”旁的“進(jìn)入工作”按鈕。
圖片
點(diǎn)擊“新建”按鈕,創(chuàng)建項(xiàng)目。
圖片
根據(jù)源賬號(hào)項(xiàng)目類(lèi)型,選擇對(duì)應(yīng)的項(xiàng)目模板。
圖片
根據(jù)源賬號(hào)項(xiàng)目信息進(jìn)行填寫(xiě),填寫(xiě)完成后點(diǎn)擊“新建”按鈕。
圖片
新建完成后的項(xiàng)目如下圖所示:
圖片
遷移需求
源賬號(hào)導(dǎo)出需求
在源賬號(hào)需求界面,點(diǎn)擊“批量操作”,在下拉菜單中單擊“導(dǎo)出全部”。
圖片
全選所有要導(dǎo)出的屬性,完成后點(diǎn)擊“開(kāi)始導(dǎo)出”按鈕。
圖片
在跳轉(zhuǎn)后的“批量操作記錄”界面,等待“進(jìn)展”的進(jìn)度條顯示成功后,點(diǎn)擊右邊的“下載”圖表按鈕,下載數(shù)據(jù)文件。
圖片
需求數(shù)據(jù)表格打開(kāi)后如下圖所示:
圖片
目標(biāo)賬號(hào)導(dǎo)入需求
在目標(biāo)賬號(hào)的任務(wù)界面右上角,點(diǎn)擊“導(dǎo)入數(shù)據(jù)”按鈕。
圖片
選中“包含子工作項(xiàng)”后,點(diǎn)擊“下載模板”按鈕。
圖片
將源賬號(hào)導(dǎo)出的數(shù)據(jù),根據(jù)導(dǎo)入模板的格式,寫(xiě)入到該excel表格里。
圖片
注意:負(fù)責(zé)人必須為當(dāng)前企業(yè)已存在的用戶名稱(chēng),迭代為當(dāng)前項(xiàng)目已創(chuàng)建的迭代,填寫(xiě)不存在的信息會(huì)導(dǎo)致導(dǎo)入報(bào)錯(cuò)
返回到導(dǎo)入數(shù)據(jù)界面中,繼續(xù)點(diǎn)擊“下一步”按鈕。
圖片
將剛填寫(xiě)完數(shù)據(jù)的導(dǎo)入模板上傳上去,完成后點(diǎn)擊“開(kāi)始導(dǎo)入”按鈕。
圖片
直到“導(dǎo)入工作項(xiàng)”顯示為成功,則表示需求數(shù)據(jù)導(dǎo)入完成。
圖片
檢查需求數(shù)據(jù)是否跟源賬號(hào)上的數(shù)據(jù)一致。
圖片
遷移任務(wù)
源賬號(hào)導(dǎo)出任務(wù)
在源賬號(hào)任務(wù)界面,點(diǎn)擊“批量操作”,在下拉菜單中單擊“導(dǎo)出全部”。
圖片
全選所有要導(dǎo)出的屬性,完成后點(diǎn)擊“開(kāi)始導(dǎo)出”按鈕。
圖片
查看導(dǎo)出的任務(wù)數(shù)據(jù),如下圖所示:
圖片
目標(biāo)賬號(hào)導(dǎo)入任務(wù)
在目標(biāo)賬號(hào)的任務(wù)界面右上角,點(diǎn)擊“導(dǎo)入數(shù)據(jù)”按鈕。
圖片
選中“包含子工作項(xiàng)”后,點(diǎn)擊“下載模板”按鈕。
圖片
將源賬號(hào)導(dǎo)出的數(shù)據(jù),根據(jù)導(dǎo)入模板的格式,寫(xiě)入到該excel表格里。
圖片
和遷移需求一樣,點(diǎn)擊“下一步”按鈕,將任務(wù)數(shù)據(jù)導(dǎo)入到目標(biāo)賬號(hào)中。
圖片
遷移代碼組、代碼倉(cāng)庫(kù)、迭代
代碼和迭代無(wú)法像需求和任務(wù)一樣批量導(dǎo)出導(dǎo)入,為了提高效率和準(zhǔn)確性,我們采用代碼化方式批量遷移代碼組、代碼倉(cāng)庫(kù)和迭代,避免手動(dòng)操作的費(fèi)時(shí)費(fèi)力及潛在錯(cuò)誤。
獲取組織ID
代碼里需要用到“組織ID”,點(diǎn)擊頭像下拉菜單里的“管理后臺(tái)”按鈕,在“基本信息”頁(yè)面即可看到“組織ID”。
圖片
獲取企業(yè)空間ID
代碼里需要用到“企業(yè)空間ID”,點(diǎn)擊
https://next.api.aliyun.com/api/devops/2021-06-25/GetCodeupOrganizatio
此鏈接,在“identity 企業(yè)標(biāo)識(shí)”輸入框內(nèi)輸入“組織ID”,完成后點(diǎn)擊“發(fā)起調(diào)用”按鈕。在左側(cè)“調(diào)用結(jié)果”頁(yè)面可以看到“namespaceId”,即為企業(yè)空間ID。
圖片
獲取個(gè)人訪問(wèn)令牌
點(diǎn)擊頭像,在下拉菜單中點(diǎn)擊“個(gè)人設(shè)置”,進(jìn)入個(gè)人設(shè)置界面,選擇“個(gè)人訪問(wèn)令牌”,點(diǎn)擊“創(chuàng)建訪問(wèn)令牌”按鈕。
輸入“令牌名稱(chēng)”,選擇到期時(shí)間,按需選擇權(quán)限。代碼管理權(quán)限必選,后續(xù)要用它進(jìn)行代碼倉(cāng)庫(kù)數(shù)據(jù)導(dǎo)入操作。完成上述配置后,點(diǎn)擊“新建”按鈕創(chuàng)建個(gè)人訪問(wèn)令牌。
執(zhí)行代碼批量遷移
運(yùn)行“源賬號(hào)導(dǎo)出代碼”,會(huì)print輸出代碼組、代碼倉(cāng)庫(kù)和迭代的信息,運(yùn)行“目標(biāo)賬號(hào)導(dǎo)入代碼”時(shí)按代碼提示輸入代碼組、代碼倉(cāng)庫(kù)和迭代的信息,即可完成導(dǎo)入。執(zhí)行前請(qǐng)?jiān)跍y(cè)試環(huán)境運(yùn)行測(cè)試?。?!
源賬號(hào)導(dǎo)出代碼
# -*- coding: utf-8 -*-
# This file is auto-generated, don't edit it. Thanks.
import sys,time
from typing import List
from alibabacloud_devops20210625.client import Client as devops20210625Client
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_devops20210625 import models as devops_20210625_models
from alibabacloud_tea_util import models as util_models
from alibabacloud_tea_util.client import Client as UtilClient
ak="源賬號(hào)AK"
sk="源賬號(hào)SK"
organization_id='源賬號(hào)組織ID'
parent_id= 源賬號(hào)企業(yè)空間ID
class Sample:
def __init__(self):
pass
@staticmethod
def create_client(
access_key_id: str,
access_key_secret: str,
) -> devops20210625Client:
config = open_api_models.Config(access_key_id=ak,access_key_secret=sk)
config.endpoint = f'devops.cn-hangzhou.aliyuncs.com'
return devops20210625Client(config)
#導(dǎo)出代碼組
@staticmethod
def ListGroupMember(
args: List[str],
) -> None:
organization_id = args[0]
groupId = args[1]
User_list=[]
client = Sample.create_client(ak, sk)
list_group_member_request = devops_20210625_models.ListGroupMemberRequest(organization_id=organization_id)
runtime = util_models.RuntimeOptions()
headers = {}
Users = client.list_group_member_with_options(groupId, list_group_member_request, headers,runtime).body.result
for User in Users:
User_dic={}
User_dic["name"]=User.name
User_dic["access_level"] = User.access_level
User_list.append(User_dic)
return User_list
@staticmethod
def ListRepositoryGroups(
args: List[str],
) -> None:
organization_id=args[0]
parent_id=args[1]
ListRepositoryGroups_list=[]
client = Sample.create_client(ak, sk)
list_repository_groups_request = devops_20210625_models.ListRepositoryGroupsRequest(organization_id=organization_id,parent_id=parent_id,page_size=100)
runtime = util_models.RuntimeOptions()
headers = {}
ListRepositoryGroups=client.list_repository_groups_with_options(list_repository_groups_request, headers, runtime).body.result
for RepositoryGroups in ListRepositoryGroups:
ListRepositoryGroup_dic = {}
ListRepositoryGroup_dic["id"] = RepositoryGroups.id
ListRepositoryGroup_dic["name"] =RepositoryGroups.name
ListRepositoryGroup_dic["description"] =RepositoryGroups.description
ListRepositoryGroup_dic["path"] = RepositoryGroups.path
ListRepositoryGroup_dic["visibility_level"] = RepositoryGroups.visibility_level
User_list=Sample.ListGroupMember([organization_id, str(RepositoryGroups.id)])
ListRepositoryGroup_dic["user_list"] = User_list
ListRepositoryGroups_list.append(ListRepositoryGroup_dic)
return ListRepositoryGroups_list
#獲取代碼倉(cāng)庫(kù)信息
@staticmethod
def GetRepositorymain(
args: List[str],
) -> None:
organization_id = args[0]
identity = args[1]
client = Sample.create_client(ak, sk)
get_repository_request = devops_20210625_models.GetRepositoryRequest(organization_id=organization_id,identity=identity)
runtime = util_models.RuntimeOptions()
headers = {}
http_url_to_repository = client.get_repository_with_options(get_repository_request, headers,runtime).body.repository.http_url_to_repository
return http_url_to_repository
@staticmethod
def ListRepositoriesmain(
args: List[str],
) -> None:
organization_id = args[0]
client = Sample.create_client(ak, sk)
list_repositories_request = devops_20210625_models.ListRepositoriesRequest(organization_id=organization_id,per_page=100)
runtime = util_models.RuntimeOptions()
headers = {}
ListRepositories = client.list_repositories_with_options(list_repositories_request, headers,runtime).body.result
repository_list = []
for Repository in ListRepositories:
repository_dic = {}
repository_dic["name"] = Repository.name
repository_dic["path"] = Repository.path
repository_dic["namespace_id"] = Repository.namespace_id
repository_dic["description"] = Repository.description
repository_dic["visibility_level"] = Repository.visibility_level
http_url_to_repository = Sample.GetRepositorymain([organization_id, Repository.id])
repository_dic["import_url"] = http_url_to_repository
repository_list.append(repository_dic)
return repository_list
# 導(dǎo)出迭代信息
@staticmethod
def ListProjectsmain(
args: List[str],
) -> None:
organization_id = args[0]
client = Sample.create_client(ak, sk)
list_projects_request = devops_20210625_models.ListProjectsRequest(category='Project')
runtime = util_models.RuntimeOptions()
headers = {}
projects = client.list_projects_with_options(organization_id, list_projects_request, headers,runtime).body.projects
projects_info = {}
for project in projects:
identifier = project.identifier
name = project.name
projects_info[name] = identifier
return projects_info
@staticmethod
def ListSprintsmain(
args: List[str],
) -> None:
organization_id = args[0]
space_identifier = args[1]
client = Sample.create_client(ak, sk)
list_sprints_request = devops_20210625_models.ListSprintsRequest(space_identifier=space_identifier,space_type='Project',max_results=200)
runtime = util_models.RuntimeOptions()
headers = {}
sprints = client.list_sprints_with_options(organization_id, list_sprints_request, headers,runtime).body.sprints
sprint_list = []
for sprint in sprints:
sprint_dic = {}
sprint_dic["name"] = sprint.name
sprint_dic["start_date"] = time.strftime('%Y-%m-%d', time.localtime(sprint.start_date / 1000))
sprint_dic["end_date"] = time.strftime('%Y-%m-%d', time.localtime(sprint.end_date / 1000))
sprint_list.append(sprint_dic)
return sprint_list
if __name__ == '__main__':
#獲取代碼組信息
ListRepositoryGroups_list=Sample.ListRepositoryGroups([organization_id,parent_id])
print("代碼組信息:"+str(ListRepositoryGroups_list))
#獲取代碼倉(cāng)庫(kù)信息
repository_list = Sample.ListRepositoriesmain([organization_id, ])
print("代碼倉(cāng)庫(kù)信息:"+str(repository_list))
#獲取迭代信息
while True:
projects_info = Sample.ListProjectsmain([organization_id, ])
print("項(xiàng)目名稱(chēng)和項(xiàng)目ID的對(duì)應(yīng)關(guān)系"+str(projects_info))
identifier = input("請(qǐng)輸入要導(dǎo)出的迭代是哪個(gè)項(xiàng)目的ID(不需要導(dǎo)出請(qǐng)輸入no):")
if identifier=="no":
break
sprint_list = Sample.ListSprintsmain([organization_id, identifier])
print("迭代的信息:"+str(sprint_list))
目標(biāo)賬號(hào)導(dǎo)入代碼
# -*- coding: utf-8 -*-
# This file is auto-generated, don't edit it. Thanks.
from typing import List
from alibabacloud_devops20210625.client import Client as devops20210625Client
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_devops20210625 import models as devops_20210625_models
from alibabacloud_tea_util import models as util_models
from alibabacloud_tea_util.client import Client as UtilClient
import ast
ak="目標(biāo)賬號(hào)AK"
sk="目標(biāo)賬號(hào)SK"
organization_id="目標(biāo)賬號(hào)組織ID"
namespaceId="目標(biāo)賬號(hào)企業(yè)空間ID"
import_account="目標(biāo)賬號(hào)個(gè)人訪問(wèn)令牌名稱(chēng)"
import_token="目標(biāo)賬號(hào)個(gè)人訪問(wèn)令牌token"
class Sample:
def __init__(self):
pass
@staticmethod
def create_client(
access_key_id: str,
access_key_secret: str,
) -> devops20210625Client:
config = open_api_models.Config(access_key_id=ak,access_key_secret=sk)
config.endpoint = f'devops.cn-hangzhou.aliyuncs.com'
return devops20210625Client(config)
#導(dǎo)入代碼組
@staticmethod
def CreateRepositoryGroupmain(
args: List[str],
) -> None:
organization_id = args[0]
name = args[1]
path = args[2]
visibility_level = args[3]
namespaceId = args[4]
description = args[5]
client = Sample.create_client(ak,sk)
create_repository_group_request = devops_20210625_models.CreateRepositoryGroupRequest(organization_id=organization_id,name=name,path=path,visibility_level=visibility_level,parent_id=namespaceId,descriptinotallow=description)
runtime = util_models.RuntimeOptions()
headers = {}
print(client.create_repository_group_with_options(create_repository_group_request, headers, runtime))
#獲取源代碼組ID與目標(biāo)代碼組ID的對(duì)應(yīng)關(guān)系
@staticmethod
def ListRepositoryGroups(
args: List[str],
) -> None:
organization_id = args[0]
parent_id = args[1]
ListRepositoryGroups_list = []
client = Sample.create_client(ak,sk)
list_repository_groups_request = devops_20210625_models.ListRepositoryGroupsRequest(organization_id=organization_id,parent_id=parent_id,page_size=100)
runtime = util_models.RuntimeOptions()
headers = {}
ListRepositoryGroups = client.list_repository_groups_with_options(list_repository_groups_request, headers,runtime).body.result
for RepositoryGroups in ListRepositoryGroups:
ListRepositoryGroup_dic = {}
ListRepositoryGroup_dic["id"] = RepositoryGroups.id
ListRepositoryGroup_dic["name"] = RepositoryGroups.name
ListRepositoryGroup_dic["description"] = RepositoryGroups.description
ListRepositoryGroup_dic["path"] = RepositoryGroups.path
ListRepositoryGroup_dic["visibility_level"] = RepositoryGroups.visibility_level
ListRepositoryGroups_list.append(ListRepositoryGroup_dic)
return ListRepositoryGroups_list
#導(dǎo)入代碼倉(cāng)庫(kù)
@staticmethod
def CreateRepositorymain(
args: List[str],
) -> None:
organization_id = args[0]
name = args[1]
path = args[2]
namespace_id = args[3]
description = args[4]
visibility_level = args[5]
import_url = args[6]
import_account = args[7]
import_token = args[8]
client = Sample.create_client(ak,sk)
create_repository_request = devops_20210625_models.CreateRepositoryRequest(organization_id=organization_id,name=name,path=path,namespace_id=namespace_id,descriptinotallow=description,visibility_level=visibility_level,import_url=import_url,import_account=import_account,import_token=import_token)
runtime = util_models.RuntimeOptions()
headers = {}
print(client.create_repository_with_options(create_repository_request, headers, runtime))
# 得到項(xiàng)目名稱(chēng)和項(xiàng)目ID的對(duì)應(yīng)關(guān)系
@staticmethod
def ListProjectsmain(
args: List[str],
) -> None:
organization_id = args[0]
client = Sample.create_client(ak,sk)
list_projects_request = devops_20210625_models.ListProjectsRequest(category='Project')
runtime = util_models.RuntimeOptions()
headers = {}
projects = client.list_projects_with_options(organization_id, list_projects_request, headers,runtime).body.projects
projects_info = {}
for project in projects:
identifier = project.identifier
name = project.name
projects_info[name] = identifier
return projects_info
#創(chuàng)建迭代
@staticmethod
def CreateSprintmain(
args: List[str],
) -> None:
organization_id=args[0]
identifier=args[1]
FROM_diedai=args[2]
staff_id=args[3]
client = Sample.create_client(ak,sk)
create_sprint_request = devops_20210625_models.CreateSprintRequest(
staff_ids=[staff_id], #ram用戶uid
name=FROM_diedai["name"], #迭代名稱(chēng)
space_identifier=identifier, #項(xiàng)目ID
start_date=FROM_diedai["start_date"], #迭代開(kāi)始時(shí)間
end_date=FROM_diedai["end_date"] #迭代結(jié)束時(shí)間
)
runtime = util_models.RuntimeOptions()
headers = {}
print(client.create_sprint_with_options(organization_id, create_sprint_request, headers, runtime))
if __name__ == '__main__':
# 導(dǎo)入代碼組
FROMListRepositoryGroups_list=ast.literal_eval(input("請(qǐng)輸入源賬號(hào)導(dǎo)出的代碼組列表:"))
for RepositoryGroup in FROMListRepositoryGroups_list:
Sample.CreateRepositoryGroupmain([organization_id,RepositoryGroup["name"],RepositoryGroup["path"],RepositoryGroup["visibility_level"],namespaceId,RepositoryGroup["description"]])
# 獲取源代碼組ID與目標(biāo)代碼組ID的對(duì)應(yīng)關(guān)系
TOListRepositoryGroups_list = Sample.ListRepositoryGroups([organization_id, namespaceId])
FROM_TOListRepositoryGroupID_dic = {}
for FROMListRepositoryGroup in FROMListRepositoryGroups_list:
for TOListRepositoryGroup in TOListRepositoryGroups_list:
if FROMListRepositoryGroup["name"] == TOListRepositoryGroup["name"]:
FROM_TOListRepositoryGroupID_dic[FROMListRepositoryGroup["id"]] = TOListRepositoryGroup["id"]
FROMparent_id=ast.literal_eval(input("請(qǐng)輸入源賬號(hào)的parent_id,也就是namespace_id:"))
FROM_TOListRepositoryGroupID_dic[FROMparent_id]=int(namespaceId)
print("源賬號(hào)代碼組ID與目標(biāo)賬號(hào)代碼組ID的對(duì)應(yīng)關(guān)系:"+str(FROM_TOListRepositoryGroupID_dic))
#導(dǎo)入代碼倉(cāng)庫(kù)
FROMrepository_list=ast.literal_eval(input("請(qǐng)輸入源賬號(hào)導(dǎo)出的代碼倉(cāng)庫(kù)列表:"))
for FROMrepository in FROMrepository_list:
Sample.CreateRepositorymain([organization_id, FROMrepository["name"], FROMrepository["path"],FROM_TOListRepositoryGroupID_dic[FROMrepository["namespace_id"]], FROMrepository["description"],FROMrepository["visibility_level"], FROMrepository["import_url"], import_account, import_token])
# 導(dǎo)入迭代
while True:
projects_info = Sample.ListProjectsmain([organization_id, ])
print("項(xiàng)目名稱(chēng)和項(xiàng)目ID的對(duì)應(yīng)關(guān)系:"+str(projects_info))
identifier = input("請(qǐng)輸入要導(dǎo)入的迭代是哪個(gè)項(xiàng)目的ID(不需要導(dǎo)出請(qǐng)輸入no):")
if identifier=="no":
break
print("用戶ID:{用戶A:用戶A的RAM賬號(hào)UID,用戶B:用戶B的RAM賬號(hào)UID,用戶C:用戶C的RAM賬號(hào)UID}")
print("項(xiàng)目與負(fù)責(zé)人的對(duì)應(yīng)關(guān)系:{'項(xiàng)目A':'負(fù)責(zé)人A','項(xiàng)目B':'負(fù)責(zé)人B','項(xiàng)目C':'負(fù)責(zé)人C'}")
staff_id = input("請(qǐng)輸入要導(dǎo)入迭代負(fù)責(zé)人的RAM賬號(hào)UID:")
FROM_diedai_list = ast.literal_eval(input("請(qǐng)輸入要導(dǎo)入的迭代內(nèi)容列表:"))
for FROM_diedai in FROM_diedai_list:
Sample.CreateSprintmain([organization_id, identifier, FROM_diedai, staff_id])
總 結(jié)
為了確保業(yè)務(wù)連續(xù)性和數(shù)據(jù)完整性,針對(duì)源阿里云賬號(hào)即將停用的情況,我們完成了云效(DevOps平臺(tái))的數(shù)據(jù)遷移工作。由于云效無(wú)法直接通過(guò)實(shí)例遷移至目標(biāo)賬號(hào),我們采取了數(shù)據(jù)導(dǎo)出與導(dǎo)入的方式,確保所有項(xiàng)目、流水線配置及代碼倉(cāng)庫(kù)等數(shù)據(jù)完整無(wú)誤地遷移到目標(biāo)賬號(hào)下。至此,平穩(wěn)順利的完成此次遷移。