Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add user group support for collaboration and release plan approve #3451

Merged
merged 10 commits into from
Apr 24, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ func (c *CollaborationModeColl) Update(username string, args *models.Collaborati
"update_time": time.Now().Unix(),
"update_by": username,
"members": args.Members,
"member_info": args.MemberInfo,
"workflows": args.Workflows,
"recycle_day": args.RecycleDay,
"revision": res.Revision + 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
config2 "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/label/config"
workflowservice "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/workflow/service/workflow"
"github.com/koderover/zadig/v2/pkg/setting"
"github.com/koderover/zadig/v2/pkg/shared/client/user"
"github.com/koderover/zadig/v2/pkg/tool/log"
)

Expand Down Expand Up @@ -296,9 +297,20 @@ func updateVisitTime(uid string, cis []*models.CollaborationInstance, logger *za
}

func GetCollaborationUpdate(projectName, uid, identityType, userName string, logger *zap.SugaredLogger) (*GetCollaborationUpdateResp, error) {
relatedGroups, err := user.New().GetUserGroupsByUid(uid)
if err != nil {
logger.Errorf("GetCollaborationUpdate error, err msg:%s", err)
return nil, err
}
members := []string{uid}
for _, group := range relatedGroups.GroupList {
members = append(members, group.ID)
}

// user uid and related gids to get collaboration mode
collaborations, err := mongodb.NewCollaborationModeColl().List(&mongodb.CollaborationModeListOptions{
Projects: []string{projectName},
Members: []string{uid},
Members: members,
})
if err != nil {
logger.Errorf("GetCollaborationUpdate error, err msg:%s", err)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,32 @@ limitations under the License.
package service

import (
"fmt"

"go.mongodb.org/mongo-driver/mongo"
"go.uber.org/zap"
"k8s.io/apimachinery/pkg/util/sets"

"github.com/koderover/zadig/v2/pkg/microservice/aslan/core/collaboration/repository/models"
"github.com/koderover/zadig/v2/pkg/microservice/aslan/core/collaboration/repository/mongodb"
)

func validateMemberInfo(collaborationMode *models.CollaborationMode) bool {
if len(collaborationMode.Members) != len(collaborationMode.MemberInfo) {
return false
}
memberSet := sets.NewString(collaborationMode.Members...)
memberInfoSet := sets.NewString()
for _, memberInfo := range collaborationMode.MemberInfo {
memberInfoSet.Insert(memberInfo.GetID())
}
return memberSet.Equal(memberInfoSet)
}

func CreateCollaborationMode(userName string, collaborationMode *models.CollaborationMode, logger *zap.SugaredLogger) error {
if !validateMemberInfo(collaborationMode) {
return fmt.Errorf("members and member_info not match")
}
err := mongodb.NewCollaborationModeColl().Create(userName, collaborationMode)
if err != nil {
logger.Errorf("CreateCollaborationMode error, err msg:%s", err)
Expand All @@ -34,6 +52,9 @@ func CreateCollaborationMode(userName string, collaborationMode *models.Collabor
}

func UpdateCollaborationMode(userName string, collaborationMode *models.CollaborationMode, logger *zap.SugaredLogger) error {
if !validateMemberInfo(collaborationMode) {
return fmt.Errorf("members and member_info not match")
}
err := mongodb.NewCollaborationModeColl().Update(userName, collaborationMode)
if err != nil {
logger.Errorf("UpdateCollaborationMode error, err msg:%s", err)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,11 @@ type Approval struct {
}

type NativeApproval struct {
Timeout int `bson:"timeout" yaml:"timeout" json:"timeout"`
ApproveUsers []*User `bson:"approve_users" yaml:"approve_users" json:"approve_users"`
NeededApprovers int `bson:"needed_approvers" yaml:"needed_approvers" json:"needed_approvers"`
RejectOrApprove config.ApproveOrReject `bson:"reject_or_approve" yaml:"-" json:"reject_or_approve"`
Timeout int `bson:"timeout" yaml:"timeout" json:"timeout"`
ApproveUsers []*User `bson:"approve_users" yaml:"approve_users" json:"approve_users"`
FloatApproveUsers []*User `bson:"-" yaml:"flat_approve_users" json:"flat_approve_users"`
landylee007 marked this conversation as resolved.
Show resolved Hide resolved
NeededApprovers int `bson:"needed_approvers" yaml:"needed_approvers" json:"needed_approvers"`
RejectOrApprove config.ApproveOrReject `bson:"reject_or_approve" yaml:"-" json:"reject_or_approve"`
// InstanceCode: native approval instance code, save for working after restart aslan
InstanceCode string `bson:"instance_code" yaml:"instance_code" json:"instance_code"`
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,16 +154,23 @@ func setCollaborationModesWorkflowDisplayName(mode *models.CollaborationMode) {
}

func setMemberInfo(mode *models.CollaborationMode) {
if mode.MemberInfo != nil && len(mode.MemberInfo) > 0 {
if mode.MemberInfo != nil && len(mode.MemberInfo) == len(mode.Members) {
return
}

memberList := make([]*types.Identity, 0)
memberInfoMap := make(map[string]*types.Identity)
for _, member := range mode.MemberInfo {
memberInfoMap[member.UID] = member
}

for _, uid := range mode.Members {
memberList = append(memberList, &types.Identity{
if _, ok := memberInfoMap[uid]; ok {
continue
}
mode.MemberInfo = append(mode.MemberInfo, &types.Identity{
IdentityType: "user",
UID: uid,
})
}
mode.MemberInfo = memberList

}
144 changes: 108 additions & 36 deletions pkg/microservice/aslan/core/release_plan/service/approval.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ import (
"time"

"github.com/google/uuid"
"github.com/koderover/zadig/v2/pkg/shared/client/systemconfig"
"github.com/koderover/zadig/v2/pkg/shared/client/user"
"github.com/koderover/zadig/v2/pkg/tool/mail"
"github.com/koderover/zadig/v2/pkg/types"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/util/sets"

configbase "github.com/koderover/zadig/v2/pkg/config"
"github.com/koderover/zadig/v2/pkg/microservice/aslan/config"
Expand All @@ -35,12 +40,9 @@ import (
approvalservice "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service/approval"
dingservice "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service/dingtalk"
larkservice "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service/lark"
"github.com/koderover/zadig/v2/pkg/shared/client/systemconfig"
"github.com/koderover/zadig/v2/pkg/shared/client/user"
"github.com/koderover/zadig/v2/pkg/tool/dingtalk"
"github.com/koderover/zadig/v2/pkg/tool/lark"
"github.com/koderover/zadig/v2/pkg/tool/log"
"github.com/koderover/zadig/v2/pkg/tool/mail"
)

//go:embed approval.html
Expand Down Expand Up @@ -210,48 +212,114 @@ func updateDingTalkApproval(ctx context.Context, approvalInfo *models.Approval)
return nil
}

func geneFlatNativeApprovalUsers(approval *models.NativeApproval) ([]*models.User, map[string]*types.UserInfo) {
// change [group + user] approvals to user approvals
approvalUsers := make([]*models.User, 0)
userSet := sets.NewString()
userMap := make(map[string]*types.UserInfo)

if approval == nil {
return approvalUsers, userMap
}

for _, u := range approval.ApproveUsers {
if u.Type == "user" || u.Type == "" {
userSet.Insert(u.UserID)
approvalUsers = append(approvalUsers, u)
}
}
for _, u := range approval.ApproveUsers {
if u.Type == "group" {
groupInfo, err := user.New().GetGroupDetailedInfo(u.GroupID)
if err != nil {
log.Warnf("CreateNativeApproval GetGroupDetailedInfo error, error msg:%s", err)
continue
}
for _, uid := range groupInfo.UIDs {
if userSet.Has(uid) {
continue
}
userSet.Insert(uid)
userDetailedInfo, err := user.New().GetUserByID(uid)
if err != nil {
log.Errorf("failed to find user %s, error: %s", uid, err)
continue
}
userMap[uid] = userDetailedInfo
approvalUsers = append(approvalUsers, &models.User{
Type: "user",
UserID: uid,
UserName: userDetailedInfo.Name,
})
}
}
}

return approvalUsers, userMap
}

func createNativeApproval(plan *models.ReleasePlan, url string) error {
if plan == nil || plan.Approval == nil || plan.Approval.NativeApproval == nil {
return errors.New("createNativeApproval: native approval data not found")
}
approval := plan.Approval.NativeApproval

approvalUsers, userMap := geneFlatNativeApprovalUsers(approval)

// send email to all approval users if necessary
go func() {
email, err := systemconfig.New().GetEmailHost()
if err != nil {
log.Errorf("CreateNativeApproval GetEmailHost error, error msg:%s", err)
return
}
var err error
mailNotifyInfo := ""
var email *systemconfig.Email

t, err := template.New("approval").Parse(string(approvalHTML))
if err != nil {
log.Errorf("CreateNativeApproval template parse error, error msg:%s", err)
return
for {
email, err = systemconfig.New().GetEmailHost()
if err != nil {
log.Errorf("CreateNativeApproval GetEmailHost error, error msg:%s", err)
break
}

t, err := template.New("approval").Parse(string(approvalHTML))
if err != nil {
log.Errorf("CreateNativeApproval template parse error, error msg:%s", err)
break
}
var buf bytes.Buffer
err = t.Execute(&buf, struct {
PlanName string
Manager string
Description string
TimeRange string
Url string
}{
PlanName: plan.Name,
Manager: plan.Manager,
Description: plan.Description,
TimeRange: time.Unix(plan.StartTime, 0).Format("2006-01-02 15:04:05") + "-" + time.Unix(plan.EndTime, 0).Format("2006-01-02 15:04:05"),
Url: url,
})
if err != nil {
log.Errorf("CreateNativeApproval template execute error, error msg:%s", err)
break
}
mailNotifyInfo = buf.String()
break
}
var buf bytes.Buffer
err = t.Execute(&buf, struct {
PlanName string
Manager string
Description string
TimeRange string
Url string
}{
PlanName: plan.Name,
Manager: plan.Manager,
Description: plan.Description,
TimeRange: time.Unix(plan.StartTime, 0).Format("2006-01-02 15:04:05") + "-" + time.Unix(plan.EndTime, 0).Format("2006-01-02 15:04:05"),
Url: url,
})
if err != nil {
log.Errorf("CreateNativeApproval template execute error, error msg:%s", err)

if email == nil {
return
}
for _, u := range approval.ApproveUsers {
info, err := user.New().GetUserByID(u.UserID)
if err != nil {
log.Warnf("CreateNativeApproval GetUserByUid error, error msg:%s", err)
continue

for _, u := range approvalUsers {
info, ok := userMap[u.UserID]
if !ok {
info, err = user.New().GetUserByID(u.UserID)
if err != nil {
log.Warnf("CreateNativeApproval GetUserByUid error, error msg:%s", err)
continue
}
}

if info.Email == "" {
log.Warnf("CreateNativeApproval user %s email is empty", info.Name)
continue
Expand All @@ -264,7 +332,7 @@ func createNativeApproval(plan *models.ReleasePlan, url string) error {
UserName: email.UserName,
Password: email.Password,
Port: email.Port,
Body: buf.String(),
Body: mailNotifyInfo,
})
if err != nil {
log.Errorf("CreateNativeApproval SendEmail error, error msg:%s", err)
Expand All @@ -273,10 +341,14 @@ func createNativeApproval(plan *models.ReleasePlan, url string) error {
}
}()

originApprovalUser := approval.ApproveUsers
approval.ApproveUsers = approvalUsers

approveKey := uuid.New().String()
approval.InstanceCode = approveKey
approveWithL := approval
approvalservice.GlobalApproveMap.SetApproval(approveKey, approveWithL)

approvalservice.GlobalApproveMap.SetApproval(approveKey, approval)
approval.ApproveUsers = originApprovalUser
return nil
}

Expand Down
13 changes: 12 additions & 1 deletion pkg/microservice/aslan/core/release_plan/service/release_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,14 @@ func ListReleasePlans(pageNum, pageSize int64) (*ListReleasePlanResp, error) {
func GetReleasePlan(id string) (*models.ReleasePlan, error) {
releasePlan, err := mongodb.NewReleasePlanColl().GetByID(context.Background(), id)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "GetReleasePlan")
}

// native approval users may be user or user groups
// convert to flat user when needed, this data is generated dynamically because group binding may be changed
if releasePlan.Approval != nil && releasePlan.Approval.NativeApproval != nil {
flatNativeApprovalUsers, _ := geneFlatNativeApprovalUsers(releasePlan.Approval.NativeApproval)
releasePlan.Approval.NativeApproval.FloatApproveUsers = flatNativeApprovalUsers
}

for _, releasePlanJob := range releasePlan.Jobs {
Expand Down Expand Up @@ -533,7 +540,11 @@ func ApproveReleasePlan(c *handler.Context, planID string, req *ApproveRequest)
if !ok {
// restore data after restart aslan
log.Infof("updateNativeApproval: approval instance code %s not found, set it", plan.Approval.NativeApproval.InstanceCode)
approvalUsers, _ := geneFlatNativeApprovalUsers(plan.Approval.NativeApproval)
originApprovalUsers := plan.Approval.NativeApproval.ApproveUsers
plan.Approval.NativeApproval.ApproveUsers = approvalUsers
approvalservice.GlobalApproveMap.SetApproval(plan.Approval.NativeApproval.InstanceCode, plan.Approval.NativeApproval)
plan.Approval.NativeApproval.ApproveUsers = originApprovalUsers
}

approval, err = approvalservice.GlobalApproveMap.DoApproval(approvalKey, c.UserName, c.UserID, req.Comment, req.Approve)
Expand Down
Loading
Loading