diff --git a/core/common/const.go b/core/common/const.go index 3ff1df7cd..0820add15 100644 --- a/core/common/const.go +++ b/core/common/const.go @@ -108,6 +108,8 @@ const ( ResourceWebhook = "webhooks" ResourceWebhookLog = "webhooklogs" + + ResourceMember = "members" ) const ( diff --git a/core/controller/application/controller.go b/core/controller/application/controller.go index c6f85d0f1..83ef0294e 100644 --- a/core/controller/application/controller.go +++ b/core/controller/application/controller.go @@ -309,16 +309,8 @@ func (c *controller) CreateApplication(ctx context.Context, groupID uint, request.TemplateInput.Pipeline, request.TemplateInput.Application) // 7. record event - if _, err := c.eventMgr.CreateEvent(ctx, &eventmodels.Event{ - EventSummary: eventmodels.EventSummary{ - ResourceType: common.ResourceApplication, - EventType: eventmodels.ApplicationCreated, - ResourceID: ret.ID, - }, - }); err != nil { - log.Warningf(ctx, "failed to create event, err: %s", err.Error()) - } - + c.recordApplicationEvent(ctx, ret.ID, eventmodels.ApplicationCreated) + c.recordMemberCreatedEvent(ctx, ret.ID) return ret, nil } @@ -436,16 +428,8 @@ func (c *controller) CreateApplicationV2(ctx context.Context, groupID uint, ret.Priority = *request.Priority } - if _, err := c.eventMgr.CreateEvent(ctx, &eventmodels.Event{ - EventSummary: eventmodels.EventSummary{ - ResourceType: common.ResourceApplication, - EventType: eventmodels.ApplicationCreated, - ResourceID: applicationDBModel.ID, - }, - }); err != nil { - log.Warningf(ctx, "failed to create event, err: %s", err.Error()) - } - + c.recordApplicationEvent(ctx, applicationDBModel.ID, eventmodels.ApplicationCreated) + c.recordMemberCreatedEvent(ctx, ret.ID) return ret, nil } @@ -498,15 +482,7 @@ func (c *controller) UpdateApplication(ctx context.Context, id uint, } // 5. record event - if _, err := c.eventMgr.CreateEvent(ctx, &eventmodels.Event{ - EventSummary: eventmodels.EventSummary{ - ResourceType: common.ResourceApplication, - EventType: eventmodels.ApplicationUpdated, - ResourceID: applicationModel.ID, - }, - }); err != nil { - log.Warningf(ctx, "failed to create event, err: %s", err.Error()) - } + c.recordApplicationEvent(ctx, applicationModel.ID, eventmodels.ApplicationUpdated) // 6. get fullPath group, err := c.groupSvc.GetChildByID(ctx, appExistsInDB.GroupID) @@ -591,15 +567,7 @@ func (c *controller) UpdateApplicationV2(ctx context.Context, id uint, } // 6. record event - if _, err := c.eventMgr.CreateEvent(ctx, &eventmodels.Event{ - EventSummary: eventmodels.EventSummary{ - ResourceType: common.ResourceApplication, - EventType: eventmodels.ApplicationUpdated, - ResourceID: appExistsInDB.ID, - }, - }); err != nil { - log.Warningf(ctx, "failed to create event, err: %s", err.Error()) - } + c.recordApplicationEvent(ctx, appExistsInDB.ID, eventmodels.ApplicationUpdated) return err } @@ -652,15 +620,7 @@ func (c *controller) DeleteApplication(ctx context.Context, id uint, hard bool) } // 4. record event - if _, err := c.eventMgr.CreateEvent(ctx, &eventmodels.Event{ - EventSummary: eventmodels.EventSummary{ - ResourceType: common.ResourceApplication, - EventType: eventmodels.ApplicationDeleted, - ResourceID: id, - }, - }); err != nil { - log.Warningf(ctx, "failed to create event, err: %s", err.Error()) - } + c.recordApplicationEvent(ctx, id, eventmodels.ApplicationDeleted) return nil } @@ -895,3 +855,36 @@ func (c *controller) GetApplicationPipelineStats(ctx context.Context, applicatio return c.pipelinemanager.ListPipelineStats(ctx, app.Name, cluster, pageNumber, pageSize) } + +func (c *controller) recordApplicationEvent(ctx context.Context, applicationID uint, eventType string) { + if _, err := c.eventMgr.CreateEvent(ctx, &eventmodels.Event{ + EventSummary: eventmodels.EventSummary{ + ResourceType: common.ResourceApplication, + ResourceID: applicationID, + EventType: eventType, + }, + }); err != nil { + log.Warningf(ctx, "failed to create event, err: %s", err.Error()) + } +} + +func (c *controller) recordMemberCreatedEvent(ctx context.Context, applicationID uint) { + members, err := c.memberManager.ListDirectMember(ctx, membermodels.TypeApplication, applicationID) + if err != nil { + log.Warningf(ctx, "failed to list members of application, err: %s", err.Error()) + return + } + events := make([]*eventmodels.Event, len(members)) + for _, m := range members { + events = append(events, &eventmodels.Event{ + EventSummary: eventmodels.EventSummary{ + ResourceType: common.ResourceMember, + ResourceID: m.ID, + EventType: eventmodels.MemberCreated, + }, + }) + } + if _, err := c.eventMgr.CreateEvent(ctx, events...); err != nil { + log.Warningf(ctx, "failed to create event, err: %s", err.Error()) + } +} diff --git a/core/controller/cluster/controller_basic.go b/core/controller/cluster/controller_basic.go index b71d86798..9a6641f00 100644 --- a/core/controller/cluster/controller_basic.go +++ b/core/controller/cluster/controller_basic.go @@ -553,16 +553,8 @@ func (c *controller) CreateCluster(ctx context.Context, applicationID uint, envi r.TemplateInput.Pipeline, r.TemplateInput.Application) // 11. record event - if _, err := c.eventMgr.CreateEvent(ctx, &eventmodels.Event{ - EventSummary: eventmodels.EventSummary{ - ResourceType: common.ResourceCluster, - EventType: eventmodels.ClusterCreated, - ResourceID: ret.ID, - }, - }); err != nil { - log.Warningf(ctx, "failed to create event, err: %s", err.Error()) - } - + c.recordClusterEvent(ctx, ret.ID, eventmodels.ClusterCreated) + c.recordMemberCreatedEvent(ctx, ret.ID) return ret, nil } @@ -725,15 +717,7 @@ func (c *controller) UpdateCluster(ctx context.Context, clusterID uint, } // 6. record event - if _, err := c.eventMgr.CreateEvent(ctx, &eventmodels.Event{ - EventSummary: eventmodels.EventSummary{ - ResourceType: common.ResourceCluster, - EventType: eventmodels.ClusterUpdated, - ResourceID: cluster.ID, - }, - }); err != nil { - log.Warningf(ctx, "failed to create event, err: %s", err.Error()) - } + c.recordClusterEvent(ctx, cluster.ID, eventmodels.ClusterUpdated) // 7. get full path group, err := c.groupSvc.GetChildByID(ctx, application.GroupID) @@ -917,16 +901,7 @@ func (c *controller) DeleteCluster(ctx context.Context, clusterID uint, hard boo } // 5. record event - if _, err := c.eventMgr.CreateEvent(newctx, &eventmodels.Event{ - EventSummary: eventmodels.EventSummary{ - ResourceType: common.ResourceCluster, - EventType: eventmodels.ClusterDeleted, - ResourceID: clusterID, - }, - ReqID: rid, - }); err != nil { - log.Warningf(newctx, "failed to create event, err: %s", err.Error()) - } + c.recordClusterEvent(newctx, clusterID, eventmodels.ClusterDeleted) }() return nil @@ -997,15 +972,7 @@ func (c *controller) FreeCluster(ctx context.Context, clusterID uint) (err error } // 4. create event - if _, err := c.eventMgr.CreateEvent(newctx, &eventmodels.Event{ - EventSummary: eventmodels.EventSummary{ - ResourceType: common.ResourceCluster, - EventType: eventmodels.ClusterFreed, - ResourceID: clusterID, - }, - }); err != nil { - log.Warningf(newctx, "failed to create event, err: %s", err.Error()) - } + c.recordClusterEvent(newctx, clusterID, eventmodels.ClusterFreed) }() return nil diff --git a/core/controller/cluster/controller_basic_v2.go b/core/controller/cluster/controller_basic_v2.go index 9486e33c8..7365e200c 100644 --- a/core/controller/cluster/controller_basic_v2.go +++ b/core/controller/cluster/controller_basic_v2.go @@ -28,6 +28,7 @@ import ( collectionmodels "github.com/horizoncd/horizon/pkg/collection/models" eventmodels "github.com/horizoncd/horizon/pkg/event/models" "github.com/horizoncd/horizon/pkg/git" + membermodels "github.com/horizoncd/horizon/pkg/member/models" prmodels "github.com/horizoncd/horizon/pkg/pr/models" tagmodels "github.com/horizoncd/horizon/pkg/tag/models" "github.com/horizoncd/horizon/pkg/templaterelease/models" @@ -245,16 +246,8 @@ func (c *controller) CreateClusterV2(ctx context.Context, } // 12. record event - if _, err := c.eventMgr.CreateEvent(ctx, &eventmodels.Event{ - EventSummary: eventmodels.EventSummary{ - ResourceType: common.ResourceCluster, - EventType: eventmodels.ClusterCreated, - ResourceID: cluster.ID, - }, - }); err != nil { - log.Warningf(ctx, "failed to create event, err: %s", err.Error()) - } - + c.recordClusterEvent(ctx, ret.ID, eventmodels.ClusterCreated) + c.recordMemberCreatedEvent(ctx, ret.ID) // 13. customize response return ret, nil } @@ -519,15 +512,7 @@ func (c *controller) UpdateClusterV2(ctx context.Context, clusterID uint, } // 7. record event - if _, err := c.eventMgr.CreateEvent(ctx, &eventmodels.Event{ - EventSummary: eventmodels.EventSummary{ - ResourceType: common.ResourceCluster, - EventType: eventmodels.ClusterUpdated, - ResourceID: cluster.ID, - }, - }); err != nil { - log.Warningf(ctx, "failed to create event, err: %s", err.Error()) - } + c.recordClusterEvent(ctx, cluster.ID, eventmodels.ClusterUpdated) // 8. update cluster in db clusterModel, tags := r.toClusterModel(cluster, expireSeconds, environmentName, @@ -831,6 +816,39 @@ func (c *controller) createPipelineRun(ctx context.Context, clusterID uint, }, nil } +func (c *controller) recordClusterEvent(ctx context.Context, clusterID uint, eventType string) { + if _, err := c.eventMgr.CreateEvent(ctx, &eventmodels.Event{ + EventSummary: eventmodels.EventSummary{ + ResourceType: common.ResourceCluster, + ResourceID: clusterID, + EventType: eventType, + }, + }); err != nil { + log.Warningf(ctx, "failed to create event, err: %s", err.Error()) + } +} + +func (c *controller) recordMemberCreatedEvent(ctx context.Context, clusterID uint) { + members, err := c.memberManager.ListDirectMember(ctx, membermodels.TypeApplicationCluster, clusterID) + if err != nil { + log.Warningf(ctx, "failed to list members of application, err: %s", err.Error()) + return + } + events := make([]*eventmodels.Event, len(members)) + for _, m := range members { + events = append(events, &eventmodels.Event{ + EventSummary: eventmodels.EventSummary{ + ResourceType: common.ResourceMember, + ResourceID: m.ID, + EventType: eventmodels.MemberCreated, + }, + }) + } + if _, err := c.eventMgr.CreateEvent(ctx, events...); err != nil { + log.Warningf(ctx, "failed to create event, err: %s", err.Error()) + } +} + func (c *controller) recordPipelinerunCreatedEvent(ctx context.Context, pr *prmodels.Pipelinerun) { _, err := c.eventMgr.CreateEvent(ctx, &eventmodels.Event{ EventSummary: eventmodels.EventSummary{ diff --git a/core/controller/cluster/controller_internal.go b/core/controller/cluster/controller_internal.go index 49bd1e956..cce84ddf6 100644 --- a/core/controller/cluster/controller_internal.go +++ b/core/controller/cluster/controller_internal.go @@ -152,15 +152,7 @@ func (c *controller) InternalDeploy(ctx context.Context, clusterID uint, } // 10. record event - if _, err := c.eventMgr.CreateEvent(ctx, &eventmodels.Event{ - EventSummary: eventmodels.EventSummary{ - ResourceType: common.ResourceCluster, - EventType: eventmodels.ClusterBuildDeployed, - ResourceID: cluster.ID, - }, - }); err != nil { - log.Warningf(ctx, "failed to create event, err: %s", err.Error()) - } + c.recordClusterEvent(ctx, cluster.ID, eventmodels.ClusterBuildDeployed) return &InternalDeployResponse{ PipelinerunID: pr.ID, diff --git a/core/controller/cluster/controller_internal_v2.go b/core/controller/cluster/controller_internal_v2.go index cc18ee353..d4f876ef0 100644 --- a/core/controller/cluster/controller_internal_v2.go +++ b/core/controller/cluster/controller_internal_v2.go @@ -24,7 +24,6 @@ import ( userauth "github.com/horizoncd/horizon/pkg/authentication/user" "github.com/horizoncd/horizon/pkg/cd" "github.com/horizoncd/horizon/pkg/cluster/gitrepo" - "github.com/horizoncd/horizon/pkg/cluster/models" perror "github.com/horizoncd/horizon/pkg/errors" eventmodels "github.com/horizoncd/horizon/pkg/event/models" prmodels "github.com/horizoncd/horizon/pkg/pr/models" @@ -179,7 +178,13 @@ func (c *controller) InternalDeployV2(ctx context.Context, clusterID uint, } // 10. record cluster event - c.recordClusterEvent(ctx, pr, cluster) + eventType := func() string { + if pr.Action == prmodels.ActionBuildDeploy { + return eventmodels.ClusterBuildDeployed + } + return eventmodels.ClusterDeployed + }() + c.recordClusterEvent(ctx, cluster.ID, eventType) return &InternalDeployResponseV2{ PipelinerunID: pr.ID, @@ -187,24 +192,6 @@ func (c *controller) InternalDeployV2(ctx context.Context, clusterID uint, }, nil } -func (c *controller) recordClusterEvent(ctx context.Context, pr *prmodels.Pipelinerun, cluster *models.Cluster) { - _, err := c.eventMgr.CreateEvent(ctx, &eventmodels.Event{ - EventSummary: eventmodels.EventSummary{ - ResourceType: common.ResourceCluster, - EventType: func() string { - if pr.Action == prmodels.ActionBuildDeploy { - return eventmodels.ClusterBuildDeployed - } - return eventmodels.ClusterDeployed - }(), - ResourceID: cluster.ID, - }, - }) - if err != nil { - log.Warningf(ctx, "failed to create event, err: %s", err.Error()) - } -} - func (c *controller) retrieveClaimsAndUser(ctx context.Context) (*tokenservice.Claims, *usermodel.User, error) { jwtTokenString, err := common.JWTTokenStringFromContext(ctx) if err != nil { diff --git a/core/controller/cluster/controller_operation.go b/core/controller/cluster/controller_operation.go index 4e747fd3b..f3eabba01 100644 --- a/core/controller/cluster/controller_operation.go +++ b/core/controller/cluster/controller_operation.go @@ -103,15 +103,7 @@ func (c *controller) Restart(ctx context.Context, clusterID uint) (_ *Pipelineru } // 5. record event - if _, err := c.eventMgr.CreateEvent(ctx, &eventmodels.Event{ - EventSummary: eventmodels.EventSummary{ - ResourceType: common.ResourceCluster, - EventType: eventmodels.ClusterRestarted, - ResourceID: cluster.ID, - }, - }); err != nil { - log.Warningf(ctx, "failed to create event, err: %s", err.Error()) - } + c.recordClusterEvent(ctx, cluster.ID, eventmodels.ClusterRestarted) return &PipelinerunIDResponse{ PipelinerunID: prCreated.ID, @@ -424,15 +416,7 @@ func (c *controller) Rollback(ctx context.Context, } // 10. record event - if _, err := c.eventMgr.CreateEvent(ctx, &eventmodels.Event{ - EventSummary: eventmodels.EventSummary{ - ResourceType: common.ResourceCluster, - EventType: eventmodels.ClusterRollbacked, - ResourceID: cluster.ID, - }, - }); err != nil { - log.Warningf(ctx, "failed to create event, err: %s", err.Error()) - } + c.recordClusterEvent(ctx, cluster.ID, eventmodels.ClusterRollbacked) return &PipelinerunIDResponse{ PipelinerunID: prCreated.ID, diff --git a/core/controller/member/controller.go b/core/controller/member/controller.go index ce3c55715..f59382922 100644 --- a/core/controller/member/controller.go +++ b/core/controller/member/controller.go @@ -18,8 +18,13 @@ import ( "context" "strconv" + "github.com/horizoncd/horizon/core/common" + eventManager "github.com/horizoncd/horizon/pkg/event/manager" + eventmodels "github.com/horizoncd/horizon/pkg/event/models" + "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/util/log" ) type Controller interface { @@ -40,12 +45,14 @@ func NewController(param *param.Param) Controller { return &controller{ memberService: param.MemberService, convertHelper: New(param), + eventMgr: param.EventMgr, } } type controller struct { memberService memberservice.Service convertHelper ConvertMemberHelp + eventMgr eventManager.Manager } func (c *controller) CreateMember(ctx context.Context, postMember *PostMember) (*Member, error) { @@ -57,6 +64,7 @@ func (c *controller) CreateMember(ctx context.Context, postMember *PostMember) ( if err != nil { return nil, err } + c.recodeMemberEvent(ctx, member, eventmodels.MemberCreated) return retMember, nil } @@ -69,11 +77,21 @@ func (c *controller) UpdateMember(ctx context.Context, id uint, role string) (*M if err != nil { return nil, err } + c.recodeMemberEvent(ctx, member, eventmodels.MemberUpdated) return retMember, nil } func (c *controller) RemoveMember(ctx context.Context, id uint) error { - return c.memberService.RemoveMember(ctx, id) + member, err := c.memberService.GetMember(ctx, id) + if err != nil { + return err + } + err = c.memberService.RemoveMember(ctx, id) + if err != nil { + return err + } + c.recodeMemberEvent(ctx, member, eventmodels.MemberDeleted) + return nil } func (c *controller) ListMember(ctx context.Context, resourceType string, id uint) ([]Member, error) { @@ -106,3 +124,16 @@ func (c *controller) GetMemberOfResource(ctx context.Context, resourceType strin } return retMember, nil } + +func (c *controller) recodeMemberEvent(ctx context.Context, member *models.Member, eventType string) { + _, err := c.eventMgr.CreateEvent(ctx, &eventmodels.Event{ + EventSummary: eventmodels.EventSummary{ + ResourceID: member.ID, + ResourceType: common.ResourceMember, + EventType: eventType, + }, + }) + if err != nil { + log.Warningf(ctx, "failed to create event, err: %s", err.Error()) + } +} diff --git a/core/http/api/v2/member/apis.go b/core/http/api/v2/member/apis.go index fbf5507fe..89cd7bab6 100644 --- a/core/http/api/v2/member/apis.go +++ b/core/http/api/v2/member/apis.go @@ -21,7 +21,9 @@ import ( "github.com/horizoncd/horizon/core/common" "github.com/horizoncd/horizon/core/controller/member" + herrors "github.com/horizoncd/horizon/core/errors" memberctx "github.com/horizoncd/horizon/pkg/context" + perror "github.com/horizoncd/horizon/pkg/errors" membermodels "github.com/horizoncd/horizon/pkg/member/models" "github.com/horizoncd/horizon/pkg/rbac/role" "github.com/horizoncd/horizon/pkg/server/response" @@ -234,6 +236,10 @@ func (a *API) DeleteMember(c *gin.Context) { } err = a.memberCtrl.RemoveMember(c, uint(uintID)) if err != nil { + if _, ok := perror.Cause(err).(*herrors.HorizonErrNotFound); ok { + response.AbortWithRequestError(c, common.InvalidRequestParam, err.Error()) + return + } response.AbortWithError(c, err) return } diff --git a/mock/pkg/member/manager/mock_manager.go b/mock/pkg/member/manager/mock_manager.go index 971d1ad06..124e70eea 100644 --- a/mock/pkg/member/manager/mock_manager.go +++ b/mock/pkg/member/manager/mock_manager.go @@ -108,6 +108,21 @@ func (mr *MockManagerMockRecorder) GetByID(ctx, memberID interface{}) *gomock.Ca return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByID", reflect.TypeOf((*MockManager)(nil).GetByID), ctx, memberID) } +// GetByIDIncludeSoftDelete mocks base method. +func (m *MockManager) GetByIDIncludeSoftDelete(ctx context.Context, memberID uint) (*models.Member, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetByIDIncludeSoftDelete", ctx, memberID) + ret0, _ := ret[0].(*models.Member) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetByIDIncludeSoftDelete indicates an expected call of GetByIDIncludeSoftDelete. +func (mr *MockManagerMockRecorder) GetByIDIncludeSoftDelete(ctx, memberID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByIDIncludeSoftDelete", reflect.TypeOf((*MockManager)(nil).GetByIDIncludeSoftDelete), ctx, memberID) +} + // HardDeleteMemberByResourceTypeID mocks base method. func (m *MockManager) HardDeleteMemberByResourceTypeID(ctx context.Context, resourceType string, resourceID uint) error { m.ctrl.T.Helper() diff --git a/pkg/event/manager/manager.go b/pkg/event/manager/manager.go index 0ed2c3219..2a456d5ca 100644 --- a/pkg/event/manager/manager.go +++ b/pkg/event/manager/manager.go @@ -137,6 +137,9 @@ var supportedEvents = map[string]string{ models.ClusterAction: "Cluster has triggered an action", models.ClusterPodsRescheduled: "Pods has been deleted to reschedule", models.ClusterKubernetesEvent: "Kubernetes event associated with cluster has been triggered", + models.MemberCreated: "New member has been created", + models.MemberUpdated: "Member has been updated", + models.MemberDeleted: "Member has been deleted", models.PipelinerunCreated: "New pipelinerun has been created", } diff --git a/pkg/event/models/event.go b/pkg/event/models/event.go index 5d356d3ef..13e2389b3 100644 --- a/pkg/event/models/event.go +++ b/pkg/event/models/event.go @@ -38,6 +38,9 @@ const ( ClusterFreed string = "clusters_freed" ClusterKubernetesEvent string = "clusters_kubernetes_event" ClusterAction = "clusters_action" + MemberCreated string = "members_created" + MemberUpdated string = "members_updated" + MemberDeleted string = "members_deleted" PipelinerunCreated string = "pipelineruns_created" // TODO: add group events ) diff --git a/pkg/eventhandler/wlgenerator/wlgenerator.go b/pkg/eventhandler/wlgenerator/wlgenerator.go index e2d29dd11..a1cdad886 100644 --- a/pkg/eventhandler/wlgenerator/wlgenerator.go +++ b/pkg/eventhandler/wlgenerator/wlgenerator.go @@ -20,6 +20,8 @@ import ( "fmt" "net/http" + membermanager "github.com/horizoncd/horizon/pkg/member" + membermodels "github.com/horizoncd/horizon/pkg/member/models" prmodels "github.com/horizoncd/horizon/pkg/pr/models" "gopkg.in/yaml.v3" @@ -56,6 +58,7 @@ type MessageContent struct { Application *ApplicationInfo `json:"application,omitempty"` Cluster *ClusterInfo `json:"cluster,omitempty"` Pipelinerun *PipelinerunInfo `json:"pipelinerun,omitempty"` + Member *MemberInfo `json:"member,omitempty"` EventType string `json:"eventType,omitempty"` User *usermodels.UserBasic `json:"user,omitempty"` Extra *string `json:"extra,omitempty"` @@ -91,6 +94,15 @@ type PipelinerunInfo struct { GitRefType string `json:"gitRefType,omitempty"` } +// MemberInfo contains basic info of member +type MemberInfo struct { + ResourceCommonInfo + ResourceID uint + ResourceType membermodels.ResourceType + Role string + MemberNameID uint +} + // WebhookLogGenerator generates webhook logs by events type WebhookLogGenerator struct { webhookMgr webhookmanager.Manager @@ -99,6 +111,7 @@ type WebhookLogGenerator struct { applicationMgr applicationmanager.Manager clusterMgr clustermanager.Manager prMgr *prmanager.PRManager + memberMgr membermanager.Manager userMgr usermanager.Manager } @@ -110,6 +123,7 @@ func NewWebhookLogGenerator(manager *managerparam.Manager) *WebhookLogGenerator applicationMgr: manager.ApplicationMgr, clusterMgr: manager.ClusterMgr, prMgr: manager.PRMgr, + memberMgr: manager.MemberMgr, userMgr: manager.UserMgr, } } @@ -120,6 +134,7 @@ type messageDependency struct { application *applicationmodels.Application cluster *clustermodels.Cluster pipelinerun *prmodels.Pipelinerun + member *membermodels.Member } // listSystemResources lists root group(0) as system resource @@ -184,6 +199,27 @@ func (w *WebhookLogGenerator) listAssociatedResourcesOfPipelinerun(ctx context.C return pr, cluster, resources } +// listAssociatedResourcesOfMember gets member by id and list all the parent resources +func (w *WebhookLogGenerator) listAssociatedResourcesOfMember(ctx context.Context, + id uint) (*membermodels.Member, map[string][]uint) { + member, err := w.memberMgr.GetByIDIncludeSoftDelete(ctx, id) + if err != nil { + log.Warningf(ctx, "member %d is not exist", id) + return nil, nil + } + var resources map[string][]uint + switch member.ResourceType { + case membermodels.TypeApplication: + _, resources = w.listAssociatedResourcesOfApp(ctx, member.ResourceID) + case membermodels.TypeApplicationCluster: + _, _, resources = w.listAssociatedResourcesOfCluster(ctx, member.ResourceID) + default: + // TODO: support member event of groups and templates + log.Warningf(ctx, "member event of resource type %s is unsupported yet", member.ResourceType) + } + return member, resources +} + // listAssociatedResources list all the associated resources of event to find all the webhooks func (w *WebhookLogGenerator) listAssociatedResources(ctx context.Context, e *models.Event) (*messageDependency, map[string][]uint) { @@ -192,6 +228,7 @@ func (w *WebhookLogGenerator) listAssociatedResources(ctx context.Context, cluster *clustermodels.Cluster application *applicationmodels.Application pr *prmodels.Pipelinerun + member *membermodels.Member dep = &messageDependency{} ) @@ -208,6 +245,9 @@ func (w *WebhookLogGenerator) listAssociatedResources(ctx context.Context, dep.cluster = cluster dep.pipelinerun = pr log.Debugf(ctx, "dep: %+v", dep) + case common.ResourceMember: + member, resources = w.listAssociatedResourcesOfMember(ctx, e.ResourceID) + dep.member = member default: log.Infof(ctx, "resource type %s is unsupported", e.ResourceType) @@ -288,6 +328,19 @@ func (w *WebhookLogGenerator) makeRequestBody(ctx context.Context, dep *messageD } } + if dep.event.ResourceType == common.ResourceMember && + dep.member != nil { + message.Member = &MemberInfo{ + ResourceCommonInfo: ResourceCommonInfo{ + ID: dep.member.ID, + }, + ResourceID: dep.member.ResourceID, + ResourceType: dep.member.ResourceType, + Role: dep.member.Role, + MemberNameID: dep.member.MemberNameID, + } + } + reqBody, err := json.Marshal(message) if err != nil { log.Errorf(ctx, fmt.Sprintf("failed to marshal message, error: %+v", err)) @@ -344,6 +397,7 @@ func (w *WebhookLogGenerator) Process(ctx context.Context, events []*models.Even application: dependency.application, cluster: dependency.cluster, pipelinerun: dependency.pipelinerun, + member: dependency.member, } conditionsToQuery[event.ID] = append(conditionsToQuery[event.ID], webhook.ID) } diff --git a/pkg/member/dao/dao.go b/pkg/member/dao/dao.go index 3478911ab..019c8cd70 100644 --- a/pkg/member/dao/dao.go +++ b/pkg/member/dao/dao.go @@ -34,6 +34,7 @@ type DAO interface { Get(ctx context.Context, resourceType models.ResourceType, resourceID uint, memberType models.MemberType, memberInfo uint) (*models.Member, error) GetByID(ctx context.Context, memberID uint) (*models.Member, error) + GetByIDIncludeSoftDelete(ctx context.Context, memberID uint) (*models.Member, error) Delete(ctx context.Context, memberID uint) error HardDelete(ctx context.Context, resourceType string, resourceID uint) error DeleteByMemberNameID(ctx context.Context, memberNameID uint) error @@ -81,6 +82,24 @@ func (d *dao) GetByID(ctx context.Context, memberID uint) (*models.Member, error var member models.Member result := d.db.WithContext(ctx).Raw(common.MemberQueryByID, memberID).Scan(&member) if result.Error != nil { + if result.Error == gorm.ErrRecordNotFound { + return nil, herrors.NewErrNotFound(herrors.MemberInfoInDB, result.Error.Error()) + } + return nil, result.Error + } + if result.RowsAffected == 0 { + return nil, nil + } + return &member, nil +} + +func (d *dao) GetByIDIncludeSoftDelete(ctx context.Context, memberID uint) (*models.Member, error) { + var member models.Member + result := d.db.WithContext(ctx).Where("id = ?", memberID).First(&member) + if result.Error != nil { + if result.Error == gorm.ErrRecordNotFound { + return nil, herrors.NewErrNotFound(herrors.MemberInfoInDB, result.Error.Error()) + } return nil, result.Error } if result.RowsAffected == 0 { diff --git a/pkg/member/manager.go b/pkg/member/manager.go index d31febf65..f8307a188 100644 --- a/pkg/member/manager.go +++ b/pkg/member/manager.go @@ -34,6 +34,9 @@ type Manager interface { // GetByID get the member by ID GetByID(ctx context.Context, memberID uint) (*models.Member, error) + // GetByIDIncludeSoftDelete gets the member by ID including soft delete + GetByIDIncludeSoftDelete(ctx context.Context, memberID uint) (*models.Member, error) + // UpdateByID update a member by memberID UpdateByID(ctx context.Context, id uint, role string) (*models.Member, error) @@ -85,6 +88,10 @@ func (m *manager) GetByID(ctx context.Context, memberID uint) (*models.Member, e return m.dao.GetByID(ctx, memberID) } +func (m *manager) GetByIDIncludeSoftDelete(ctx context.Context, memberID uint) (*models.Member, error) { + return m.dao.GetByIDIncludeSoftDelete(ctx, memberID) +} + func (m *manager) UpdateByID(ctx context.Context, memberID uint, role string) (*models.Member, error) { return m.dao.UpdateByID(ctx, memberID, role) }