Skip to content

Commit

Permalink
feat: add webhook events of members
Browse files Browse the repository at this point in the history
Signed-off-by: xu.zhu <[email protected]>
  • Loading branch information
xuzhu-591 committed Sep 24, 2023
1 parent d09b910 commit 098c80c
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 3 deletions.
2 changes: 2 additions & 0 deletions core/common/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ const (

ResourceWebhook = "webhooks"
ResourceWebhookLog = "webhooklogs"

ResourceMember = "members"
)

const (
Expand Down
25 changes: 23 additions & 2 deletions core/controller/application/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ func (c *controller) CreateApplication(ctx context.Context, groupID uint,
}); err != nil {
log.Warningf(ctx, "failed to create event, err: %s", err.Error())
}

c.recordMemberCreatedEvent(ctx, ret.ID)
return ret, nil
}

Expand Down Expand Up @@ -445,10 +445,31 @@ func (c *controller) CreateApplicationV2(ctx context.Context, groupID uint,
}); err != nil {
log.Warningf(ctx, "failed to create event, err: %s", err.Error())
}

c.recordMemberCreatedEvent(ctx, ret.ID)
return ret, nil
}

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())
}
}

func (c *controller) UpdateApplication(ctx context.Context, id uint,
request *UpdateApplicationRequest) (_ *GetApplicationResponse, err error) {
const op = "application controller: update application"
Expand Down
33 changes: 32 additions & 1 deletion core/controller/member/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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) {
Expand All @@ -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
}

Expand All @@ -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) {
Expand Down Expand Up @@ -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())
}
}
6 changes: 6 additions & 0 deletions core/http/api/v2/member/apis.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
Expand Down
15 changes: 15 additions & 0 deletions mock/pkg/member/manager/mock_manager.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions pkg/event/manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/event/models/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down
54 changes: 54 additions & 0 deletions pkg/eventhandler/wlgenerator/wlgenerator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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"`
Expand Down Expand Up @@ -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
Expand All @@ -99,6 +111,7 @@ type WebhookLogGenerator struct {
applicationMgr applicationmanager.Manager
clusterMgr clustermanager.Manager
prMgr *prmanager.PRManager
memberMgr membermanager.Manager
userMgr usermanager.Manager
}

Expand All @@ -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,
}
}
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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{}
)

Expand All @@ -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)
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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)
}
Expand Down
19 changes: 19 additions & 0 deletions pkg/member/dao/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
7 changes: 7 additions & 0 deletions pkg/member/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)
}
Expand Down

0 comments on commit 098c80c

Please sign in to comment.