Skip to content

Commit

Permalink
adding platform mode support to repo config (#184)
Browse files Browse the repository at this point in the history
* Add platform mode flag to GlobalCfg

* Added server side config support for platform mode

* Adding flag to enable platform mode

* fix tests

* Moved some of the project level validations to project struct

Added platform mode config support for project level workflow overrides.
We do not support custom platform mode workflows on repo configs

* fix broken test

* moved validations to repo_cfg

* moved some repetitive code to helper functino

* remove platform mode changes

* clean up for loop

* remove platform mode values from project

* adding platform mode support to repo config
  • Loading branch information
msarvar authored Feb 22, 2022
1 parent 78eea78 commit 7e2e205
Show file tree
Hide file tree
Showing 5 changed files with 308 additions and 3 deletions.
4 changes: 4 additions & 0 deletions server/core/config/raw/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ type Project struct {
Dir *string `yaml:"dir,omitempty"`
Workspace *string `yaml:"workspace,omitempty"`
Workflow *string `yaml:"workflow,omitempty"`
PullRequestWorkflowName *string `yaml:"pull_request_workflow,omitempty"`
DeploymentWorkflowName *string `yaml:"deployment_workflow,omitempty"`
TerraformVersion *string `yaml:"terraform_version,omitempty"`
Autoplan *Autoplan `yaml:"autoplan,omitempty"`
ApplyRequirements []string `yaml:"apply_requirements,omitempty"`
Expand Down Expand Up @@ -86,6 +88,8 @@ func (p Project) ToValid() valid.Project {
}

v.WorkflowName = p.Workflow
v.PullRequestWorkflowName = p.PullRequestWorkflowName
v.DeploymentWorkflowName = p.DeploymentWorkflowName
if p.TerraformVersion != nil {
v.TerraformVersion, _ = version.NewVersion(*p.TerraformVersion)
}
Expand Down
17 changes: 17 additions & 0 deletions server/core/config/valid/global_cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,24 @@ func (g GlobalCfg) MergeProjectCfg(log logging.SimpleLogging, repoID string, pro
}
log.Debug("overriding server-defined %s with repo-specified workflow: %q", WorkflowKey, workflow.Name)
}
case PullRequestWorkflowKey:
if proj.PullRequestWorkflowName != nil {
name := *proj.PullRequestWorkflowName
if w, ok := g.PullRequestWorkflows[name]; ok {
pullRequestWorkflow = w
}
}

log.Debug("overriding server-defined %s with repo-specified pull_request_workflow: %q", PullRequestWorkflowKey, workflow.Name)
case DeploymentWorkflowKey:
if proj.DeploymentWorkflowName != nil {
name := *proj.DeploymentWorkflowName
if w, ok := g.DeploymentWorkflows[name]; ok {
deploymentWorkflow = w
}
}

log.Debug("overriding server-defined %s with repo-specified deployment_workflow: %q", DeploymentWorkflowKey, workflow.Name)
case DeleteSourceBranchOnMergeKey:
//We check whether the server configured value and repo-root level
//config is different. If it is then we change to the more granular.
Expand Down
30 changes: 29 additions & 1 deletion server/core/config/valid/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import (
type workflowType string

const (
DefaultWorkflowType workflowType = "workflow"
DefaultWorkflowType workflowType = "workflow"
PullRequestWorkflowType workflowType = "pull_request_workflow"
DeploymentWorkflowType workflowType = "deployment_workflow"
)

type Project struct {
Expand Down Expand Up @@ -44,6 +46,12 @@ func (p Project) ValidateAllowedOverrides(allowedOverrides []string) error {
if p.WorkflowName != nil && !sliceContains(allowedOverrides, WorkflowKey) {
return fmt.Errorf("repo config not allowed to set '%s' key: server-side config needs '%s: [%s]'", WorkflowKey, AllowedOverridesKey, WorkflowKey)
}
if p.PullRequestWorkflowName != nil && !sliceContains(allowedOverrides, PullRequestWorkflowKey) {
return fmt.Errorf("repo config not allowed to set '%s' key: server-side config needs '%s: [%s]'", PullRequestWorkflowKey, AllowedOverridesKey, PullRequestWorkflowKey)
}
if p.DeploymentWorkflowName != nil && !sliceContains(allowedOverrides, DeploymentWorkflowKey) {
return fmt.Errorf("repo config not allowed to set '%s' key: server-side config needs '%s: [%s]'", DeploymentWorkflowKey, AllowedOverridesKey, DeploymentWorkflowKey)
}
if p.ApplyRequirements != nil && !sliceContains(allowedOverrides, ApplyRequirementsKey) {
return fmt.Errorf("repo config not allowed to set '%s' key: server-side config needs '%s: [%s]'", ApplyRequirementsKey, AllowedOverridesKey, ApplyRequirementsKey)
}
Expand All @@ -59,6 +67,10 @@ func (p Project) getWorkflowName(workflowType workflowType) *string {
switch workflowType {
case DefaultWorkflowType:
name = p.WorkflowName
case PullRequestWorkflowType:
name = p.PullRequestWorkflowName
case DeploymentWorkflowType:
name = p.DeploymentWorkflowName
}
return name
}
Expand All @@ -67,10 +79,26 @@ func (p Project) ValidateWorkflow(repoWorkflows map[string]Workflow, globalWorkf
return p.validateWorkflowForType(DefaultWorkflowType, repoWorkflows, globalWorkflows)
}

func (p Project) ValidatePRWorkflow(globalWorkflows map[string]Workflow) error {
return p.validateWorkflowForType(PullRequestWorkflowType, map[string]Workflow{}, globalWorkflows)
}

func (p Project) ValidateDeploymentWorkflow(globalWorkflows map[string]Workflow) error {
return p.validateWorkflowForType(DeploymentWorkflowType, map[string]Workflow{}, globalWorkflows)
}

func (p Project) ValidateWorkflowAllowed(allowedWorkflows []string) error {
return p.validateWorkflowAllowedForType(DefaultWorkflowType, allowedWorkflows)
}

func (p Project) ValidatePRWorkflowAllowed(allowedWorkflows []string) error {
return p.validateWorkflowAllowedForType(PullRequestWorkflowType, allowedWorkflows)
}

func (p Project) ValidateDeploymentWorkflowAllowed(allowedWorkflows []string) error {
return p.validateWorkflowAllowedForType(DeploymentWorkflowType, allowedWorkflows)
}

func (p Project) validateWorkflowForType(
workflowType workflowType,
repoWorkflows map[string]Workflow,
Expand Down
216 changes: 214 additions & 2 deletions server/core/config/valid/project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,115 @@ func TestProject_ValidateWorkflow(t *testing.T) {
}
}

func TestProject_ValidateDeploymentWorkflow(t *testing.T) {
defaultWorklfow := valid.Workflow{
Name: "default",
}
customWorkflow := valid.Workflow{
Name: "custom",
}
undefinedWorkflowName := "undefined"
cases := map[string]struct {
globalWorkflows map[string]valid.Workflow
project valid.Project
expErr string
}{
"failed validation with undefined workflow": {
globalWorkflows: map[string]valid.Workflow{
"default": defaultWorklfow,
},
project: valid.Project{
DeploymentWorkflowName: &undefinedWorkflowName,
},
expErr: "deployment_workflow \"undefined\" is not defined anywhere",
},
"workflow defined in global config": {
globalWorkflows: map[string]valid.Workflow{
"default": defaultWorklfow,
"custom": customWorkflow,
},
project: valid.Project{
DeploymentWorkflowName: &customWorkflow.Name,
},
},
"missing workflow name is valid": {
globalWorkflows: map[string]valid.Workflow{
"default": defaultWorklfow,
"custom": customWorkflow,
},
project: valid.Project{
DeploymentWorkflowName: nil,
},
},
}

for name, c := range cases {
t.Run(name, func(t *testing.T) {
actErr := c.project.ValidateDeploymentWorkflow(c.globalWorkflows)
if c.expErr == "" {
Ok(t, actErr)
} else {
ErrEquals(t, c.expErr, actErr)
}
})
}
}

func TestProject_ValidatePRWorkflow(t *testing.T) {
defaultWorklfow := valid.Workflow{
Name: "default",
}
customWorkflow := valid.Workflow{
Name: "custom",
}
undefinedWorkflowName := "undefined"

cases := map[string]struct {
globalWorkflows map[string]valid.Workflow
project valid.Project
expErr string
}{
"failed validation with undefined workflow": {
globalWorkflows: map[string]valid.Workflow{
"default": defaultWorklfow,
},
project: valid.Project{
PullRequestWorkflowName: &undefinedWorkflowName,
},
expErr: "pull_request_workflow \"undefined\" is not defined anywhere",
},
"workflow defined in global config": {
globalWorkflows: map[string]valid.Workflow{
"default": defaultWorklfow,
"custom": customWorkflow,
},
project: valid.Project{
PullRequestWorkflowName: &customWorkflow.Name,
},
},
"missing workflow name is valid": {
globalWorkflows: map[string]valid.Workflow{
"default": defaultWorklfow,
"custom": customWorkflow,
},
project: valid.Project{
PullRequestWorkflowName: nil,
},
},
}

for name, c := range cases {
t.Run(name, func(t *testing.T) {
actErr := c.project.ValidatePRWorkflow(c.globalWorkflows)
if c.expErr == "" {
Ok(t, actErr)
} else {
ErrEquals(t, c.expErr, actErr)
}
})
}
}

func TestProject_ValidateWorkflowAllowed(t *testing.T) {
undefinedWorkflowName := "undefined"
customWorkflowName := "custom"
Expand Down Expand Up @@ -120,6 +229,91 @@ func TestProject_ValidateWorkflowAllowed(t *testing.T) {
})
}
}

func TestProject_ValidatePRWorkflowAllowed(t *testing.T) {
undefinedWorkflowName := "undefined"
customWorkflowName := "custom"

cases := map[string]struct {
allowedWorkflows []string
project valid.Project
expErr string
}{
"failed validation with undefined workflow": {
allowedWorkflows: []string{"custom"},
project: valid.Project{
PullRequestWorkflowName: &undefinedWorkflowName,
},
expErr: "pull_request_workflow \"undefined\" is not allowed for this repo",
},
"workflow is allowed": {
allowedWorkflows: []string{"custom"},
project: valid.Project{
PullRequestWorkflowName: &customWorkflowName,
},
},
"missing workflow name is valid": {
allowedWorkflows: []string{"custom"},
project: valid.Project{
PullRequestWorkflowName: nil,
},
},
}

for name, c := range cases {
t.Run(name, func(t *testing.T) {
actErr := c.project.ValidatePRWorkflowAllowed(c.allowedWorkflows)
if c.expErr == "" {
Ok(t, actErr)
} else {
ErrEquals(t, c.expErr, actErr)
}
})
}
}

func TestProject_ValidateDeploymentWorkflowAllowed(t *testing.T) {
undefinedWorkflowName := "undefined"
customWorkflowName := "custom"

cases := map[string]struct {
allowedWorkflows []string
project valid.Project
expErr string
}{
"failed validation with undefined workflow": {
allowedWorkflows: []string{"custom"},
project: valid.Project{
DeploymentWorkflowName: &undefinedWorkflowName,
},
expErr: "deployment_workflow \"undefined\" is not allowed for this repo",
},
"workflow is allowed": {
allowedWorkflows: []string{"custom"},
project: valid.Project{
DeploymentWorkflowName: &customWorkflowName,
},
},
"missing workflow name is valid": {
allowedWorkflows: []string{"custom"},
project: valid.Project{
DeploymentWorkflowName: nil,
},
},
}

for name, c := range cases {
t.Run(name, func(t *testing.T) {
actErr := c.project.ValidateDeploymentWorkflowAllowed(c.allowedWorkflows)
if c.expErr == "" {
Ok(t, actErr)
} else {
ErrEquals(t, c.expErr, actErr)
}
})
}
}

func TestProject_ValidateAllowedOverrides(t *testing.T) {
workflowName := "custom"
deleteSourceBranch := true
Expand All @@ -136,6 +330,20 @@ func TestProject_ValidateAllowedOverrides(t *testing.T) {
},
expErr: "repo config not allowed to set 'workflow' key: server-side config needs 'allowed_overrides: [workflow]'",
},
"pull_request_workflow is not allowed override": {
allowedOverrides: []string{},
project: valid.Project{
PullRequestWorkflowName: &workflowName,
},
expErr: "repo config not allowed to set 'pull_request_workflow' key: server-side config needs 'allowed_overrides: [pull_request_workflow]'",
},
"deployment_workflow is not allowed override": {
allowedOverrides: []string{},
project: valid.Project{
DeploymentWorkflowName: &workflowName,
},
expErr: "repo config not allowed to set 'deployment_workflow' key: server-side config needs 'allowed_overrides: [deployment_workflow]'",
},
"apply_requirements is not allowed override": {
allowedOverrides: []string{},
project: valid.Project{
Expand All @@ -151,19 +359,23 @@ func TestProject_ValidateAllowedOverrides(t *testing.T) {
expErr: "repo config not allowed to set 'delete_source_branch_on_merge' key: server-side config needs 'allowed_overrides: [delete_source_branch_on_merge]'",
},
"no errors when allowed override": {
allowedOverrides: []string{"apply_requirements", "workflow", "delete_source_branch_on_merge"},
allowedOverrides: []string{"apply_requirements", "deployment_workflow", "pull_request_workflow", "workflow", "delete_source_branch_on_merge"},
project: valid.Project{
DeleteSourceBranchOnMerge: &deleteSourceBranch,
ApplyRequirements: []string{"mergeable"},
DeploymentWorkflowName: &workflowName,
WorkflowName: &workflowName,
PullRequestWorkflowName: &workflowName,
},
},
"no errors if override attributes nil": {
allowedOverrides: []string{"apply_requirements", "workflow", "delete_source_branch_on_merge"},
allowedOverrides: []string{"apply_requirements", "deployment_workflow", "pull_request_workflow", "workflow", "delete_source_branch_on_merge"},
project: valid.Project{
DeleteSourceBranchOnMerge: nil,
ApplyRequirements: nil,
DeploymentWorkflowName: nil,
WorkflowName: nil,
PullRequestWorkflowName: nil,
},
},
}
Expand Down
Loading

0 comments on commit 7e2e205

Please sign in to comment.