-
Notifications
You must be signed in to change notification settings - Fork 342
/
protection_manager.go
123 lines (111 loc) · 4.4 KB
/
protection_manager.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package branch
import (
"context"
"errors"
"fmt"
"time"
"github.com/gobwas/glob"
"github.com/gobwas/glob/syntax"
"github.com/treeverse/lakefs/pkg/cache"
"github.com/treeverse/lakefs/pkg/graveler"
"github.com/treeverse/lakefs/pkg/graveler/settings"
"google.golang.org/protobuf/proto"
)
const ProtectionSettingKey = "protected_branches"
const (
matcherCacheSize = 100_000
matcherCacheExpiry = 1 * time.Hour
matcherCacheJitter = 1 * time.Minute
)
var (
ErrRuleAlreadyExists = errors.New("branch protection rule already exists")
ErrRuleNotExists = errors.New("branch protection rule does not exist")
)
type ProtectionManager struct {
settingManager *settings.KVManager
matchers cache.Cache
}
func NewProtectionManager(settingManager *settings.KVManager) *ProtectionManager {
return &ProtectionManager{settingManager: settingManager, matchers: cache.NewCache(matcherCacheSize, matcherCacheExpiry, cache.NewJitterFn(matcherCacheJitter))}
}
func (m *ProtectionManager) Add(ctx context.Context, repository *graveler.RepositoryRecord, branchNamePattern string, blockedActions []graveler.BranchProtectionBlockedAction) error {
_, err := syntax.Parse(branchNamePattern)
if err != nil {
return fmt.Errorf("invalid branch pattern syntax: %w", err)
}
return m.settingManager.Update(ctx, repository, ProtectionSettingKey, &graveler.BranchProtectionRules{}, func(message proto.Message) (proto.Message, error) {
rules := message.(*graveler.BranchProtectionRules)
if rules.BranchPatternToBlockedActions == nil {
rules.BranchPatternToBlockedActions = make(map[string]*graveler.BranchProtectionBlockedActions)
}
if _, ok := rules.BranchPatternToBlockedActions[branchNamePattern]; ok {
return nil, ErrRuleAlreadyExists
}
rules.BranchPatternToBlockedActions[branchNamePattern] = &graveler.BranchProtectionBlockedActions{Value: blockedActions}
return rules, nil
})
}
func (m *ProtectionManager) Delete(ctx context.Context, repository *graveler.RepositoryRecord, branchNamePattern string) error {
return m.settingManager.Update(ctx, repository, ProtectionSettingKey, &graveler.BranchProtectionRules{}, func(message proto.Message) (proto.Message, error) {
rules := message.(*graveler.BranchProtectionRules)
if rules.BranchPatternToBlockedActions == nil {
rules.BranchPatternToBlockedActions = make(map[string]*graveler.BranchProtectionBlockedActions)
}
if _, ok := rules.BranchPatternToBlockedActions[branchNamePattern]; !ok {
return nil, ErrRuleNotExists
}
delete(rules.BranchPatternToBlockedActions, branchNamePattern)
return rules, nil
})
}
func (m *ProtectionManager) Get(ctx context.Context, repository *graveler.RepositoryRecord, branchNamePattern string) ([]graveler.BranchProtectionBlockedAction, error) {
rules, err := m.settingManager.GetLatest(ctx, repository, ProtectionSettingKey, &graveler.BranchProtectionRules{})
if errors.Is(err, graveler.ErrNotFound) {
return nil, nil
}
if err != nil {
return nil, err
}
actions := rules.(*graveler.BranchProtectionRules).BranchPatternToBlockedActions[branchNamePattern]
if actions == nil {
return nil, nil
}
return actions.GetValue(), nil
}
func (m *ProtectionManager) GetRules(ctx context.Context, repository *graveler.RepositoryRecord) (*graveler.BranchProtectionRules, error) {
rules, err := m.settingManager.GetLatest(ctx, repository, ProtectionSettingKey, &graveler.BranchProtectionRules{})
if errors.Is(err, graveler.ErrNotFound) {
return &graveler.BranchProtectionRules{}, nil
}
if err != nil {
return nil, err
}
return rules.(*graveler.BranchProtectionRules), nil
}
func (m *ProtectionManager) IsBlocked(ctx context.Context, repository *graveler.RepositoryRecord, branchID graveler.BranchID, action graveler.BranchProtectionBlockedAction) (bool, error) {
rules, err := m.settingManager.Get(ctx, repository, ProtectionSettingKey, &graveler.BranchProtectionRules{})
if errors.Is(err, graveler.ErrNotFound) {
return false, nil
}
if err != nil {
return false, err
}
for pattern, blockedActions := range rules.(*graveler.BranchProtectionRules).BranchPatternToBlockedActions {
pattern := pattern
matcher, err := m.matchers.GetOrSet(pattern, func() (v interface{}, err error) {
return glob.Compile(pattern)
})
if err != nil {
return false, err
}
if !matcher.(glob.Glob).Match(string(branchID)) {
continue
}
for _, c := range blockedActions.GetValue() {
if c == action {
return true, nil
}
}
}
return false, nil
}