mirror of https://github.com/harness/drone.git
185 lines
5.3 KiB
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
|
|
}
|