drone/app/services/migrate/rule.go

185 lines
5.3 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 migrate
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/harness/gitness/app/services/protection"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/errors"
gitness_store "github.com/harness/gitness/store"
"github.com/harness/gitness/store/database/dbtx"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/check"
"github.com/harness/gitness/types/enum"
migratetypes "github.com/harness/harness-migrate/types"
"github.com/rs/zerolog/log"
)
type Rule struct {
ruleStore store.RuleStore
principalStore store.PrincipalStore
tx dbtx.Transactor
DefDeserializationMap map[migratetypes.RuleType]definitionDeserializer
PatternDeserializationMap map[migratetypes.RuleType]patternDeserializer
}
func NewRule(
ruleStore store.RuleStore,
tx dbtx.Transactor,
principalStore store.PrincipalStore,
) *Rule {
rule := &Rule{
ruleStore: ruleStore,
principalStore: principalStore,
tx: tx,
DefDeserializationMap: make(map[ExternalRuleType]definitionDeserializer),
PatternDeserializationMap: make(map[ExternalRuleType]patternDeserializer),
}
rule.registerDeserializers(principalStore)
return rule
}
func (migrate Rule) Import(
ctx context.Context,
migrator types.Principal,
repo *types.Repository,
typ ExternalRuleType,
extRules []*ExternalRule,
) ([]*types.Rule, error) {
rules := make([]*types.Rule, len(extRules))
for i, extRule := range extRules {
if err := check.Identifier(extRule.Identifier); err != nil {
return nil, fmt.Errorf("branch rule identifier '%s' is invalid: %w", extRule.Identifier, err)
}
def, err := migrate.DefDeserializationMap[typ](ctx, string(extRule.Definition))
if err != nil {
return nil, fmt.Errorf("failed to deserialize rule definition: %w", err)
}
if err = def.Sanitize(); err != nil {
return nil, fmt.Errorf("provided rule definition is invalid: %w", err)
}
definitionJSON, err := json.Marshal(def)
if err != nil {
return nil, fmt.Errorf("failed to marshal rule definition: %w", err)
}
pattern, err := migrate.PatternDeserializationMap[typ](ctx, string(extRule.Pattern))
if err != nil {
return nil, fmt.Errorf("failed to deserialize rule pattern: %w", err)
}
if err = pattern.Validate(); err != nil {
return nil, fmt.Errorf("provided rule pattern is invalid: %w", err)
}
now := time.Now().UnixMilli()
r := &types.Rule{
CreatedBy: migrator.ID,
Created: now,
Updated: now,
RepoID: &repo.ID,
SpaceID: nil,
Type: protection.TypeBranch,
State: enum.RuleStateActive,
Identifier: extRule.Identifier,
Pattern: pattern.JSON(),
Definition: json.RawMessage(definitionJSON),
}
rules[i] = r
}
err := migrate.tx.WithTx(ctx, func(ctx context.Context) error {
for _, rule := range rules {
err := migrate.ruleStore.Create(ctx, rule)
if err != nil {
return fmt.Errorf("failed to create branch rule: %w", err)
}
}
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to store external branch rules: %w", err)
}
return rules, nil
}
func mapToBranchRules(
ctx context.Context,
rule ExternalDefinition,
principalStore store.PrincipalStore,
) (*protection.Branch, error) {
// map users
var userIDs []int64
for _, email := range rule.Bypass.UserEmails {
principal, err := principalStore.FindByEmail(ctx, email)
if err != nil && !errors.Is(err, gitness_store.ErrResourceNotFound) {
return nil, fmt.Errorf("failed to find principal by email for '%s': %w", email, err)
}
if errors.Is(err, gitness_store.ErrResourceNotFound) {
log.Ctx(ctx).Warn().Msgf("skipping principal '%s' on bypass list", email)
continue
}
userIDs = append(userIDs, principal.ID)
}
return &protection.Branch{
Bypass: protection.DefBypass{
UserIDs: userIDs,
RepoOwners: rule.Bypass.RepoOwners,
},
PullReq: protection.DefPullReq{
Approvals: protection.DefApprovals(rule.PullReq.Approvals),
Comments: protection.DefComments(rule.PullReq.Comments),
StatusChecks: protection.DefStatusChecks(rule.PullReq.StatusChecks),
Merge: protection.DefMerge{
StrategiesAllowed: convertMergeMethods(rule.PullReq.Merge.StrategiesAllowed),
DeleteBranch: rule.PullReq.Merge.DeleteBranch,
Block: false,
},
},
Lifecycle: protection.DefLifecycle{
CreateForbidden: rule.Lifecycle.CreateForbidden,
DeleteForbidden: rule.Lifecycle.DeleteForbidden,
UpdateForbidden: rule.Lifecycle.UpdateForbidden,
UpdateForceForbidden: false,
},
}, nil
}
func convertMergeMethods(vals []string) []enum.MergeMethod {
res := make([]enum.MergeMethod, len(vals))
for i := range vals {
res[i] = enum.MergeMethod(vals[i])
}
return res
}