今天还是一些CRUD操作,继续分享k8s集群和应用的管理。Kubernetes和应用模型classKubernetesCluster(TimeAbstract):K8s集群配置namemodels。CharField(maxlength100,uniqueTrue,verbosename集群名称)versionmodels。JSONField(defaultdict,verbosename版本,helptext{core:1。14,apiversion:appsv1}core:集群版本apiversion:API版本)descmodels。TextField(nullTrue,blankTrue,verbosename集群描述)configmodels。JSONField(defaultdict,verbosename集群配置)environmentmodels。ManyToManyField(Environment,relatednameenvk8s,blankTrue,verbosename环境)productmodels。ManyToManyField(Product,relatednameproductk8s,blankTrue,verbosename产品)idcmodels。ForeignKey(Idc,blankTrue,nullTrue,ondeletemodels。PROTECT,verbosenameIDC)defstr(self):returnself。nameclassExtMeta:relatedTruedashboardTrueiconk8sclassMeta:defaultpermissions()ordering〔id〕verbosenameK8s集群verbosenamepluralverbosename管理defgetdefaultvalue():return{key:default,value:default}应用部署方式GDEPLOYTYPE((nonk8s,非Kubernetes部署),(docker,Docker部署),(k8s,Kubernetes部署))GONLINECHOICE((0,未上线),(1,已上线),(2,部署中),(3,部署异常),(9,已申请上线))classMicroApp(TimeAbstract):product。project。microappappidmodels。CharField(maxlength250,dbindexTrue,uniqueTrue,verbosename应用ID,helptext应用唯一标识,无需填写)namemodels。CharField(maxlength128,verbosename应用)aliasmodels。CharField(maxlength128,blankTrue,verbosename别名)projectmodels。ForeignKey(Project,ondeletemodels。PROTECT,nullTrue,blankTrue,verbosename项目)creatormodels。ForeignKey(User,ondeletemodels。PROTECT,nullTrue,blankTrue,verbosename创建者,helptext前端不需要传递)repomodels。JSONField(defaultdict,verbosename仓库地址,helptext{name:name,description:,pathwithnamespace:,httpurltorepo:url})targetmodels。JSONField(defaultgetdefaultvalue,verbosenameJAR包配置,helptext默认:default,{default:default,custom:xxxa。war})extramembersmodels。JSONField(defaultgetdefaultextramembers,verbosename额外成员组,helptext{name:自定义成员组1,members:〔1,2,3〕})categorymodels。CharField(maxlength128,blankTrue,nullTrue,verbosename应用分类)templatemodels。JSONField(defaultdict,verbosenameK8sDeployment模板,helptextKubernetesDeployment部署模板配置)languagemodels。CharField(maxlength32,defaultjava,verbosename开发语言)multipleappmodels。BooleanField(defaultFalse,blankTrue,verbosename多应用标志)multipleidsmodels。JSONField(defaultlist,verbosename多应用关联ID列表)dockerfilemodels。JSONField(defaultgetdefaultvalue,verbosenameDockerfile配置,helptext默认:{default:null},可选:{default默认:null,project使用项目Dockerfile:project,custom自定义Dockerfile:})onlinemodels。BooleanField(defaultTrue,blankTrue,verbosename上线下线,helptext应用上线下线状态标记,下线状态的应用禁止发布。)descmodels。TextField(verbosename描述,nullTrue,blankTrue)notifymodels。JSONField(defaultdict,verbosename消息通知)caneditmodels。JSONField(defaultlist,verbosename管理人员,helptext有权限编辑该应用的人员ID格式为数组,如〔1,2〕)isk8smodels。CharField(maxlength8,defaultk8s,choicesGDEPLOYTYPE,verbosename部署方式,helptextf默认k8s,可选:{dict(GDEPLOYTYPE)})modulesmodels。JSONField(defaultlist,verbosename工程模块)defstr(self):return〔s〕s(self。name,self。alias)classExtMeta:relatedTruedashboardTrueiconcomponentclassMeta:defaultpermissions()ordering〔createdtime〕verbosename应用verbosenamepluralverbosename管理classAppInfo(TimeAbstract):uniqtag:product。project。microapp。envuniqtagmodels。CharField(maxlength128,uniqueTrue,verbosename唯一标识,helptext前端留空,无需传值)appmodels。ForeignKey(MicroApp,blankTrue,nullTrue,ondeletemodels。PROTECT,verbosename应用)environmentmodels。ForeignKey(Environment,ondeletemodels。PROTECT,nullTrue,verbosename环境)branchmodels。CharField(maxlength64,blankTrue,nullTrue,verbosename默认构建分支)allowcibranchmodels。JSONField(defaultlist,verbosename允许构建的分支,helptext存储数组格式,具体的分支名;默认〔〕,表示允许所有分支。)allowcdbranchmodels。JSONField(defaultlist,verbosename允许发布的分支,helptext存储数组格式,具体的分支名;默认〔〕,表示允许所有分支。)buildcommandmodels。CharField(maxlength250,blankTrue,nullTrue,verbosename构建命令,helptext根据应用开发语言,从getKey(LANGUAGE)获取数据,取出extra字段的build值)kubernetesmodels。ManyToManyField(KubernetesCluster,relatednamek8sapp,throughKubernetesDeploy,verbosenameK8s集群)hostsmodels。JSONField(defaultlist,verbosename部署主机,helptext部署主机,格式:〔〕)templatemodels。JSONField(defaultdict,verbosenameK8sDeployment模板,helptext继承自当前应用的template字段,数据格式为对象字段说明:type:01,0表示继承应用模板,template为空字典;1表示自定义模板示例:{type:0,template:{}}){0:禁用,1:启用}isenablemodels。SmallIntegerField(default1,verbosename启用,helptext状态{0:禁用,1:启用},默认值为1)descmodels。TextField(verbosename描述,nullTrue,blankTrue)caneditmodels。JSONField(defaultlist,verbosename管理人员,helptext有权限编辑该应用的人员ID格式为数组,如〔1,2〕)onlinemodels。SmallIntegerField(default0,choicesGONLINECHOICE,verbosename是否上线,helptextf默认为0,即未上线可选项:{GONLINECHOICE})defstr(self):returnself。uniqtagpropertydefnamespace(self):returnf{self。environment。name。replace(,)}{self。app。project。name。replace(,)}。lower()propertydefjenkinsjobname(self):try:jobnamef{self。environment。name}{self。app。category。split(。)〔1〕}{self。app。project。name}{self。app。name。split(。)〔1〕}。lower()exceptAppInfo。DoesNotExist:jobnamereturnjobnameclassExtMeta:relatedTruedashboardTrueclassMeta:defaultpermissions()ordering〔updatetime,id〕verbosename应用模块verbosenamepluralverbosename管理classKubernetesDeploy(TimeAbstract):appinfomodels。ForeignKey(AppInfo,relatednameappinfo,nullTrue,ondeletemodels。CASCADE)kubernetesmodels。ForeignKey(KubernetesCluster,relatednameappk8s,nullTrue,ondeletemodels。CASCADE)onlinemodels。SmallIntegerField(default0,choicesGONLINECHOICE,verbosename是否上线,helptextf默认为0,即未上线可选项:{GONLINECHOICE})versionmodels。CharField(maxlength250,blankTrue,nullTrue,verbosename当前版本)defstr(self):returnss(self。appinfo。app。appid,self。kubernetes。name)classMeta:defaultpermissions()classDataDict(CommonParent):keymodels。CharField(maxlength80,uniqueTrue,verbosename键)valuemodels。CharField(maxlength80,verbosename值)extramodels。TextField(nullTrue,blankTrue,default,verbosename额外参数)descmodels。CharField(maxlength255,blankTrue,nullTrue,verbosename备注)defstr(self):returnself。valueclassMeta:defaultpermissions()verbosename字典verbosenamepluralverbosename管理编写序列化器importjsonfromtypingimportListfromrestframeworkimportserializersfromdjango。dbimporttransactionfromdjango。contrib。auth。modelsimportUserfromcmdb。modelsimportProduct,Project,Environment,KubernetesCluster,MicroApp,AppInfo,KubernetesDeployclassKubernetesClusterListSerializers(serializers。ModelSerializer):configserializers。SerializerMethodField()defgetconfig(self,instance):returnjson。loads(instance。config)classMeta:modelKubernetesClusterfieldsallclassKubernetesClusterSerializers(serializers。ModelSerializer):classMeta:modelKubernetesClusterfieldsallclassMicroAppListSerializers(serializers。ModelSerializer):projectinfoserializers。SerializerMethodField()appinfoserializers。SerializerMethodField()creatorinfoserializers。SerializerMethodField()extrateaminfoserializers。SerializerMethodField()defgetprojectinfo(self,instance):projectinstance。projectreturn{project:{id:project。id,alias:project。alias},product:{id:project。product。id,alias:project。product。alias}}defgetappinfo(self,instance):return〔{id:i。id,envalias:i。environment。alias,env:{name:i。environment。name,id:i。environment。id},online:i。online}foriininstance。appinfoset。all()〕defgetcreatorinfo(self,instance):try:return{id:instance。creator。id,firstname:instance。creator。firstname,username:instance。creator。username}exceptBaseExceptionase:return{id:,firstname:,username:}defgetextrateaminfo(self,instance):data{}fork,vininstance。extramembers。items():data〔k〕〔{id:i。id,name:i。name,firstname:i。firstname,username:i。username}foriinUser。objects。filter(idinv)〕returndataclassMeta:modelMicroAppfieldsallclassMicroAppSerializers(serializers。ModelSerializer):classMeta:modelMicroAppfieldsallreadonlyfields(appid,)staticmethoddefperformextendsave(validateddata):defdefaultvalue(fields:List):forfieldinfields:ifvalidateddata。get(field):ifvalidateddata〔field〕。get(key)!custom:validateddata〔field〕〔value〕validateddata〔field〕〔key〕returnvalidateddatavalidateddatadefaultvalue(〔dockerfile,target〕)validateddata〔appid〕f{validateddata〔project〕。product。name}。{validateddata〔project〕。name}。{validateddata〔name〕}returnvalidateddatadefcreate(self,validateddata):instanceMicroApp。objects。create(canedit〔validateddata〔creator〕。id〕,self。performextendsave(validateddata))returninstancedefupdate(self,instance,validateddata):returnsuper()。update(instance,self。performextendsave(validateddata))classAppInfoListSerializers(serializers。ModelSerializer):appMicroAppSerializers()kubernetesinfoserializers。SerializerMethodField()defgetkubernetesinfo(self,instance):serializerKubernetesDeploySerializers(dataKubernetesDeploy。objects。filter(appinfoinstance。id),manyTrue)serializer。isvalid()returnserializer。dataclassMeta:modelAppInfofieldsallclassAppInfoSerializers(serializers。ModelSerializer):classMeta:modelAppInfofieldsalldefperformextendsave(self,validateddata,args,kwargs):ifvalidateddata。get(app,None)andvalidateddata。get(environment,None):validateddata〔uniqtag〕f{validateddata〔app〕。appid}。{validateddata〔environment〕。name。split()〔1〕。lower()}ifkwargs。get(instance,None):kubernetesself。initialdata。get(kubernetes)bulk〔〕forkidinkubernetes:ksKubernetesCluster。objects。get(idkid)bulk。append(KubernetesDeploy(appinfokwargs〔instance〕,kubernetesks))KubernetesDeploy。objects。bulkcreate(bulk,ignoreconflictsTrue)returnvalidateddatatransaction。atomicdefcreate(self,validateddata):instanceAppInfo。objects。create(self。performextendsave(validateddata))ifkubernetesinself。initialdata:self。performextendsave(validateddata,{instance:instance})returninstancetransaction。atomicdefupdate(self,instance,validateddata):KubernetesDeploy。objects。filter(appinfoinstance)。delete()instance。dict。update(self。performextendsave(validateddata,{instance:instance}))instance。save()returninstance编写视图fromrestframework。responseimportResponsefromrestframework。decoratorsimportactionfromdjango。dbimporttransactionfromcmdb。modelsimportProduct,Project,Environment,KubernetesCluster,MicroApp,AppInfofromcmdb。serializersimportAppInfoListSerializers,AppInfoSerializers,KubernetesClusterListSerializers,KubernetesClusterSerializers,MicroAppListSerializers,MicroAppSerializersfromcommon。extends。decoratorsimportcmdbappuniquecheckclassKubernetesClusterViewSet(viewsets。ModelViewSet):Kubernetes集群视图Kubernetes集群权限{:(k8sclusterall,k8s集群管理)},{get:(k8sclusterlist,查看k8s集群)},{post:(k8sclustercreate,创建k8s集群)},{put:(k8sclusteredit,编辑k8s集群)},{patch:(k8sclusteredit,编辑k8s集群)},{delete:(k8sclusterdelete,删除k8s集群)}permsmap({:(admin,管理员)},{:(k8sclusterall,k8s集群管理)},{get:(k8sclusterlist,查看k8s集群)},{post:(k8sclustercreate,创建k8s集群)},{put:(k8sclusteredit,编辑k8s集群)},{patch:(k8sclusteredit,编辑k8s集群)},{delete:(k8sclusterdelete,删除k8s集群)})querysetKubernetesCluster。objects。all()serializerclassKubernetesClusterSerializersdefgetserializerclass(self):ifself。actionin〔list,retrieve〕:returnKubernetesClusterListSerializersreturnKubernetesClusterSerializersclassMicroAppViewSet(viewsets。ModelViewSet):项目应用视图项目应用权限{:(microappall,应用管理)},{get:(microapplist,查看应用)},{post:(microappcreate,创建应用)},{put:(microappedit,编辑应用)},{patch:(microappedit,编辑应用)},{delete:(microappdelete,删除应用)}permsmap({:(admin,管理员)},{:(microappall,应用管理)},{get:(microapplist,查看应用)},{post:(microappcreate,创建应用)},{put:(microappedit,编辑应用)},{patch:(microappedit,编辑应用)},{delete:(microappdelete,删除应用)})querysetMicroApp。objects。all()serializerclassMicroAppSerializersdefgetserializerclass(self):ifself。actionin〔list,retrieve〕:returnMicroAppListSerializersreturnMicroAppSerializerscmdbappuniquecheck()defcreate(self,request,args,kwargs):创建应用提交参数创建:{}try:request。data〔name〕request。data〔name〕。strip()。replace(,)exceptBaseExceptionase:logger。error(exception,str(e))serializerself。getserializer(datarequest。data)ifnotserializer。isvalid():returnResponse({code:40000,status:failed,message:serializer。errors})try:self。performcreate(serializer)exceptBaseExceptionase:logger。error(e)returnResponse({code:50000,status:failed,message:str(e)})data{data:serializer。data,status:success,code:20000}returnResponse(data)transaction。atomicdefperformcreate(self,serializer):serializer。save(creatorself。request。user)action(methods〔POST〕,urlpathrelated,detailFalse)defapprelated(self,request):应用关联传递参数:ids:待关联应用id数组target:目标应用idtry:targetrequest。data。get(target,None)idsrequest。data。get(ids,None)iftarget:instanceself。queryset。get(idtarget)ids。extend(instance。multipleids)self。queryset。filter(idinlist(set(ids)))。update(multipleappTrue,multipleidslist(set(ids)))returnResponse({code:20000,data:应用关联成功。})exceptBaseExceptionase:logger。error(err,e)returnResponse({code:50000,data:关联应用异常,请联系管理员!})action(methods〔POST〕,urlpathunrelated,detailFalse)defappunrelated(self,request):取消应用关联传递参数:id:应用idtry:instanceself。queryset。filter(idrequest。data。get(id))获取关联应用ID列表idsinstance〔0〕。multipleidsids。remove(instance〔0〕。id)iflen(ids)1:如果关联应用只剩下一个,则一起取消关联self。queryset。filter(idininstance〔0〕。multipleids)。update(multipleappFalse,multipleids〔〕)else:更新其它应用的关联应用IDself。queryset。filter(idinids)。update(multipleidsids)取消当前实例应用关联instance。update(multipleappFalse,multipleids〔〕)returnResponse({code:20000,data:应用取消关联成功。})exceptBaseExceptionase:returnResponse({code:50000,data:关联应用异常,请联系管理员!})classAppInfoViewSet(viewsets。ModelViewSet):项目应用服务服务对应着应用的不同环境,即应用每个环境创建一个对应的服务项目应用服务权限{:(microappall,应用管理)},{get:(microapplist,查看应用)},{post:(microappcreate,创建应用)},{put:(microappedit,编辑应用)},{patch:(microappedit,编辑应用)},{delete:(microappdelete,删除应用)}permsmap({:(admin,管理员)},{:(microappall,应用管理)},{get:(microapplist,查看应用)},{post:(microappcreate,创建应用)},{put:(microappedit,编辑应用)},{patch:(microappedit,编辑应用)},{delete:(microappdelete,删除应用)})querysetAppInfo。objects。all()serializerclassAppInfoSerializersdefgetserializerclass(self):ifself。actionin〔list,retrieve〕:returnAppInfoListSerializersreturnAppInfoSerializersdefcreate(self,request,args,kwargs):request。data〔uniqtag〕defaultserializerself。getserializer(datarequest。data)serializer。isvalid(raiseexceptionTrue)self。performcreate(serializer)dataserializer。datadata{code:20000,status:200,data:data}returnResponse(data)添加装饰器 细心的朋友应该可以发现,在视图里文件里导入一个自己编写的装饰器 fromcommon。extends。decoratorsimportcmdbappuniquecheck 我们先在项目根下创建common目录(venv)ydevopsbackendmkdirpcommonextends(venv)ydevopsbackendtouchcommonextendsdecorators。py 然后编写装饰器cmdbappuniquecheckfromfunctoolsimportwrapsfromrestframework。responseimportResponsefromcmdb。modelsimportMicroAppdefcmdbappuniquecheck():应用唯一性检查appid:{product。name}。{app。name}defcheckapp(product,name):try:ifMicroApp。objects。filter(projectproductidproduct,namename)。exists():存在应用returnTrueexceptBaseExceptionase:passreturnFalsedefdecorator(func):wraps(func)defwrapper(self,request,args,kwargs):ifcheckapp(request。data〔product〕,request。data〔name〕):returnResponse({code:50000,message:f该产品下已存在〔{request。data〔name〕}〕同名应用。})returnfunc(self,request,args,kwargs)returnwrapperreturndecorator添加路由fromdjango。contribimportadminfromdjango。urlsimportpath,includefromcmdb。view。viewcmdbimportAppInfoViewSet,KubernetesClusterViewSet,MicroAppViewSetfromrestframework。routersimportDefaultRouterfromcmdb。viewsimportRegionViewSet,IdcViewSet,ProductViewSet,ProjectViewSet,EnvironmentViewSetrouterDefaultRouter()router。register(region,RegionViewSet)router。register(assetidc,IdcViewSet)router。register(product,ProductViewSet)router。register(project,ProjectViewSet)router。register(environment,EnvironmentViewSet)router。register(appservice,AppInfoViewSet)router。register(app,MicroAppViewSet)router。register(kubernetes,KubernetesClusterViewSet)urlpatterns〔path(admin,admin。site。urls),path(api,include(router。urls)),〕迁移数据表(venv)ydevopsbackendpythonmanage。pymakemigrationsMigrationsforcmdb:appscmdbmigrations0003appinfokubernetesclustermicroappkubernetesdeployandmore。pyCreatemodelAppInfoCreatemodelKubernetesClusterCreatemodelMicroAppCreatemodelKubernetesDeployAddfieldapptoappinfoAddfieldenvironmenttoappinfoAddfieldkubernetestoappinfo(venv)ydevopsbackendpythonmanage。pymakemigrationsMigrationsforcmdb:appscmdbmigrations0004datadict。pyCreatemodelDataDict(venv)ydevopsbackendpythonmanage。pymigrateOperationstoperform:Applyallmigrations:admin,auth,cmdb,contenttypes,sessionsRunningmigrations:Applyingcmdb。0003appinfokubernetesclustermicroappkubernetesdeployandmore。。。OKApplyingcmdb。0004datadict。。。OK运行项目(venv)ydevopsbackendpythonmanage。pyrunserver0。0。0。0:9000WatchingforfilechangeswithStatReloaderPerformingsystemchecks。。。Systemcheckidentifiednoissues(0silenced)。March23,202322:13:47Djangoversion4。1。7,usingsettingsdevopsbackend。settingsStartingdevelopmentserverathttp:0。0。0。0:9000QuittheserverwithCONTROLC。 打开浏览器访问http:localhost:9000api就可以看到目前已完成的接口了