Skip to content

Commit 86f36be

Browse files
committed
initial changes
1 parent 3de76d6 commit 86f36be

File tree

9 files changed

+183
-3
lines changed

9 files changed

+183
-3
lines changed

api/swagger.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5597,6 +5597,44 @@ paths:
55975597
default:
55985598
$ref: "#/components/responses/ServerError"
55995599

5600+
/repositories/{repository}/refs/{ref}/actions/{action}/triggers:
5601+
post:
5602+
tags:
5603+
- actions
5604+
operationId: triggerAction
5605+
summary: manually trigger an action
5606+
parameters:
5607+
- in: path
5608+
name: repository
5609+
required: true
5610+
schema:
5611+
type: string
5612+
- in: path
5613+
name: ref
5614+
required: true
5615+
schema:
5616+
type: string
5617+
- in: path
5618+
name: action
5619+
required: true
5620+
schema:
5621+
type: string
5622+
responses:
5623+
202:
5624+
description: action accepted for background execution
5625+
400:
5626+
$ref: "#/components/responses/BadRequest"
5627+
401:
5628+
$ref: "#/components/responses/Unauthorized"
5629+
403:
5630+
$ref: "#/components/responses/Forbidden"
5631+
404:
5632+
$ref: "#/components/responses/NotFound"
5633+
420:
5634+
description: too many requests
5635+
default:
5636+
$ref: "#/components/responses/ServerError"
5637+
56005638
/repositories/{repository}/metadata/object/{type}/{object_id}:
56015639
parameters:
56025640
- in: path

docs/src/assets/js/swagger.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5597,6 +5597,44 @@ paths:
55975597
default:
55985598
$ref: "#/components/responses/ServerError"
55995599

5600+
/repositories/{repository}/refs/{ref}/actions/{action}/triggers:
5601+
post:
5602+
tags:
5603+
- actions
5604+
operationId: triggerAction
5605+
summary: manually trigger an action
5606+
parameters:
5607+
- in: path
5608+
name: repository
5609+
required: true
5610+
schema:
5611+
type: string
5612+
- in: path
5613+
name: ref
5614+
required: true
5615+
schema:
5616+
type: string
5617+
- in: path
5618+
name: action
5619+
required: true
5620+
schema:
5621+
type: string
5622+
responses:
5623+
202:
5624+
description: action accepted for background execution
5625+
400:
5626+
$ref: "#/components/responses/BadRequest"
5627+
401:
5628+
$ref: "#/components/responses/Unauthorized"
5629+
403:
5630+
$ref: "#/components/responses/Forbidden"
5631+
404:
5632+
$ref: "#/components/responses/NotFound"
5633+
420:
5634+
description: too many requests
5635+
default:
5636+
$ref: "#/components/responses/ServerError"
5637+
56005638
/repositories/{repository}/metadata/object/{type}/{object_id}:
56015639
parameters:
56025640
- in: path

pkg/actions/action.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,10 @@ func isEventSupported(event graveler.EventType) bool {
8484
graveler.EventTypePreDeleteTag,
8585
graveler.EventTypePostDeleteTag,
8686
graveler.EventTypePreRevert,
87-
graveler.EventTypePostRevert:
87+
graveler.EventTypePostRevert,
88+
graveler.EventTypePreCherryPick,
89+
graveler.EventTypePostCherryPick,
90+
graveler.EventTypeManualTrigger:
8891
return true
8992
}
9093
return false

pkg/actions/errors.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ var (
1111
ErrUnknownHookType = errors.New("unknown hook type")
1212
ErrInvalidAction = errors.New("invalid action")
1313
ErrInvalidEventParameter = errors.New("invalid event parameter")
14+
ErrActionsDisabled = errors.New("actions are disabled")
1415
)

pkg/actions/service.go

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/hashicorp/go-multierror"
1717
"github.com/treeverse/lakefs/pkg/actions/lua/hook"
1818
"github.com/treeverse/lakefs/pkg/auth"
19+
"github.com/treeverse/lakefs/pkg/catalog"
1920
"github.com/treeverse/lakefs/pkg/graveler"
2021
"github.com/treeverse/lakefs/pkg/kv"
2122
"github.com/treeverse/lakefs/pkg/logging"
@@ -210,6 +211,7 @@ type Service interface {
210211
GetTaskResult(ctx context.Context, repositoryID string, runID string, hookRunID string) (*TaskResult, error)
211212
ListRunResults(ctx context.Context, repositoryID string, branchID, commitID string, after string) (RunResultIterator, error)
212213
ListRunTaskResults(ctx context.Context, repositoryID string, runID string, after string) (TaskResultIterator, error)
214+
TriggerAction(ctx context.Context, repository *catalog.Repository, ref string, actionName string) (string, error)
213215
graveler.HooksHandler
214216
}
215217

@@ -303,7 +305,23 @@ func (s *StoreService) loadMatchedActions(ctx context.Context, record graveler.H
303305
if err != nil {
304306
return nil, err
305307
}
306-
return MatchedActions(actions, spec)
308+
309+
// First filter by event type and branch
310+
matchedActions, err := MatchedActions(actions, spec)
311+
if err != nil {
312+
return nil, err
313+
}
314+
315+
// If ActionName is specified in the record, filter by it
316+
if record.ActionName != "" {
317+
for _, action := range matchedActions {
318+
if action.Name == record.ActionName {
319+
return []*Action{action}, nil
320+
}
321+
}
322+
return nil, ErrNotFound
323+
}
324+
return matchedActions, nil
307325
}
308326

309327
func (s *StoreService) allocateTasks(runID string, actions []*Action) ([][]*Task, error) {
@@ -606,6 +624,53 @@ func (s *StoreService) PostCherryPickHook(ctx context.Context, record graveler.H
606624
return nil
607625
}
608626

627+
func (s *StoreService) TriggerAction(ctx context.Context, repository *catalog.Repository, ref string, actionName string) (string, error) {
628+
if !s.cfg.Enabled {
629+
logging.FromContext(ctx).WithField("repository", repository.Name).Debug("Hooks are disabled, skipping manual trigger")
630+
// Return NoRunID when actions are disabled - the caller should check the error
631+
return "", ErrActionsDisabled
632+
}
633+
634+
// Construct graveler.RepositoryRecord from catalog.Repository
635+
repoRecord := &graveler.RepositoryRecord{
636+
RepositoryID: graveler.RepositoryID(repository.Name),
637+
Repository: &graveler.Repository{
638+
StorageID: graveler.StorageID(repository.StorageID),
639+
StorageNamespace: graveler.StorageNamespace(repository.StorageNamespace),
640+
CreationDate: repository.CreationDate,
641+
DefaultBranchID: graveler.BranchID(repository.DefaultBranch),
642+
State: graveler.RepositoryState_ACTIVE,
643+
InstanceUID: "", // Not available from catalog API
644+
ReadOnly: repository.ReadOnly,
645+
},
646+
}
647+
648+
runID := s.NewRunID()
649+
650+
// Create hook record for manual trigger with all required fields
651+
record := graveler.HookRecord{
652+
RunID: runID,
653+
EventType: graveler.EventTypeManualTrigger,
654+
Repository: repoRecord,
655+
SourceRef: graveler.Ref(ref),
656+
BranchID: graveler.BranchID(ref), // For manual triggers, we use the ref as branch
657+
ActionName: actionName,
658+
}
659+
660+
// verify that the action exists before scheduling the run
661+
spec := MatchSpec{
662+
EventType: record.EventType,
663+
BranchID: record.BranchID,
664+
}
665+
_, err := s.loadMatchedActions(ctx, record, spec)
666+
if err != nil {
667+
return "", ErrNotFound
668+
}
669+
// Run the action asynchronously
670+
s.asyncRun(ctx, record)
671+
return runID, nil
672+
}
673+
609674
func (s *StoreService) NewRunID() string {
610675
return s.idGen.NewRunID()
611676
}

pkg/api/controller.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ type actionsHandler interface {
8686
GetTaskResult(ctx context.Context, repositoryID, runID, hookRunID string) (*actions.TaskResult, error)
8787
ListRunResults(ctx context.Context, repositoryID, branchID, commitID, after string) (actions.RunResultIterator, error)
8888
ListRunTaskResults(ctx context.Context, repositoryID, runID, after string) (actions.TaskResultIterator, error)
89+
TriggerAction(ctx context.Context, repository *catalog.Repository, ref, actionName string) (string, error)
8990
}
9091

9192
type Migrator interface {
@@ -2703,6 +2704,34 @@ func (c *Controller) GetRun(w http.ResponseWriter, r *http.Request, repository,
27032704
writeResponse(w, r, http.StatusOK, response)
27042705
}
27052706

2707+
func (c *Controller) TriggerAction(w http.ResponseWriter, r *http.Request, repository, refParam, action string) {
2708+
if !c.authorize(w, r, permissions.Node{
2709+
Permission: permissions.Permission{
2710+
Action: permissions.TriggerActionsAction,
2711+
Resource: permissions.RepoArn(repository),
2712+
},
2713+
}) {
2714+
return
2715+
}
2716+
ctx := r.Context()
2717+
c.LogAction(ctx, "actions_trigger", r, repository, refParam, "")
2718+
2719+
// Get repository information
2720+
repo, err := c.Catalog.GetRepository(ctx, repository)
2721+
if c.handleAPIError(ctx, w, r, err) {
2722+
return
2723+
}
2724+
2725+
// Trigger the action
2726+
_, err = c.Actions.TriggerAction(ctx, repo, refParam, action)
2727+
if c.handleAPIError(ctx, w, r, err) {
2728+
return
2729+
}
2730+
2731+
// For async execution, just return 202 Accepted
2732+
writeResponse(w, r, http.StatusAccepted, nil)
2733+
}
2734+
27062735
func (c *Controller) ListRunHooks(w http.ResponseWriter, r *http.Request, repository, runID string, params apigen.ListRunHooksParams) {
27072736
if !c.authorize(w, r, permissions.Node{
27082737
Permission: permissions.Permission{
@@ -2951,7 +2980,8 @@ func (c *Controller) handleAPIErrorCallback(ctx context.Context, w http.Response
29512980

29522981
case errors.Is(err, block.ErrForbidden),
29532982
errors.Is(err, graveler.ErrProtectedBranch),
2954-
errors.Is(err, graveler.ErrReadOnlyRepository):
2983+
errors.Is(err, graveler.ErrReadOnlyRepository),
2984+
errors.Is(err, actions.ErrActionsDisabled):
29552985
cb(w, r, http.StatusForbidden, err)
29562986

29572987
case errors.Is(err, authentication.ErrSessionExpired):

pkg/graveler/hooks_handler.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const (
2727
EventTypePostRevert EventType = "post-revert"
2828
EventTypePreCherryPick EventType = "pre-cherry-pick"
2929
EventTypePostCherryPick EventType = "post-cherry-pick"
30+
EventTypeManualTrigger EventType = "manual-trigger"
3031

3132
UnixYear3000 = 32500915200
3233
)
@@ -52,6 +53,8 @@ type HookRecord struct {
5253
TagID TagID
5354
// Exists only in merge actions. Contains the requested source to merge from (branch/tag/ref) as requested in the merge request
5455
MergeSource Ref
56+
// Exists only in manual trigger actions. Contains the action name that was triggered
57+
ActionName string
5558
}
5659

5760
type HooksHandler interface {

pkg/permissions/actions.gen.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/permissions/actions.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ const (
6565
DeleteUserExternalPrincipalAction = "auth:DeleteUserExternalPrincipal"
6666
ReadExternalPrincipalAction = "auth:ReadExternalPrincipal"
6767
ReadActionsAction = "ci:ReadAction"
68+
TriggerActionsAction = "ci:TriggerAction"
6869
PrepareGarbageCollectionCommitsAction = "retention:PrepareGarbageCollectionCommits"
6970
GetGarbageCollectionRulesAction = "retention:GetGarbageCollectionRules"
7071
SetGarbageCollectionRulesAction = "retention:SetGarbageCollectionRules"

0 commit comments

Comments
 (0)