horizoncd/horizon

View on GitHub
core/controller/template/controller.go

Summary

Maintainability
F
4 days
Test Coverage
F
55%
File `controller.go` has 774 lines of code (exceeds 500 allowed). Consider refactoring.
// Copyright © 2023 Horizoncd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
 
package template
 
import (
"bytes"
"context"
"fmt"
"sort"
"strconv"
"strings"
"sync"
"time"
 
membermanager "github.com/horizoncd/horizon/pkg/member/manager"
"helm.sh/helm/v3/pkg/chart/loader"
 
"github.com/horizoncd/horizon/core/common"
herrors "github.com/horizoncd/horizon/core/errors"
"github.com/horizoncd/horizon/lib/q"
hctx "github.com/horizoncd/horizon/pkg/context"
perror "github.com/horizoncd/horizon/pkg/errors"
"github.com/horizoncd/horizon/pkg/git"
gmanager "github.com/horizoncd/horizon/pkg/group/manager"
groupModels "github.com/horizoncd/horizon/pkg/group/models"
"github.com/horizoncd/horizon/pkg/group/service"
membermodels "github.com/horizoncd/horizon/pkg/member/models"
memberservice "github.com/horizoncd/horizon/pkg/member/service"
"github.com/horizoncd/horizon/pkg/param"
"github.com/horizoncd/horizon/pkg/rbac/role"
tmanager "github.com/horizoncd/horizon/pkg/template/manager"
"github.com/horizoncd/horizon/pkg/template/models"
trmanager "github.com/horizoncd/horizon/pkg/templaterelease/manager"
trmodels "github.com/horizoncd/horizon/pkg/templaterelease/models"
"github.com/horizoncd/horizon/pkg/templaterelease/schema"
"github.com/horizoncd/horizon/pkg/templaterepo"
"github.com/horizoncd/horizon/pkg/util/permission"
"github.com/horizoncd/horizon/pkg/util/wlog"
)
 
type Controller interface {
// ListV2 lists all template available
ListV2(ctx context.Context, query *q.Query, withFullPath bool) (Templates, error)
// ListTemplate list all template available
ListTemplate(ctx context.Context) (Templates, error)
// ListTemplateRelease list all releases of the specified template
ListTemplateRelease(ctx context.Context, templateName string) (Releases, error)
// GetTemplateSchema get schema for a template release
GetTemplateSchema(ctx context.Context, releaseID uint, params map[string]string) (*Schemas, error)
// ListTemplateByGroupID lists all template available by group ID
ListTemplateByGroupID(ctx context.Context, groupID uint, withoutCI bool) (Templates, error)
// ListTemplateReleaseByTemplateID lists all releases of the specified template
ListTemplateReleaseByTemplateID(ctx context.Context, templateID uint) (Releases, error)
// CreateTemplate creates a template with a release under a group
CreateTemplate(ctx context.Context, groupID uint, request CreateTemplateRequest) (*Template, error)
// CreateRelease downloads template archive and push it to chatmusuem,
// then creates a template release in database.
CreateRelease(ctx context.Context, templateID uint, request CreateReleaseRequest) (*Release, error)
// GetTemplate gets template by templateID
GetTemplate(ctx context.Context, templateID uint) (*Template, error)
// GetRelease gets release by releaseID
GetRelease(ctx context.Context, releaseID uint) (*Release, error)
// DeleteTemplate deletes a template by ID
DeleteTemplate(ctx context.Context, templateID uint) error
// DeleteRelease deletes a template release by ID
DeleteRelease(ctx context.Context, releaseID uint) error
// UpdateTemplate deletes a template by ID
UpdateTemplate(ctx context.Context, templateID uint, request UpdateTemplateRequest) error
// UpdateRelease deletes a template release by ID
UpdateRelease(ctx context.Context, releaseID uint, request UpdateReleaseRequest) error
// SyncReleaseToRepo downloads template from gitlab, packages the template and uploads it to chart repo
SyncReleaseToRepo(ctx context.Context, releaseID uint) error
}
 
`controller` has 25 methods (exceeds 20 allowed). Consider refactoring.
type controller struct {
gitgetter git.Helper
templateRepo templaterepo.TemplateRepo
groupMgr gmanager.Manager
templateMgr tmanager.Manager
templateReleaseMgr trmanager.Manager
memberMgr membermanager.Manager
memberSvc memberservice.Service
templateSchemaGetter schema.Getter
}
 
var _ Controller = (*controller)(nil)
 
// NewController initializes a new controller
func NewController(param *param.Param, repo templaterepo.TemplateRepo) Controller {
return &controller{
gitgetter: param.GitGetter,
templateMgr: param.TemplateMgr,
templateReleaseMgr: param.TemplateReleaseMgr,
templateSchemaGetter: param.TemplateSchemaGetter,
templateRepo: repo,
memberMgr: param.MemberMgr,
memberSvc: param.MemberService,
groupMgr: param.GroupMgr,
}
}
 
Method `controller.ListV2` has a Cognitive Complexity of 63 (exceeds 20 allowed). Consider refactoring.
Method `controller.ListV2` has 75 lines of code (exceeds 50 allowed). Consider refactoring.
Method `controller.ListV2` has 9 return statements (exceeds 4 allowed).
func (c *controller) ListV2(ctx context.Context, query *q.Query, withFullPath bool) (Templates, error) {
var (
groupIDs []uint
err error
)
if query != nil &&
query.Keywords != nil {
if query.Keywords[common.TemplateQueryByUser] != nil {
if userID, ok := query.Keywords[common.TemplateQueryByUser].(uint); ok {
if err := permission.OnlySelfAndAdmin(ctx, userID); err != nil {
return nil, err
}
// get groups authorized to current user
groupIDs, err = c.memberMgr.ListResourceOfMemberInfoByRole(
ctx, membermodels.TypeGroup, userID, role.Owner)
if err != nil {
return nil, perror.WithMessage(err, "failed to list group resource of current user")
}
 
// get these groups' subGroups
subGroups, err := c.groupMgr.GetSubGroupsByGroupIDs(ctx, groupIDs)
if err != nil {
return nil, perror.WithMessage(err, "failed to get groups")
}
 
groupIDs = nil
for _, group := range subGroups {
var member *membermodels.Member
if member, err = c.memberMgr.Get(ctx, membermodels.TypeGroup,
group.ID, membermodels.MemberUser, userID); err != nil {
return nil, err
}
if member == nil || member.Role == role.Owner {
groupIDs = append(groupIDs, group.ID)
}
}
}
}
 
if groupID, ok := query.Keywords[common.TemplateQueryByGroupRecursive].(uint); ok {
if !c.groupMgr.IsRootGroup(groupID) {
group, err := c.groupMgr.GetByID(ctx, groupID)
if err != nil {
return nil, err
}
IDStrs := strings.Split(group.TraversalIDs, ",")
var groupIDs []uint
for _, idStr := range IDStrs {
id, err := strconv.ParseUint(idStr, 10, 64)
if err != nil {
return nil, perror.Wrapf(
herrors.ErrParamInvalid,
"failed to parse traversal ID\n"+
"id = %s\nerr = %v", idStr, err)
}
groupIDs = append(groupIDs, uint(id))
}
if groupID, ok := query.Keywords[common.TemplateQueryByGroups].(uint); ok {
groupIDs = append(groupIDs, groupID)
}
query.Keywords[common.TemplateQueryByGroups] = groupIDs
}
}
}
 
templates, err := c.templateMgr.ListV2(ctx, query, groupIDs...)
if err != nil {
return nil, err
}
 
var tpls Templates
for _, template := range templates {
if c.checkHasOnlyOwnerPermissionForTemplate(ctx, template) {
tpls = append(tpls, toTemplate(template))
}
}
 
if withFullPath {
tpls, err = c.addFullPath(ctx, tpls)
if err != nil {
return nil, err
}
}
return tpls, nil
}
 
// ListTemplate TODO: remove this, keep it for api callers
func (c *controller) ListTemplate(ctx context.Context) (Templates, error) {
const op = "template controller: listTemplate"
defer wlog.Start(ctx, op).StopPrint()
 
var (
tpls Templates
err error
)
 
if selfOnly, ok := ctx.Value(hctx.TemplateListSelfOnly).(bool); ok && selfOnly {
tpls, err = c.listTemplateByUser(ctx)
if err != nil {
return nil, err
}
} else {
templateModels, err := c.templateMgr.ListTemplate(ctx)
if err != nil {
return nil, err
}
 
for _, template := range templateModels {
if c.checkHasOnlyOwnerPermissionForTemplate(ctx, template) {
tpls = append(tpls, toTemplate(template))
}
}
}
 
if withFullpath, ok := ctx.Value(hctx.TemplateWithFullPath).(bool); ok && withFullpath {
tpls, err = c.addFullPath(ctx, tpls)
if err != nil {
return nil, err
}
}
 
return tpls, nil
}
 
// listTemplateByUser returns all templates a user obtaining, that means has owner permission
Method `controller.listTemplateByUser` has 54 lines of code (exceeds 50 allowed). Consider refactoring.
Method `controller.listTemplateByUser` has 10 return statements (exceeds 4 allowed).
func (c *controller) listTemplateByUser(ctx context.Context) (Templates, error) {
// get current user
currentUser, err := common.UserFromContext(ctx)
if err != nil {
return nil, perror.WithMessage(err, "no user in context")
}
 
// get groups authorized to current user
groupIDs, err := c.memberMgr.ListResourceOfMemberInfoByRole(
ctx, membermodels.TypeGroup, currentUser.GetID(), role.Owner)
if err != nil {
return nil, perror.WithMessage(err, "failed to list group resource of current user")
}
 
// get these groups' subGroups
subGroups, err := c.groupMgr.GetSubGroupsByGroupIDs(ctx, groupIDs)
if err != nil {
return nil, perror.WithMessage(err, "failed to get groups")
}
 
groupIDs = nil
for _, group := range subGroups {
var member *membermodels.Member
if member, err = c.memberMgr.Get(ctx, membermodels.TypeGroup,
group.ID, membermodels.MemberUser, currentUser.GetID()); err != nil {
return nil, err
}
if member == nil || member.Role == role.Owner {
groupIDs = append(groupIDs, group.ID)
}
}
 
// list templates of these subGroups
templates, err := c.templateMgr.ListByGroupIDs(ctx, groupIDs)
if err != nil {
return nil, err
}
 
// get templates authorized to current user
authorizedTemplateIDs, err := c.memberMgr.ListResourceOfMemberInfoByRole(ctx,
membermodels.TypeTemplate, currentUser.GetID(), role.Owner)
if err != nil {
return nil, err
}
 
// all applicationIDs, including:
// (1) templates under the authorized groups
// (2) authorized templates directly
authorizedTemplates, err := c.templateMgr.ListByIDs(ctx, authorizedTemplateIDs)
if err != nil {
return nil, err
}
 
set := make(map[uint]struct{})
for _, template := range templates {
set[template.ID] = struct{}{}
}
 
filter := func(t *models.Template) bool {
if _, ok := set[t.ID]; !ok {
set[t.ID] = struct{}{}
return true
}
return false
}
 
for _, t := range authorizedTemplates {
if filter(t) {
templates = append(templates, t)
}
}
 
return toTemplates(templates), nil
}
 
func (c *controller) addFullPath(ctx context.Context, tpls Templates) (Templates, error) {
for i, tpl := range tpls {
if tpl.GroupID == service.RootGroupID {
tpls[i].FullPath = "/" + tpl.Name
continue
}
group, err := c.groupMgr.GetByID(ctx, tpl.GroupID)
if err != nil {
return nil, err
}
groupStrArr := strings.Split(group.TraversalIDs, ",")
groupIDArr := make([]uint, 0, len(groupStrArr))
for _, groupStr := range groupStrArr {
t, err := strconv.Atoi(groupStr)
if err != nil {
return nil, perror.Wrap(herrors.ErrTemplateParamInvalid, err.Error())
}
groupIDArr = append(groupIDArr, uint(t))
}
groups, err := c.groupMgr.GetByIDs(ctx, groupIDArr)
if err != nil {
return nil, err
}
groupMap := make(map[uint]*groupModels.Group, len(groupIDArr))
for _, group := range groups {
groupMap[group.ID] = group
}
fullpath := strings.Builder{}
for _, groupID := range groupIDArr {
fullpath.WriteString("/")
fullpath.WriteString(groupMap[groupID].Path)
}
fullpath.WriteString("/")
fullpath.WriteString(tpl.Name)
tpls[i].FullPath = fullpath.String()
}
return tpls, nil
}
 
func (c *controller) ListTemplateRelease(ctx context.Context, templateName string) (_ Releases, err error) {
const op = "template controller: listTemplateRelease"
defer wlog.Start(ctx, op).StopPrint()
 
template, err := c.templateMgr.GetByName(ctx, templateName)
if err != nil {
return nil, err
}
 
if !c.checkHasOnlyOwnerPermissionForTemplate(ctx, template) {
return nil, perror.Wrapf(herrors.ErrForbidden,
"you have no permission to access this resource\n"+
"template name = %s", templateName)
}
 
templateReleaseModels, err := c.templateReleaseMgr.ListByTemplateName(ctx, templateName)
if err != nil {
return nil, err
}
 
templateReleaseModels = c.checkStatusForReleases(ctx, template, templateReleaseModels)
 
for _, release := range templateReleaseModels {
_ = c.templateReleaseMgr.UpdateByID(ctx, release.ID, release)
}
 
var releases Releases
for _, release := range templateReleaseModels {
if c.checkHasOnlyOwnerPermissionForRelease(ctx, release) {
releases = append(releases, toRelease(release))
}
}
sort.Sort(releases)
return releases, nil
}
 
func (c *controller) GetTemplateSchema(ctx context.Context, releaseID uint,
param map[string]string) (_ *Schemas, err error) {
const op = "template controller: getTemplateSchema"
defer wlog.Start(ctx, op).StopPrint()
 
release, err := c.templateReleaseMgr.GetByID(ctx, releaseID)
if err != nil {
return nil, err
}
 
schemas, err := c.templateSchemaGetter.GetTemplateSchema(ctx, release.TemplateName, release.Name, param)
if err != nil {
return nil, err
}
 
return toSchemas(schemas), nil
}
 
// ListTemplateByGroupID lists all template available
Method `controller.ListTemplateByGroupID` has 5 return statements (exceeds 4 allowed).
func (c *controller) ListTemplateByGroupID(ctx context.Context, groupID uint, withoutCI bool) (Templates, error) {
const op = "template controller: listTemplateByGroupID"
defer wlog.Start(ctx, op).StopPrint()
 
if !c.groupMgr.GroupExist(ctx, groupID) {
reason := fmt.Sprintf("group not found: %d", groupID)
return nil, perror.Wrap(herrors.NewErrNotFound(herrors.GroupInDB, reason), reason)
}
 
if listRecursively, ok := ctx.Value(hctx.TemplateListRecursively).(bool); ok && listRecursively {
return c.listTemplateByGroupIDRecursively(ctx, groupID, withoutCI)
}
 
templates, err := c.templateMgr.ListByGroupID(ctx, groupID)
if err != nil {
return nil, err
}
 
var tpls Templates
for _, template := range templates {
if c.checkHasOnlyOwnerPermissionForTemplate(ctx, template) {
if template.WithoutCI == withoutCI {
tpls = append(tpls, toTemplate(template))
}
}
}
 
if withFullpath, ok := ctx.Value(hctx.TemplateWithFullPath).(bool); ok && withFullpath {
tpls, err = c.addFullPath(ctx, tpls)
if err != nil {
return nil, err
}
}
return tpls, err
}
 
Method `controller.listTemplateByGroupIDRecursively` has 7 return statements (exceeds 4 allowed).
func (c *controller) listTemplateByGroupIDRecursively(ctx context.Context,
groupID uint, withoutCI bool) (Templates, error) {
if !c.groupMgr.GroupExist(ctx, groupID) {
reason := fmt.Sprintf("group not found: %d", groupID)
return nil, perror.Wrap(herrors.NewErrNotFound(herrors.GroupInDB, reason), reason)
}
 
groupIDs := make([]uint, 0)
 
if c.groupMgr.IsRootGroup(groupID) {
return c.ListTemplate(ctx)
}
group, err := c.groupMgr.GetByID(ctx, groupID)
if err != nil {
return nil, err
}
 
idStrs := strings.Split(group.TraversalIDs, ",")
for _, idStr := range idStrs {
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
return nil, perror.Wrapf(herrors.ErrParamInvalid,
"failed to parse traversal ID:\n"+
"traversal ID = %s", group.TraversalIDs)
}
groupIDs = append(groupIDs, uint(id))
}
 
templates, err := c.templateMgr.ListByGroupIDs(ctx, groupIDs)
if err != nil {
return nil, err
}
 
var tpls Templates
for _, template := range templates {
if c.checkHasOnlyOwnerPermissionForTemplate(ctx, template) {
if template.WithoutCI == withoutCI {
tpls = append(tpls, toTemplate(template))
}
}
}
 
if withFullpath, ok := ctx.Value(hctx.TemplateWithFullPath).(bool); ok && withFullpath {
tpls, err = c.addFullPath(ctx, tpls)
if err != nil {
return nil, err
}
}
return tpls, err
}
 
// ListTemplateReleaseByTemplateID lists all releases of the specified template
Method `controller.ListTemplateReleaseByTemplateID` has 6 return statements (exceeds 4 allowed).
func (c *controller) ListTemplateReleaseByTemplateID(ctx context.Context, templateID uint) (Releases, error) {
const op = "template controller: listTemplateReleaseByTemplateID"
defer wlog.Start(ctx, op).StopPrint()
 
user, err := common.UserFromContext(ctx)
if err != nil {
return nil, err
}
 
template, err := c.templateMgr.GetByID(ctx, templateID)
if err != nil {
return nil, err
}
 
if !c.checkHasOnlyOwnerPermissionForTemplate(ctx, template) {
return nil, perror.Wrapf(herrors.ErrForbidden,
"you have no permission to access this resource:\n"+
"template id = %d", templateID)
}
 
releases, err := c.templateReleaseMgr.ListByTemplateID(ctx, templateID)
if err != nil {
return nil, err
}
 
releases = c.checkStatusForReleases(ctx, template, releases)
 
for _, release := range releases {
_ = c.templateReleaseMgr.UpdateByID(ctx, release.ID, release)
}
 
if user.IsAdmin() {
return toReleases(releases), nil
}
 
var releaseModels Releases
for _, release := range releases {
if c.checkHasOnlyOwnerPermissionForRelease(ctx, release) {
releaseModels = append(releaseModels, toRelease(release))
}
}
return releaseModels, nil
}
 
Method `controller.CreateTemplate` has 9 return statements (exceeds 4 allowed).
func (c *controller) CreateTemplate(ctx context.Context,
groupID uint, request CreateTemplateRequest) (*Template, error) {
const op = "template controller: createTemplate"
defer wlog.Start(ctx, op).StopPrint()
 
user, err := common.UserFromContext(ctx)
if err != nil {
return nil, err
}
 
// skip root group check
if !c.groupMgr.GroupExist(ctx, groupID) {
reason := fmt.Sprintf("group not found: %d", groupID)
return nil, perror.Wrap(herrors.NewErrNotFound(herrors.GroupInDB, reason), reason)
}
 
// check if template exist
if _, err = c.templateMgr.GetByName(ctx, request.Name); err != nil {
if _, ok := perror.Cause(err).(*herrors.HorizonErrNotFound); !ok {
return nil, err
}
} else {
return nil, perror.Wrap(herrors.ErrNameConflict, "an template with the same name already exists, "+
"please do not create it again")
}
 
template, err := request.toTemplateModel(ctx)
if err != nil {
return nil, err
}
template.ChartName = template.Name
template.GroupID = groupID
// should always be true
template.WithoutCI = true
 
if template.Type == "" {
return nil, perror.Wrapf(herrors.ErrParamInvalid, "template type is empty")
}
 
template, err = c.templateMgr.Create(ctx, template)
if err != nil {
return nil, err
}
 
_, err = c.memberMgr.Create(ctx, &membermodels.Member{
ResourceType: membermodels.TypeTemplate,
ResourceID: template.ID,
Role: role.Owner,
MemberType: membermodels.MemberUser,
MemberNameID: user.GetID(),
GrantedBy: user.GetID(),
CreatedBy: user.GetID(),
})
if err != nil {
return nil, err
}
 
return toTemplate(template), nil
}
 
Method `controller.CreateRelease` has 6 return statements (exceeds 4 allowed).
func (c *controller) CreateRelease(ctx context.Context,
templateID uint, request CreateReleaseRequest) (*Release, error) {
const op = "template controller: createTemplateRelease"
defer wlog.Start(ctx, op).StopPrint()
 
template, err := c.templateMgr.GetByID(ctx, templateID)
if err != nil {
return nil, err
}
 
release, err := request.toReleaseModel(ctx, template)
if err != nil {
return nil, err
}
 
if syncToRepo, ok := ctx.Value(hctx.ReleaseSyncToRepo).(bool); !ok || (ok && syncToRepo) {
tag, err := c.getTag(ctx, template.Repository, template.ChartName, release.Name)
if err != nil {
return nil, err
}
chartVersion := fmt.Sprintf(common.ChartVersionFormat, release.Name, tag.ShortID)
err = c.syncReleaseToRepo(tag.ArchiveData, template.ChartName, chartVersion)
if err != nil {
return nil, err
}
release.CommitID = tag.ShortID
release.SyncStatus = trmodels.StatusSucceed
release.ChartVersion = chartVersion
} else {
release.SyncStatus = trmodels.StatusOutOfSync
}
 
release.Template = template.ID
release.LastSyncAt = time.Now()
 
var newRelease *trmodels.TemplateRelease
if newRelease, err = c.templateReleaseMgr.Create(ctx, release); err != nil {
return nil, err
}
return toRelease(newRelease), nil
}
 
// GetTemplate gets template by templateID
func (c *controller) GetTemplate(ctx context.Context, templateID uint) (*Template, error) {
const op = "template controller: getTemplate"
defer wlog.Start(ctx, op).StopPrint()
 
template, err := c.templateMgr.GetByID(ctx, templateID)
if err != nil {
return nil, err
}
 
if !c.checkHasOnlyOwnerPermissionForTemplate(ctx, template) {
return nil, perror.Wrapf(herrors.ErrForbidden,
"you have no permission to access this resource:\n"+
"template id = %d", templateID)
}
 
tpl := toTemplate(template)
withRelease, ok := ctx.Value(hctx.TemplateWithRelease).(bool)
if ok && withRelease {
if templateReleases, err := c.templateReleaseMgr.
ListByTemplateID(ctx, template.ID); err == nil {
for _, release := range templateReleases {
if c.checkHasOnlyOwnerPermissionForRelease(ctx, release) {
tpl.Releases = append(tpl.Releases, toRelease(release))
}
}
}
}
return tpl, nil
}
 
Method `controller.GetRelease` has 5 return statements (exceeds 4 allowed).
func (c *controller) GetRelease(ctx context.Context, releaseID uint) (*Release, error) {
const op = "template controller: getRelease"
defer wlog.Start(ctx, op).StopPrint()
 
release, err := c.templateReleaseMgr.GetByID(ctx, releaseID)
if err != nil {
return nil, err
}
 
if !c.checkHasOnlyOwnerPermissionForRelease(ctx, release) {
return nil, perror.Wrapf(herrors.ErrForbidden,
"you have no permission to access this resource:\n"+
"release id = %d", releaseID)
}
 
template, err := c.templateMgr.GetByID(ctx, release.Template)
if err != nil {
return nil, err
}
 
if !c.checkHasOnlyOwnerPermissionForTemplate(ctx, template) {
return nil, perror.Wrapf(herrors.ErrForbidden,
"you have no permission to access this resource:\n"+
"template id = %d, release id = %d", release.Template, releaseID)
}
 
return toRelease(release), nil
}
 
Method `controller.DeleteTemplate` has 7 return statements (exceeds 4 allowed).
func (c *controller) DeleteTemplate(ctx context.Context, templateID uint) error {
const op = "template controller: deleteTemplate"
defer wlog.Start(ctx, op).StopPrint()
 
releases, err := c.templateReleaseMgr.ListByTemplateID(ctx, templateID)
if err != nil {
return err
}
if len(releases) != 0 {
return perror.Wrap(herrors.ErrSubResourceExist,
"this template cannot be deleted because there are releases under this template.")
}
 
ctx = context.WithValue(ctx, hctx.TemplateOnlyRefCount, true)
_, count, err := c.templateMgr.GetRefOfApplication(ctx, templateID)
if err != nil {
return err
}
if count != 0 {
return perror.Wrap(herrors.ErrSubResourceExist,
"this template cannot be deleted because it was used by applications.")
}
 
_, count, err = c.templateMgr.GetRefOfCluster(ctx, templateID)
if err != nil {
return err
}
if count != 0 {
return perror.Wrap(herrors.ErrSubResourceExist, "this template cannot be deleted because it was used by clusters.")
}
 
return c.templateMgr.DeleteByID(ctx, templateID)
}
 
Method `controller.DeleteRelease` has 5 return statements (exceeds 4 allowed).
func (c *controller) DeleteRelease(ctx context.Context, releaseID uint) error {
const op = "template controller: deleteRelease"
defer wlog.Start(ctx, op).StopPrint()
 
ctx = context.WithValue(ctx, hctx.TemplateOnlyRefCount, true)
_, count, err := c.templateReleaseMgr.GetRefOfApplication(ctx, releaseID)
if err != nil {
return err
}
if count != 0 {
return perror.Wrap(herrors.ErrSubResourceExist, "this release cannot be deleted because it was used by applications.")
}
 
_, count, err = c.templateReleaseMgr.GetRefOfCluster(ctx, releaseID)
if err != nil {
return err
}
if count != 0 {
return perror.Wrap(herrors.ErrSubResourceExist, "this release cannot be deleted because it was used by clusters.")
}
 
return c.templateReleaseMgr.DeleteByID(ctx, releaseID)
}
 
// UpdateTemplate deletes a template by ID
Method `controller.UpdateTemplate` has 5 return statements (exceeds 4 allowed).
func (c *controller) UpdateTemplate(ctx context.Context, templateID uint, request UpdateTemplateRequest) error {
const op = "template controller: updateTemplate"
defer wlog.Start(ctx, op).StopPrint()
 
template, err := c.templateMgr.GetByID(ctx, templateID)
if err != nil {
return err
}
 
releases, err := c.templateReleaseMgr.ListByTemplateID(ctx, template.ID)
if err != nil {
return err
}
 
if len(releases) != 0 && request.Repository != "" &&
request.Repository != template.Repository {
return perror.Wrapf(herrors.ErrForbidden,
"can not modify template repository while releases existing:\n"+
"releases numbers: %d", len(releases))
}
 
tplUpdate, err := request.toTemplateModel(ctx)
if err != nil {
return err
}
 
return c.templateMgr.UpdateByID(ctx, templateID, tplUpdate)
}
 
// UpdateRelease deletes a template release by ID
func (c *controller) UpdateRelease(ctx context.Context, releaseID uint, request UpdateReleaseRequest) error {
const op = "template controller: updateRelease"
defer wlog.Start(ctx, op).StopPrint()
 
trUpdate, err := request.toReleaseModel(ctx)
if err != nil {
return err
}
 
return c.templateReleaseMgr.UpdateByID(ctx, releaseID, trUpdate)
}
 
func (c *controller) SyncReleaseToRepo(ctx context.Context, releaseID uint) error {
const op = "template controller: syncReleaseToRepo"
defer wlog.Start(ctx, op).StopPrint()
 
release, err := c.templateReleaseMgr.GetByID(ctx, releaseID)
if err != nil {
return err
}
 
template, err := c.templateMgr.GetByID(ctx, release.Template)
if err != nil {
return err
}
 
tag, err := c.getTag(ctx, template.Repository, template.ChartName, release.Name)
if err != nil {
return err
}
chartVersion := fmt.Sprintf(common.ChartVersionFormat, release.Name, tag.ShortID)
err = c.syncReleaseToRepo(tag.ArchiveData, template.ChartName, chartVersion)
if err != nil {
_ = c.handleReleaseSyncStatus(ctx, release, tag.ShortID, err.Error())
} else {
_ = c.handleReleaseSyncStatus(ctx, release, tag.ShortID, "")
}
return err
}
 
func (c *controller) handleReleaseSyncStatus(ctx context.Context,
release *trmodels.TemplateRelease, commitID string, failedReason string) error {
if failedReason == "" {
release.SyncStatus = trmodels.StatusSucceed
release.FailedReason = ""
release.ChartVersion = fmt.Sprintf(common.ChartVersionFormat, release.Name, commitID)
} else {
release.FailedReason = failedReason
release.SyncStatus = trmodels.StatusFailed
}
release.CommitID = commitID
release.LastSyncAt = time.Now()
return c.templateReleaseMgr.UpdateByID(ctx, release.ID, release)
}
 
func (c *controller) getTag(ctx context.Context, repository,
name, tag string) (*git.Tag, error) {
tagDetail, err := c.gitgetter.GetTagArchive(ctx, repository, tag)
if err != nil {
return nil, err
}
 
return tagDetail, nil
}
 
func (c *controller) checkStatusForReleases(ctx context.Context,
template *models.Template, releases []*trmodels.TemplateRelease) []*trmodels.TemplateRelease {
var wg sync.WaitGroup
res := make([]*trmodels.TemplateRelease, len(releases))
 
wg.Add(len(releases))
for i := range releases {
go func(index int, r *trmodels.TemplateRelease) {
res[index], _ = c.checkStatusForRelease(ctx, template, r)
wg.Done()
}(i, releases[i])
}
wg.Wait()
 
return res
}
 
func (c *controller) checkStatusForRelease(ctx context.Context,
template *models.Template, release *trmodels.TemplateRelease) (*trmodels.TemplateRelease, error) {
if release.SyncStatus != trmodels.StatusSucceed {
return release, nil
}
 
tag, err := c.getTag(ctx, template.Repository, release.ChartName, release.Name)
if err != nil {
release.SyncStatus = trmodels.StatusUnknown
return release, err
}
if tag.ShortID != release.CommitID {
release.SyncStatus = trmodels.StatusOutOfSync
}
return release, nil
}
 
func (c *controller) syncReleaseToRepo(chartBytes []byte, name, tag string) error {
chart, err := loader.LoadArchive(bytes.NewReader(chartBytes))
if err != nil {
return perror.Wrap(herrors.ErrLoadChartArchive, fmt.Sprintf("failed to load archive: %v", err))
}
chart.Metadata.Version = tag
chart.Metadata.Name = name
 
return c.templateRepo.UploadChart(chart)
}
 
Method `controller.checkHasOnlyOwnerPermissionForTemplate` has 7 return statements (exceeds 4 allowed).
Similar blocks of code found in 2 locations. Consider refactoring.
func (c *controller) checkHasOnlyOwnerPermissionForTemplate(ctx context.Context,
template *models.Template) bool {
user, err := common.UserFromContext(ctx)
if err != nil {
return false
}
if user.IsAdmin() {
return true
}
 
if template == nil || template.OnlyOwner == nil {
return false
}
 
if !*template.OnlyOwner {
return true
}
 
member, err := c.memberSvc.GetMemberOfResource(ctx, common.ResourceTemplate, strconv.Itoa(int(template.ID)))
if err != nil || member == nil {
return false
}
if member.Role == role.Owner {
return true
}
return false
}
 
Method `controller.checkHasOnlyOwnerPermissionForRelease` has 7 return statements (exceeds 4 allowed).
Similar blocks of code found in 2 locations. Consider refactoring.
func (c *controller) checkHasOnlyOwnerPermissionForRelease(ctx context.Context,
release *trmodels.TemplateRelease) bool {
user, err := common.UserFromContext(ctx)
if err != nil {
return false
}
if user.IsAdmin() {
return true
}
 
if release == nil || release.OnlyOwner == nil {
return false
}
 
if !*release.OnlyOwner {
return true
}
 
member, err := c.memberSvc.GetMemberOfResource(ctx, common.ResourceTemplate, strconv.Itoa(int(release.Template)))
if err != nil || member == nil {
return false
}
if member.Role == role.Owner {
return true
}
return false
}