drone/registry/services/webhook/handler_artifact.go

258 lines
8.7 KiB
Go

// Copyright 2023 Harness, Inc.
//
// 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 webhook
import (
"context"
"fmt"
gitnesswebhook "github.com/harness/gitness/app/services/webhook"
"github.com/harness/gitness/events"
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
registryevents "github.com/harness/gitness/registry/app/events"
registrytypes "github.com/harness/gitness/registry/types"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// ArtifactEventPayload describes the payload of Artifact related webhook triggers.
type ArtifactEventPayload struct {
Trigger enum.WebhookTrigger `json:"trigger"`
Registry RegistryInfo `json:"registry"`
Principal gitnesswebhook.PrincipalInfo `json:"principal"`
ArtifactInfo *registryevents.ArtifactInfo `json:"artifact_info"`
ArtifactChangeInfo *registryevents.ArtifactChangeInfo `json:"artifact_change_info"`
}
type RegistryInfo struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
URL string `json:"url"`
}
// handleEventArtifactCreated handles branch created events
// and triggers branch created webhooks for the source repo.
func (s *Service) handleEventArtifactCreated(
ctx context.Context,
event *events.Event[*registryevents.ArtifactCreatedPayload],
) error {
return s.triggerForEventWithArtifact(ctx, enum.WebhookTriggerArtifactCreated,
event.ID, event.Payload.PrincipalID, event.Payload.RegistryID,
func(
principal *types.Principal,
registry *registrytypes.Registry,
) (any, error) {
space, err := s.spaceStore.Find(ctx, registry.ParentID)
if err != nil {
return nil, err
}
return &ArtifactEventPayload{
Trigger: enum.WebhookTriggerArtifactCreated,
Registry: RegistryInfo{
ID: registry.ID,
Name: registry.Name,
Description: registry.Description,
URL: s.urlProvider.GenerateUIRegistryURL(ctx, space.Path, registry.Name),
},
Principal: gitnesswebhook.PrincipalInfo{
ID: principal.ID,
UID: principal.UID,
DisplayName: principal.DisplayName,
Email: principal.Email,
Type: principal.Type,
Created: principal.Created,
Updated: principal.Updated,
},
ArtifactInfo: getArtifactInfo(event.Payload.Artifact),
}, nil
})
}
// handleEventArtifactUpdated handles branch updated events
// and triggers branch updated webhooks for the source repo.
func (s *Service) handleEventArtifactUpdated(
ctx context.Context,
event *events.Event[*registryevents.ArtifactUpdatedPayload],
) error {
return s.triggerForEventWithArtifact(ctx, enum.WebhookTriggerArtifactUpdated,
event.ID, event.Payload.PrincipalID, event.Payload.RegistryID,
func(
principal *types.Principal,
registry *registrytypes.Registry,
) (any, error) {
space, err := s.spaceStore.Find(ctx, registry.ParentID)
if err != nil {
return nil, err
}
return &ArtifactEventPayload{
Trigger: enum.WebhookTriggerArtifactUpdated,
Registry: RegistryInfo{
ID: registry.ID,
Name: registry.Name,
Description: registry.Description,
URL: s.urlProvider.GenerateUIRegistryURL(ctx, space.Path, registry.Name),
},
Principal: gitnesswebhook.PrincipalInfo{
ID: principal.ID,
UID: principal.UID,
DisplayName: principal.DisplayName,
Email: principal.Email,
Type: principal.Type,
Created: principal.Created,
Updated: principal.Updated,
},
ArtifactInfo: getArtifactInfo(event.Payload.ArtifactChange.New),
ArtifactChangeInfo: getArtifactInfoForArtifactUpdated(*event.Payload),
}, nil
})
}
// handleEventArtifactDeleted handles branch deleted events
// and triggers branch deleted webhooks for the source repo.
func (s *Service) handleEventArtifactDeleted(
ctx context.Context,
event *events.Event[*registryevents.ArtifactDeletedPayload],
) error {
return s.triggerForEventWithArtifact(ctx, enum.WebhookTriggerArtifactDeleted,
event.ID, event.Payload.PrincipalID, event.Payload.RegistryID,
func(
principal *types.Principal,
registry *registrytypes.Registry,
) (any, error) {
space, err := s.spaceStore.Find(ctx, registry.ParentID)
if err != nil {
return nil, err
}
return &ArtifactEventPayload{
Trigger: enum.WebhookTriggerArtifactDeleted,
Registry: RegistryInfo{
ID: registry.ID,
Name: registry.Name,
Description: registry.Description,
URL: s.urlProvider.GenerateUIRegistryURL(ctx, space.Path, registry.Name),
},
Principal: gitnesswebhook.PrincipalInfo{
ID: principal.ID,
UID: principal.UID,
DisplayName: principal.DisplayName,
Email: principal.Email,
Type: principal.Type,
Created: principal.Created,
Updated: principal.Updated,
},
ArtifactInfo: getArtifactInfo(event.Payload.Artifact),
}, nil
})
}
func getArtifactInfo(eventArtifact registryevents.Artifact) *registryevents.ArtifactInfo {
artifactInfo := registryevents.ArtifactInfo{}
if dockerArtifact, ok := eventArtifact.(*registryevents.DockerArtifact); ok {
artifactInfo.Type = artifact.PackageTypeDOCKER
artifactInfo.Name = dockerArtifact.Name
artifactInfo.Version = dockerArtifact.Tag
artifactInfo.Artifact = &dockerArtifact
} else if helmArtifact, ok := eventArtifact.(*registryevents.HelmArtifact); ok {
artifactInfo.Type = artifact.PackageTypeHELM
artifactInfo.Name = helmArtifact.Name
artifactInfo.Version = helmArtifact.Tag
artifactInfo.Artifact = &helmArtifact
}
return &artifactInfo
}
func getArtifactInfoForArtifactUpdated(
payload registryevents.ArtifactUpdatedPayload,
) *registryevents.ArtifactChangeInfo {
artifactInfo := registryevents.ArtifactChangeInfo{
Type: payload.ArtifactType,
}
if dockerArtifact, ok := payload.ArtifactChange.New.(*registryevents.DockerArtifact); ok {
artifactInfo.Name = dockerArtifact.Name
} else if helmArtifact, ok := payload.ArtifactChange.New.(*registryevents.HelmArtifact); ok {
artifactInfo.Name = helmArtifact.Name
}
artifactInfo.ArtifactChange = &payload.ArtifactChange
return &artifactInfo
}
// triggerForEventWithArtifact triggers all webhooks for the given registry and triggerType
// using the eventID to generate a deterministic triggerID and using the output of bodyFn as payload.
// The method tries to find the registry and principal and provides both to the bodyFn to generate the body.
// NOTE: technically we could avoid this call if we send the data via the event (though then events will get big).
func (s *Service) triggerForEventWithArtifact(
ctx context.Context,
triggerType enum.WebhookTrigger,
eventID string,
principalID int64,
registryID int64,
createBodyFn func(*types.Principal, *registrytypes.Registry) (any, error),
) error {
principal, err := s.WebhookExecutor.FindPrincipalForEvent(ctx, principalID)
if err != nil {
return err
}
registry, err := s.registryRepository.Get(ctx, registryID)
if err != nil {
return err
}
body, err := createBodyFn(principal, registry)
if err != nil {
return fmt.Errorf("body creation function failed: %w", err)
}
parents, err := s.getParentInfoRegistry(ctx, registry.ID, true)
if err != nil {
return fmt.Errorf("failed to get webhook parent info: %w", err)
}
return s.WebhookExecutor.TriggerForEvent(ctx, eventID, parents, triggerType, body)
}
func (s *Service) getParentInfoRegistry(
ctx context.Context,
registryID int64,
inherited bool,
) ([]types.WebhookParentInfo, error) {
var parents []types.WebhookParentInfo
parents = append(parents, types.WebhookParentInfo{
ID: registryID,
Type: enum.WebhookParentRegistry,
})
if inherited {
registry, err := s.registryRepository.Get(ctx, registryID)
if err != nil {
return nil, fmt.Errorf("failed to get registry: %w", err)
}
ids, err := s.spaceStore.GetAncestorIDs(ctx, registry.ParentID)
if err != nil {
return nil, fmt.Errorf("failed to get parent space ids: %w", err)
}
for _, id := range ids {
parents = append(parents, types.WebhookParentInfo{
Type: enum.WebhookParentSpace,
ID: id,
})
}
}
return parents, nil
}