mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-08-04 01:38:40 +00:00
269 lines
8.0 KiB
Go
269 lines
8.0 KiB
Go
package raw
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
validation "github.com/go-ozzo/ozzo-validation"
|
|
"github.com/pkg/errors"
|
|
"github.com/runatlantis/atlantis/server/core/config/valid"
|
|
)
|
|
|
|
// GlobalCfg is the raw schema for server-side repo config.
|
|
type GlobalCfg struct {
|
|
Repos []Repo `yaml:"repos" json:"repos"`
|
|
Workflows map[string]Workflow `yaml:"workflows" json:"workflows"`
|
|
PolicySets PolicySets `yaml:"policies" json:"policies"`
|
|
Metrics Metrics `yaml:"metrics" json:"metrics"`
|
|
}
|
|
|
|
// Repo is the raw schema for repos in the server-side repo config.
|
|
type Repo struct {
|
|
ID string `yaml:"id" json:"id"`
|
|
Branch string `yaml:"branch" json:"branch"`
|
|
ApplyRequirements []string `yaml:"apply_requirements" json:"apply_requirements"`
|
|
PreWorkflowHooks []WorkflowHook `yaml:"pre_workflow_hooks" json:"pre_workflow_hooks"`
|
|
Workflow *string `yaml:"workflow,omitempty" json:"workflow,omitempty"`
|
|
PostWorkflowHooks []WorkflowHook `yaml:"post_workflow_hooks" json:"post_workflow_hooks"`
|
|
AllowedWorkflows []string `yaml:"allowed_workflows,omitempty" json:"allowed_workflows,omitempty"`
|
|
AllowedOverrides []string `yaml:"allowed_overrides" json:"allowed_overrides"`
|
|
AllowCustomWorkflows *bool `yaml:"allow_custom_workflows,omitempty" json:"allow_custom_workflows,omitempty"`
|
|
DeleteSourceBranchOnMerge *bool `yaml:"delete_source_branch_on_merge,omitempty" json:"delete_source_branch_on_merge,omitempty"`
|
|
}
|
|
|
|
func (g GlobalCfg) Validate() error {
|
|
err := validation.ValidateStruct(&g,
|
|
validation.Field(&g.Repos),
|
|
validation.Field(&g.Workflows),
|
|
validation.Field(&g.Metrics),
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Check that all workflows referenced by repos are actually defined.
|
|
for _, repo := range g.Repos {
|
|
if repo.Workflow == nil {
|
|
continue
|
|
}
|
|
name := *repo.Workflow
|
|
if name == valid.DefaultWorkflowName {
|
|
// The 'default' workflow will always be defined.
|
|
continue
|
|
}
|
|
found := false
|
|
for w := range g.Workflows {
|
|
if w == name {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
return fmt.Errorf("workflow %q is not defined", name)
|
|
}
|
|
}
|
|
|
|
// Check that all allowed workflows are defined
|
|
for _, repo := range g.Repos {
|
|
if repo.AllowedWorkflows == nil {
|
|
continue
|
|
}
|
|
for _, name := range repo.AllowedWorkflows {
|
|
if name == valid.DefaultWorkflowName {
|
|
// The 'default' workflow will always be defined.
|
|
continue
|
|
}
|
|
found := false
|
|
for w := range g.Workflows {
|
|
if w == name {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
return fmt.Errorf("workflow %q is not defined", name)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (g GlobalCfg) ToValid(defaultCfg valid.GlobalCfg) valid.GlobalCfg {
|
|
workflows := make(map[string]valid.Workflow)
|
|
|
|
// assumes: globalcfg is always initialized with one repo .*
|
|
applyReqs := defaultCfg.Repos[0].ApplyRequirements
|
|
|
|
var globalApplyReqs []string
|
|
|
|
for _, req := range applyReqs {
|
|
for _, nonOverrideableReq := range valid.NonOverrideableApplyReqs {
|
|
if req == nonOverrideableReq {
|
|
globalApplyReqs = append(globalApplyReqs, req)
|
|
}
|
|
}
|
|
}
|
|
|
|
for k, v := range g.Workflows {
|
|
validatedWorkflow := v.ToValid(k)
|
|
workflows[k] = validatedWorkflow
|
|
if k == valid.DefaultWorkflowName {
|
|
// Handle the special case where they're redefining the default
|
|
// workflow. In this case, our default repo config references
|
|
// the "old" default workflow and so needs to be redefined.
|
|
defaultCfg.Repos[0].Workflow = &validatedWorkflow
|
|
}
|
|
}
|
|
// Merge in defaults without overriding.
|
|
for k, v := range defaultCfg.Workflows {
|
|
if _, ok := workflows[k]; !ok {
|
|
workflows[k] = v
|
|
}
|
|
}
|
|
|
|
var repos []valid.Repo
|
|
for _, r := range g.Repos {
|
|
repos = append(repos, r.ToValid(workflows, globalApplyReqs))
|
|
}
|
|
repos = append(defaultCfg.Repos, repos...)
|
|
|
|
return valid.GlobalCfg{
|
|
Repos: repos,
|
|
Workflows: workflows,
|
|
PolicySets: g.PolicySets.ToValid(),
|
|
Metrics: g.Metrics.ToValid(),
|
|
}
|
|
}
|
|
|
|
// HasRegexID returns true if r is configured with a regex id instead of an
|
|
// exact match id.
|
|
func (r Repo) HasRegexID() bool {
|
|
return strings.HasPrefix(r.ID, "/") && strings.HasSuffix(r.ID, "/")
|
|
}
|
|
|
|
// HasRegexBranch returns true if a branch regex was set.
|
|
func (r Repo) HasRegexBranch() bool {
|
|
return strings.HasPrefix(r.Branch, "/") && strings.HasSuffix(r.Branch, "/")
|
|
}
|
|
|
|
func (r Repo) Validate() error {
|
|
idValid := func(value interface{}) error {
|
|
id := value.(string)
|
|
if !r.HasRegexID() {
|
|
return nil
|
|
}
|
|
_, err := regexp.Compile(id[1 : len(id)-1])
|
|
return errors.Wrapf(err, "parsing: %s", id)
|
|
}
|
|
|
|
branchValid := func(value interface{}) error {
|
|
branch := value.(string)
|
|
if !r.HasRegexBranch() {
|
|
return nil
|
|
}
|
|
_, err := regexp.Compile(branch[1 : len(branch)-1])
|
|
return errors.Wrapf(err, "parsing: %s", branch)
|
|
}
|
|
|
|
overridesValid := func(value interface{}) error {
|
|
overrides := value.([]string)
|
|
for _, o := range overrides {
|
|
if o != valid.ApplyRequirementsKey && o != valid.WorkflowKey && o != valid.DeleteSourceBranchOnMergeKey {
|
|
return fmt.Errorf("%q is not a valid override, only %q, %q and %q are supported", o, valid.ApplyRequirementsKey, valid.WorkflowKey, valid.DeleteSourceBranchOnMergeKey)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
workflowExists := func(value interface{}) error {
|
|
// We validate workflows in ParserValidator.validateRepoWorkflows
|
|
// because we need the list of workflows to validate.
|
|
return nil
|
|
}
|
|
|
|
deleteSourceBranchOnMergeValid := func(value interface{}) error {
|
|
//TOBE IMPLEMENTED
|
|
return nil
|
|
}
|
|
|
|
return validation.ValidateStruct(&r,
|
|
validation.Field(&r.ID, validation.Required, validation.By(idValid)),
|
|
validation.Field(&r.Branch, validation.By(branchValid)),
|
|
validation.Field(&r.AllowedOverrides, validation.By(overridesValid)),
|
|
validation.Field(&r.ApplyRequirements, validation.By(validApplyReq)),
|
|
validation.Field(&r.Workflow, validation.By(workflowExists)),
|
|
validation.Field(&r.DeleteSourceBranchOnMerge, validation.By(deleteSourceBranchOnMergeValid)),
|
|
)
|
|
}
|
|
|
|
func (r Repo) ToValid(workflows map[string]valid.Workflow, globalApplyReqs []string) valid.Repo {
|
|
var id string
|
|
var idRegex *regexp.Regexp
|
|
if r.HasRegexID() {
|
|
withoutSlashes := r.ID[1 : len(r.ID)-1]
|
|
// Safe to use MustCompile because we test it in Validate().
|
|
idRegex = regexp.MustCompile(withoutSlashes)
|
|
} else {
|
|
id = r.ID
|
|
}
|
|
|
|
var branchRegex *regexp.Regexp
|
|
if r.HasRegexBranch() {
|
|
withoutSlashes := r.Branch[1 : len(r.Branch)-1]
|
|
// Safe to use MustCompile because we test it in Validate().
|
|
branchRegex = regexp.MustCompile(withoutSlashes)
|
|
}
|
|
|
|
var workflow *valid.Workflow
|
|
if r.Workflow != nil {
|
|
// This key is guaranteed to exist because we test for it in
|
|
// ParserValidator.validateRepoWorkflows.
|
|
ptr := workflows[*r.Workflow]
|
|
workflow = &ptr
|
|
}
|
|
|
|
var preWorkflowHooks []*valid.WorkflowHook
|
|
if len(r.PreWorkflowHooks) > 0 {
|
|
for _, hook := range r.PreWorkflowHooks {
|
|
preWorkflowHooks = append(preWorkflowHooks, hook.ToValid())
|
|
}
|
|
}
|
|
|
|
var postWorkflowHooks []*valid.WorkflowHook
|
|
if len(r.PostWorkflowHooks) > 0 {
|
|
for _, hook := range r.PostWorkflowHooks {
|
|
postWorkflowHooks = append(postWorkflowHooks, hook.ToValid())
|
|
}
|
|
}
|
|
|
|
var mergedApplyReqs []string
|
|
|
|
mergedApplyReqs = append(mergedApplyReqs, r.ApplyRequirements...)
|
|
|
|
// only add global reqs if they don't exist already.
|
|
OUTER:
|
|
for _, globalReq := range globalApplyReqs {
|
|
for _, currReq := range r.ApplyRequirements {
|
|
if globalReq == currReq {
|
|
continue OUTER
|
|
}
|
|
}
|
|
mergedApplyReqs = append(mergedApplyReqs, globalReq)
|
|
}
|
|
|
|
return valid.Repo{
|
|
ID: id,
|
|
IDRegex: idRegex,
|
|
BranchRegex: branchRegex,
|
|
ApplyRequirements: mergedApplyReqs,
|
|
PreWorkflowHooks: preWorkflowHooks,
|
|
Workflow: workflow,
|
|
PostWorkflowHooks: postWorkflowHooks,
|
|
AllowedWorkflows: r.AllowedWorkflows,
|
|
AllowedOverrides: r.AllowedOverrides,
|
|
AllowCustomWorkflows: r.AllowCustomWorkflows,
|
|
DeleteSourceBranchOnMerge: r.DeleteSourceBranchOnMerge,
|
|
}
|
|
}
|