Skip to content

Commit 4c3290f

Browse files
committed
initial changes
1 parent 3de76d6 commit 4c3290f

File tree

7 files changed

+184
-1
lines changed

7 files changed

+184
-1
lines changed

api/swagger.yml

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

5600+
/repositories/{repository}/refs/{ref}/actions/{action}/trigger:
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+
200:
5624+
description: action run result
5625+
content:
5626+
application/json:
5627+
schema:
5628+
$ref: "#/components/schemas/ActionRun"
5629+
400:
5630+
$ref: "#/components/responses/BadRequest"
5631+
401:
5632+
$ref: "#/components/responses/Unauthorized"
5633+
404:
5634+
$ref: "#/components/responses/NotFound"
5635+
420:
5636+
description: too many requests
5637+
default:
5638+
$ref: "#/components/responses/ServerError"
5639+
56005640
/repositories/{repository}/metadata/object/{type}/{object_id}:
56015641
parameters:
56025642
- 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/service.go

Lines changed: 92 additions & 0 deletions
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) (*RunResult, error)
213215
graveler.HooksHandler
214216
}
215217

@@ -606,6 +608,96 @@ func (s *StoreService) PostCherryPickHook(ctx context.Context, record graveler.H
606608
return nil
607609
}
608610

611+
func (s *StoreService) TriggerAction(ctx context.Context, repository *catalog.Repository, ref string, actionName string) (*RunResult, error) {
612+
if !s.cfg.Enabled {
613+
logging.FromContext(ctx).WithField("repository", repository.Name).Debug("Hooks are disabled, skipping manual trigger")
614+
return nil, fmt.Errorf("actions are disabled")
615+
}
616+
617+
// Construct graveler.RepositoryRecord from catalog.Repository
618+
repoRecord := &graveler.RepositoryRecord{
619+
RepositoryID: graveler.RepositoryID(repository.Name),
620+
Repository: &graveler.Repository{
621+
StorageID: graveler.StorageID(repository.StorageID),
622+
StorageNamespace: graveler.StorageNamespace(repository.StorageNamespace),
623+
CreationDate: repository.CreationDate,
624+
DefaultBranchID: graveler.BranchID(repository.DefaultBranch),
625+
State: graveler.RepositoryState_ACTIVE,
626+
InstanceUID: "", // Not available from catalog API
627+
ReadOnly: repository.ReadOnly,
628+
},
629+
}
630+
631+
runID := s.NewRunID()
632+
633+
// Create hook record for manual trigger with all required fields
634+
record := graveler.HookRecord{
635+
RunID: runID,
636+
EventType: graveler.EventTypeManualTrigger,
637+
Repository: repoRecord,
638+
SourceRef: graveler.Ref(ref),
639+
BranchID: graveler.BranchID(ref), // For manual triggers, we use the ref as branch
640+
CommitID: graveler.CommitID(""), // Empty for manual triggers
641+
TagID: graveler.TagID(""), // Empty for manual triggers
642+
MergeSource: graveler.Ref(""), // Empty for manual triggers
643+
Commit: graveler.Commit{
644+
Message: "Manual trigger",
645+
MetaRangeID: graveler.MetaRangeID(""),
646+
CreationDate: time.Now(),
647+
Version: graveler.CommitVersion(1),
648+
Metadata: map[string]string{},
649+
Parents: []graveler.CommitID{},
650+
},
651+
}
652+
653+
// Load actions from the repository
654+
actions, err := LoadActions(ctx, s.Source, record)
655+
if err != nil {
656+
return nil, fmt.Errorf("failed to load actions: %w", err)
657+
}
658+
659+
// Filter actions for manual trigger with specific action name
660+
var matchedActions []*Action
661+
for _, action := range actions {
662+
// Check if action has manual-trigger event and matches the specified action name
663+
if actionOn, ok := action.On[graveler.EventTypeManualTrigger]; ok && action.Name == actionName {
664+
// For manual triggers, we don't need to check branches since they're triggered explicitly
665+
_ = actionOn // actionOn might be nil, which is fine for manual triggers
666+
matchedActions = append(matchedActions, action)
667+
}
668+
}
669+
670+
if len(matchedActions) == 0 {
671+
return nil, fmt.Errorf("no action named '%s' with manual-trigger found", actionName)
672+
}
673+
674+
// Allocate and run tasks
675+
tasks, err := s.allocateTasks(runID, matchedActions)
676+
if err != nil {
677+
return nil, fmt.Errorf("failed to allocate tasks: %w", err)
678+
}
679+
680+
runErr := s.runTasks(ctx, record, tasks)
681+
682+
// Save run information
683+
err = s.saveRunInformation(ctx, record, tasks)
684+
if err != nil {
685+
return nil, fmt.Errorf("failed to save run information: %w", err)
686+
}
687+
688+
// Return the run result
689+
if runErr != nil {
690+
// Return the run result even if there was an error, so caller can see what failed
691+
runResult, getErr := s.GetRunResult(ctx, string(repoRecord.RepositoryID), runID)
692+
if getErr != nil {
693+
return nil, fmt.Errorf("action failed and could not retrieve run result: %w", runErr)
694+
}
695+
return runResult, runErr
696+
}
697+
698+
return s.GetRunResult(ctx, string(repoRecord.RepositoryID), runID)
699+
}
700+
609701
func (s *StoreService) NewRunID() string {
610702
return s.idGen.NewRunID()
611703
}

pkg/api/controller.go

Lines changed: 45 additions & 0 deletions
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) (*actions.RunResult, error)
8990
}
9091

9192
type Migrator interface {
@@ -2703,6 +2704,50 @@ 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+
runResult, err := c.Actions.TriggerAction(ctx, repo, refParam, action)
2727+
if c.handleAPIError(ctx, w, r, err) {
2728+
return
2729+
}
2730+
2731+
// Convert to API response format
2732+
var status string
2733+
if runResult.Passed {
2734+
status = actionStatusCompleted
2735+
} else {
2736+
status = actionStatusFailed
2737+
}
2738+
2739+
response := apigen.ActionRun{
2740+
RunId: runResult.RunID,
2741+
EventType: runResult.EventType,
2742+
StartTime: runResult.StartTime,
2743+
EndTime: &runResult.EndTime,
2744+
Status: status,
2745+
Branch: runResult.BranchID,
2746+
CommitId: runResult.CommitID,
2747+
}
2748+
writeResponse(w, r, http.StatusOK, response)
2749+
}
2750+
27062751
func (c *Controller) ListRunHooks(w http.ResponseWriter, r *http.Request, repository, runID string, params apigen.ListRunHooksParams) {
27072752
if !c.authorize(w, r, permissions.Node{
27082753
Permission: permissions.Permission{

pkg/graveler/hooks_handler.go

Lines changed: 1 addition & 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
)

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)