diff --git a/core/controller/cluster/controller.go b/core/controller/cluster/controller.go index 730b57a2a..1eaa58b43 100644 --- a/core/controller/cluster/controller.go +++ b/core/controller/cluster/controller.go @@ -17,6 +17,7 @@ package cluster import ( "context" + templatemanager "github.com/horizoncd/horizon/pkg/template/manager" "k8s.io/apimachinery/pkg/runtime/schema" "github.com/horizoncd/horizon/core/config" @@ -133,6 +134,7 @@ type controller struct { applicationMgr appmanager.Manager autoFreeSvc *service.AutoFreeSVC applicationSvc applicationservice.Service + templateMgr templatemanager.Manager templateReleaseMgr trmanager.Manager templateSchemaGetter templateschema.Getter outputGetter output.Getter @@ -172,6 +174,7 @@ func NewController(config *config.Config, param *param.Param) Controller { k8sutil: param.K8sUtil, applicationMgr: param.ApplicationMgr, applicationSvc: param.ApplicationSvc, + templateMgr: param.TemplateMgr, templateReleaseMgr: param.TemplateReleaseMgr, templateSchemaGetter: param.TemplateSchemaGetter, autoFreeSvc: param.AutoFreeSvc, diff --git a/core/controller/cluster/controller_basic_v2.go b/core/controller/cluster/controller_basic_v2.go index d804458e7..0b6028bc9 100644 --- a/core/controller/cluster/controller_basic_v2.go +++ b/core/controller/cluster/controller_basic_v2.go @@ -115,11 +115,9 @@ func (c *controller) CreateClusterV2(ctx context.Context, if err := validateClusterName(params.Name); err != nil { return nil, err } - if params.Git != nil { - if params.Git.URL != "" { - if err := validate.CheckGitURL(params.Git.URL); err != nil { - return nil, err - } + if params.Git != nil && params.Git.URL != "" { + if err := validate.CheckGitURL(params.Git.URL); err != nil { + return nil, err } } if params.Image != nil { @@ -165,7 +163,11 @@ func (c *controller) CreateClusterV2(ctx context.Context, return nil, err } - // 7. get templateRelease + // 7. get template and templateRelease + template, err := c.templateMgr.GetByName(ctx, buildTemplateInfo.TemplateInfo.Name) + if err != nil { + return nil, err + } tr, err := c.templateReleaseMgr.GetByTemplateNameAndRelease(ctx, buildTemplateInfo.TemplateInfo.Name, buildTemplateInfo.TemplateInfo.Release) if err != nil { @@ -174,7 +176,7 @@ func (c *controller) CreateClusterV2(ctx context.Context, // 8. customize db infos cluster, tags := params.toClusterModel(application, - envEntity, buildTemplateInfo, expireSeconds) + envEntity, buildTemplateInfo, template, expireSeconds) // 9. update db and tags clusterResp, err := c.clusterMgr.Create(ctx, cluster, tags, params.ExtraMembers) diff --git a/core/controller/cluster/controller_build_deploy.go b/core/controller/cluster/controller_build_deploy.go index 730429dc5..334675963 100644 --- a/core/controller/cluster/controller_build_deploy.go +++ b/core/controller/cluster/controller_build_deploy.go @@ -203,12 +203,15 @@ func assembleImageURL(regionEntity *regionmodels.RegionEntity, normalizedBranch := strings.Join(pinyin.LazyPinyin(branch, args), "") normalizedBranch = regexp.MustCompile(`[^a-zA-Z0-9_.-]`).ReplaceAllString(normalizedBranch, "_") - if len(commit) > 8 { - commit = commit[:8] - } - + normalizedCommit := func(commit string) string { + res := commit + if len(res) > 8 { + res = res[:8] + } + return regexp.MustCompile(`[^a-zA-Z0-9_.-]`).ReplaceAllString(res, "_") + }(commit) return path.Join(domain, regionEntity.Registry.Path, application, - fmt.Sprintf("%v:%v-%v-%v", cluster, normalizedBranch, commit, timeStr)) + fmt.Sprintf("%v:%v-%v-%v", cluster, normalizedBranch, normalizedCommit, timeStr)) } func (c *controller) GetDiff(ctx context.Context, clusterID uint, refType, ref string) (_ *GetDiffResponse, err error) { diff --git a/core/controller/cluster/controller_build_deploy_test.go b/core/controller/cluster/controller_build_deploy_test.go index e166caba4..574f9ecff 100644 --- a/core/controller/cluster/controller_build_deploy_test.go +++ b/core/controller/cluster/controller_build_deploy_test.go @@ -101,6 +101,23 @@ func testImageURL(t *testing.T) { }, want: "harbor.com/path/app/cluster:ceshi_zhongguohello_-117651f0", }, + { + name: "normal5", + args: args{ + regionEntity: ®ionmodels.RegionEntity{ + Registry: ®istrymodels.Registry{ + Path: "path", + Server: "https://harbor.com", + }, + }, + application: "app", + cluster: "cluster", + branch: "fix/bug", + // commit will be branch name if the repo could not be fetched + commit: "fix/bug", + }, + want: "harbor.com/path/app/cluster:fix_bug-fix_bug", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/core/controller/cluster/controller_test.go b/core/controller/cluster/controller_test.go index 3b80756fa..93dd5f67c 100644 --- a/core/controller/cluster/controller_test.go +++ b/core/controller/cluster/controller_test.go @@ -555,6 +555,7 @@ func test(t *testing.T) { }, nil).AnyTimes() appMgr := manager.ApplicationMgr + templateMgr := manager.TemplateMgr trMgr := manager.TemplateReleaseMgr envMgr := manager.EnvMgr regionMgr := manager.RegionMgr @@ -636,6 +637,7 @@ func test(t *testing.T) { cd: cd, k8sutil: k8sutil, applicationMgr: appMgr, + templateMgr: templateMgr, templateReleaseMgr: trMgr, templateSchemaGetter: templateSchemaGetter, envMgr: envMgr, @@ -1302,6 +1304,7 @@ func testV2(t *testing.T) { }, nil).Times(1) appMgr := manager.ApplicationMgr + templateMgr := manager.TemplateMgr trMgr := manager.TemplateReleaseMgr envMgr := manager.EnvMgr regionMgr := manager.RegionMgr @@ -1378,6 +1381,16 @@ func testV2(t *testing.T) { }, nil) assert.Nil(t, err) + _, err = templateMgr.Create(ctx, &templatemodels.Template{ + Name: "rollout", + ChartName: "rollout", + GroupID: 0, + OnlyOwner: nil, + WithoutCI: true, + Type: templatemodels.TemplateTypeWorkload, + }) + assert.Nil(t, err) + tr, err := trMgr.Create(ctx, &trmodels.TemplateRelease{ TemplateName: templateName, Name: "v1.0.0", @@ -1391,6 +1404,7 @@ func testV2(t *testing.T) { clusterMgr: manager.ClusterMgr, clusterGitRepo: clusterGitRepo, applicationMgr: appMgr, + templateMgr: templateMgr, templateReleaseMgr: trMgr, templateSchemaGetter: templateSchemaGetter, envMgr: envMgr, @@ -1615,6 +1629,7 @@ func testUpgrade(t *testing.T) { clusterMgr: manager.ClusterMgr, clusterGitRepo: clusterGitRepo, applicationMgr: appMgr, + templateMgr: manager.TemplateMgr, templateReleaseMgr: trMgr, templateSchemaGetter: templateSchemaGetter, envMgr: envMgr, diff --git a/core/controller/cluster/models_basic_v2.go b/core/controller/cluster/models_basic_v2.go index bba0911fb..cc9c978ec 100644 --- a/core/controller/cluster/models_basic_v2.go +++ b/core/controller/cluster/models_basic_v2.go @@ -23,6 +23,7 @@ import ( "github.com/horizoncd/horizon/pkg/cluster/models" envregionmodels "github.com/horizoncd/horizon/pkg/environmentregion/models" tagmodels "github.com/horizoncd/horizon/pkg/tag/models" + templatemodels "github.com/horizoncd/horizon/pkg/template/models" ) type CreateClusterRequestV2 struct { @@ -53,7 +54,7 @@ type CreateClusterParamsV2 struct { func (r *CreateClusterParamsV2) toClusterModel(application *appmodels.Application, er *envregionmodels.EnvironmentRegion, info *BuildTemplateInfo, - expireSeconds uint) (*models.Cluster, []*tagmodels.Tag) { + template *templatemodels.Template, expireSeconds uint) (*models.Cluster, []*tagmodels.Tag) { cluster := &models.Cluster{ ApplicationID: application.ID, Name: r.Name, @@ -65,41 +66,58 @@ func (r *CreateClusterParamsV2) toClusterModel(application *appmodels.Applicatio TemplateRelease: info.TemplateInfo.Release, Status: common.ClusterStatusCreating, } - if cluster.Template == application.Template { - cluster.GitURL = func() string { - if r.Git == nil { - return application.GitURL + if template.Type == templatemodels.TemplateTypeWorkload { + // only workload template need git info and image info + if application.GitURL == "" && r.Git != nil { + // application's git info is miss, use git info from request directly + cluster.GitURL = r.Git.URL + cluster.GitSubfolder = r.Git.Subfolder + cluster.GitRef = r.Git.Ref() + cluster.GitRefType = r.Git.RefType() + } else if application.GitURL != "" && r.Git != nil { + if r.Git.URL != application.GitURL && r.Git.URL != "" { + // git url is not equal, do not inherit git info from application + cluster.GitURL = r.Git.URL + cluster.GitSubfolder = r.Git.Subfolder + cluster.GitRef = r.Git.Ref() + cluster.GitRefType = r.Git.RefType() + } else { + // use git info from cluster, default to application + cluster.GitURL = application.GitURL + cluster.GitSubfolder = func() string { + if r.Git.Subfolder != "" { + return r.Git.Subfolder + } + return application.GitSubfolder + }() + cluster.GitRef = func() string { + if r.Git.Ref() != "" { + return r.Git.Ref() + } + return application.GitRef + }() + cluster.GitRefType = func() string { + if r.Git.RefType() != "" { + return r.Git.RefType() + } + return application.GitRefType + }() } - if r.Git.URL == "" && application.GitURL != "" { - return application.GitURL - } - // if URL is empty string, this means this cluster not depends on build from git - return r.Git.URL - }() - cluster.GitSubfolder = func() string { - if r.Git == nil || r.Git.Subfolder == "" { - return application.GitSubfolder - } - return r.Git.Subfolder - }() - cluster.GitRef = func() string { - if r.Git == nil { - return application.GitRef - } - return r.Git.Ref() - }() - cluster.GitRefType = func() string { - if r.Git == nil { - return application.GitRefType - } - return r.Git.RefType() - }() - cluster.Image = func() string { - if r.Image == nil { + } else if r.Image != nil { + cluster.Image = func() string { + if *r.Image != "" { + return *r.Image + } return application.Image - } - return *r.Image - }() + }() + } else { + // git info and image info are both empty, use them from application + cluster.GitURL = application.GitURL + cluster.GitSubfolder = application.GitSubfolder + cluster.GitRef = application.GitRef + cluster.GitRefType = application.GitRefType + cluster.Image = application.Image + } } tags := make([]*tagmodels.Tag, 0) for _, tag := range r.Tags { diff --git a/core/controller/cluster/models_basic_v2_test.go b/core/controller/cluster/models_basic_v2_test.go new file mode 100644 index 000000000..ab649cd73 --- /dev/null +++ b/core/controller/cluster/models_basic_v2_test.go @@ -0,0 +1,234 @@ +package cluster + +import ( + "testing" + + appmodels "github.com/horizoncd/horizon/pkg/application/models" + codemodels "github.com/horizoncd/horizon/pkg/cluster/code" + "github.com/horizoncd/horizon/pkg/cluster/models" + envregionmodels "github.com/horizoncd/horizon/pkg/environmentregion/models" + templatemodels "github.com/horizoncd/horizon/pkg/template/models" + "github.com/horizoncd/horizon/pkg/util/common" + "github.com/stretchr/testify/assert" +) + +func TestModelsBasicV2(t *testing.T) { + type args struct { + params *CreateClusterParamsV2 + application *appmodels.Application + er *envregionmodels.EnvironmentRegion + info *BuildTemplateInfo + template *templatemodels.Template + expireSeconds uint + } + gitInstanceParams := &CreateClusterParamsV2{ + CreateClusterRequestV2: &CreateClusterRequestV2{ + Name: "git-instance", + Description: "git instance", + Git: &codemodels.Git{ + URL: "ssh://git@localhost:7999/horizon/instance.git", + Subfolder: "subfolder", + Branch: "main", + }, + }, + } + imageInstanceParams := &CreateClusterParamsV2{ + CreateClusterRequestV2: &CreateClusterRequestV2{ + Name: "image-instance", + Description: "image instance", + Image: common.StringPtr("horizon/instance:v1.0"), + }, + } + chartInstanceParams := &CreateClusterParamsV2{ + CreateClusterRequestV2: &CreateClusterRequestV2{ + Name: "chart-instance", + Description: "chart instance", + }, + } + gitApplication := &appmodels.Application{ + GitURL: "ssh://git@localhost:7999/horizon/application.git", + GitSubfolder: "", + GitRef: "master", + GitRefType: codemodels.GitRefTypeBranch, + } + imageApplication := &appmodels.Application{ + Image: "horizon/application:v1.0", + } + er := &envregionmodels.EnvironmentRegion{} + rolloutBuildInfo := &BuildTemplateInfo{ + TemplateInfo: &codemodels.TemplateInfo{ + Name: "rollout", + Release: "v1.0", + }, + } + workloadTemplate := &templatemodels.Template{ + Type: templatemodels.TemplateTypeWorkload, + } + chartBuildInfo := &BuildTemplateInfo{ + TemplateInfo: &codemodels.TemplateInfo{ + Name: "chart", + Release: "v1.0", + }, + } + chartTemplate := &templatemodels.Template{ + Type: "chart", + } + tests := []struct { + name string + args args + desired *models.Cluster + }{ + { + name: "create git instance in git application", + args: args{ + params: gitInstanceParams, + application: gitApplication, + er: er, + info: rolloutBuildInfo, + template: workloadTemplate, + expireSeconds: 0, + }, + desired: &models.Cluster{ + GitURL: "ssh://git@localhost:7999/horizon/instance.git", + GitSubfolder: "subfolder", + GitRef: "main", + GitRefType: codemodels.GitRefTypeBranch, + }, + }, + { + name: "create git instance in git application and use default info", + args: args{ + params: chartInstanceParams, + application: gitApplication, + er: er, + info: rolloutBuildInfo, + template: workloadTemplate, + expireSeconds: 0, + }, + desired: &models.Cluster{ + GitURL: "ssh://git@localhost:7999/horizon/application.git", + GitSubfolder: "", + GitRef: "master", + GitRefType: codemodels.GitRefTypeBranch, + }, + }, + { + name: "create git instance in git application and only specify git branch", + args: args{ + params: &CreateClusterParamsV2{ + CreateClusterRequestV2: &CreateClusterRequestV2{ + Name: "git-instance", + Description: "git instance", + Git: &codemodels.Git{ + Branch: "feature", + }, + }, + }, + application: gitApplication, + er: er, + info: rolloutBuildInfo, + template: workloadTemplate, + expireSeconds: 0, + }, + desired: &models.Cluster{ + GitURL: "ssh://git@localhost:7999/horizon/application.git", + GitSubfolder: "", + GitRef: "feature", + GitRefType: codemodels.GitRefTypeBranch, + }, + }, + { + name: "create image instance in git application", + args: args{ + params: imageInstanceParams, + application: gitApplication, + er: er, + info: rolloutBuildInfo, + template: workloadTemplate, + expireSeconds: 0, + }, + desired: &models.Cluster{ + Image: "horizon/instance:v1.0", + }, + }, + { + name: "create chart instance in git application", + args: args{ + params: chartInstanceParams, + application: gitApplication, + er: er, + info: chartBuildInfo, + template: chartTemplate, + expireSeconds: 0, + }, + desired: &models.Cluster{}, + }, + { + name: "create git instance in image application", + args: args{ + params: gitInstanceParams, + application: imageApplication, + er: er, + info: rolloutBuildInfo, + template: workloadTemplate, + expireSeconds: 0, + }, + desired: &models.Cluster{ + GitURL: "ssh://git@localhost:7999/horizon/instance.git", + GitSubfolder: "subfolder", + GitRef: "main", + GitRefType: codemodels.GitRefTypeBranch, + }, + }, + { + name: "create image instance in image application", + args: args{ + params: imageInstanceParams, + application: imageApplication, + er: er, + info: rolloutBuildInfo, + template: workloadTemplate, + expireSeconds: 0, + }, + desired: &models.Cluster{ + Image: "horizon/instance:v1.0", + }, + }, + { + name: "create image instance in image application, use default info", + args: args{ + params: chartInstanceParams, + application: imageApplication, + er: er, + info: rolloutBuildInfo, + template: workloadTemplate, + expireSeconds: 0, + }, + desired: &models.Cluster{ + Image: "horizon/application:v1.0", + }, + }, + { + name: "create chart instance in image application", + args: args{ + params: chartInstanceParams, + application: imageApplication, + er: er, + info: chartBuildInfo, + template: chartTemplate, + expireSeconds: 0, + }, + desired: &models.Cluster{}, + }, + } + for _, test := range tests { + t.Logf("toClusterModel test: %s", test.name) + cluster, _ := test.args.params.toClusterModel(test.args.application, test.args.er, + test.args.info, test.args.template, test.args.expireSeconds) + assert.Equal(t, test.desired.GitURL, cluster.GitURL) + assert.Equal(t, test.desired.GitSubfolder, cluster.GitSubfolder) + assert.Equal(t, test.desired.GitRef, cluster.GitRef) + assert.Equal(t, test.desired.GitRefType, cluster.GitRefType) + assert.Equal(t, test.desired.Image, cluster.Image) + } +} diff --git a/pkg/template/models/template.go b/pkg/template/models/template.go index a315cbece..2a13c580d 100644 --- a/pkg/template/models/template.go +++ b/pkg/template/models/template.go @@ -18,6 +18,8 @@ import ( "github.com/horizoncd/horizon/pkg/server/global" ) +const TemplateTypeWorkload = "workload" + type Template struct { global.Model