chore: Add Struct required tags and Server Struct Validator (#5371)

Signed-off-by: X-Guardian <32168619+X-Guardian@users.noreply.github.com>
This commit is contained in:
Simon Heather
2025-03-09 11:34:48 +00:00
committed by GitHub
parent 001690e568
commit 4bea4e934a
13 changed files with 122 additions and 105 deletions

View File

@@ -22,21 +22,21 @@ const atlantisTokenHeader = "X-Atlantis-Token"
type APIController struct {
APISecret []byte
Locker locking.Locker
Logger logging.SimpleLogging
Parser events.EventParsing
ProjectCommandBuilder events.ProjectCommandBuilder
ProjectPlanCommandRunner events.ProjectPlanCommandRunner
ProjectApplyCommandRunner events.ProjectApplyCommandRunner
Locker locking.Locker `validate:"required"`
Logger logging.SimpleLogging `validate:"required"`
Parser events.EventParsing `validate:"required"`
ProjectCommandBuilder events.ProjectCommandBuilder `validate:"required"`
ProjectPlanCommandRunner events.ProjectPlanCommandRunner `validate:"required"`
ProjectApplyCommandRunner events.ProjectApplyCommandRunner `validate:"required"`
FailOnPreWorkflowHookError bool
PreWorkflowHooksCommandRunner events.PreWorkflowHooksCommandRunner
PostWorkflowHooksCommandRunner events.PostWorkflowHooksCommandRunner
RepoAllowlistChecker *events.RepoAllowlistChecker
Scope tally.Scope
VCSClient vcs.Client
WorkingDir events.WorkingDir
WorkingDirLocker events.WorkingDirLocker
CommitStatusUpdater events.CommitStatusUpdater
PreWorkflowHooksCommandRunner events.PreWorkflowHooksCommandRunner `validate:"required"`
PostWorkflowHooksCommandRunner events.PostWorkflowHooksCommandRunner `validate:"required"`
RepoAllowlistChecker *events.RepoAllowlistChecker `validate:"required"`
Scope tally.Scope `validate:"required"`
VCSClient vcs.Client `validate:"required"`
WorkingDir events.WorkingDir `validate:"required"`
WorkingDirLocker events.WorkingDirLocker `validate:"required"`
CommitStatusUpdater events.CommitStatusUpdater `validate:"required"`
}
type APIRequest struct {

View File

@@ -58,12 +58,12 @@ const azuredevopsTestURL = "https://fabrikam.visualstudio.com/DefaultCollection/
// VCSEventsController handles all webhook requests which signify 'events' in the
// VCS host, ex. GitHub.
type VCSEventsController struct {
CommandRunner events.CommandRunner
PullCleaner events.PullCleaner
Logger logging.SimpleLogging
Scope tally.Scope
Parser events.EventParsing
CommentParser events.CommentParsing
CommandRunner events.CommandRunner `validate:"required"`
PullCleaner events.PullCleaner `validate:"required"`
Logger logging.SimpleLogging `validate:"required"`
Scope tally.Scope `validate:"required"`
Parser events.EventParsing `validate:"required"`
CommentParser events.CommentParsing `validate:"required"`
ApplyDisabled bool
EmojiReaction string
ExecutableName string
@@ -71,20 +71,20 @@ type VCSEventsController struct {
// UI that identifies this call as coming from GitHub. If empty, no
// request validation is done.
GithubWebhookSecret []byte
GithubRequestValidator GithubRequestValidator
GitlabRequestParserValidator GitlabRequestParserValidator
GithubRequestValidator GithubRequestValidator `validate:"required"`
GitlabRequestParserValidator GitlabRequestParserValidator `validate:"required"`
// GitlabWebhookSecret is the secret added to this webhook via the GitLab
// UI that identifies this call as coming from GitLab. If empty, no
// request validation is done.
GitlabWebhookSecret []byte
RepoAllowlistChecker *events.RepoAllowlistChecker
RepoAllowlistChecker *events.RepoAllowlistChecker `validate:"required"`
// SilenceAllowlistErrors controls whether we write an error comment on
// pull requests from non-allowlisted repos.
SilenceAllowlistErrors bool
// SupportedVCSHosts is which VCS hosts Atlantis was configured upon
// startup to support.
SupportedVCSHosts []models.VCSHostType
VCSClient vcs.Client
SupportedVCSHosts []models.VCSHostType `validate:"required"`
VCSClient vcs.Client `validate:"required"`
TestingMode bool
// BitbucketWebhookSecret is the secret added to this webhook via the Bitbucket
// UI that identifies this call as coming from Bitbucket. If empty, no
@@ -99,7 +99,7 @@ type VCSEventsController struct {
// webhook via the Azure DevOps UI that identifies this call as coming from your
// Azure DevOps Team Project. If empty, no request validation is done.
AzureDevopsWebhookBasicPassword []byte
AzureDevopsRequestValidator AzureDevopsRequestValidator
AzureDevopsRequestValidator AzureDevopsRequestValidator `validate:"required"`
GiteaWebhookSecret []byte
}

View File

@@ -13,10 +13,10 @@ import (
// GithubAppController handles the creation and setup of a new GitHub app
type GithubAppController struct {
AtlantisURL *url.URL
Logger logging.SimpleLogging
AtlantisURL *url.URL `validate:"required"`
Logger logging.SimpleLogging `validate:"required"`
GithubSetupComplete bool
GithubHostname string
GithubHostname string `validate:"required"`
GithubOrg string
}

View File

@@ -26,15 +26,15 @@ func (g JobIDKeyGenerator) Generate(r *http.Request) (string, error) {
}
type JobsController struct {
AtlantisVersion string
AtlantisURL *url.URL
Logger logging.SimpleLogging
ProjectJobsTemplate web_templates.TemplateWriter
ProjectJobsErrorTemplate web_templates.TemplateWriter
Backend locking.Backend
WsMux *websocket.Multiplexor
AtlantisVersion string `validate:"required"`
AtlantisURL *url.URL `validate:"required"`
Logger logging.SimpleLogging `validate:"required"`
ProjectJobsTemplate web_templates.TemplateWriter `validate:"required"`
ProjectJobsErrorTemplate web_templates.TemplateWriter `validate:"required"`
Backend locking.Backend `validate:"required"`
WsMux *websocket.Multiplexor `validate:"required"`
KeyGenerator JobIDKeyGenerator
StatsScope tally.Scope
StatsScope tally.Scope `validate:"required"`
}
func (j *JobsController) getProjectJobs(w http.ResponseWriter, r *http.Request) error {

View File

@@ -17,17 +17,17 @@ import (
// LocksController handles all requests relating to Atlantis locks.
type LocksController struct {
AtlantisVersion string
AtlantisURL *url.URL
Locker locking.Locker
Logger logging.SimpleLogging
ApplyLocker locking.ApplyLocker
VCSClient vcs.Client
LockDetailTemplate web_templates.TemplateWriter
WorkingDir events.WorkingDir
WorkingDirLocker events.WorkingDirLocker
Backend locking.Backend
DeleteLockCommand events.DeleteLockCommand
AtlantisVersion string `validate:"required"`
AtlantisURL *url.URL `validate:"required"`
Locker locking.Locker `validate:"required"`
Logger logging.SimpleLogging `validate:"required"`
ApplyLocker locking.ApplyLocker `validate:"required"`
VCSClient vcs.Client `validate:"required"`
LockDetailTemplate web_templates.TemplateWriter `validate:"required"`
WorkingDir events.WorkingDir `validate:"required"`
WorkingDirLocker events.WorkingDirLocker `validate:"required"`
Backend locking.Backend `validate:"required"`
DeleteLockCommand events.DeleteLockCommand `validate:"required"`
}
// LockApply handles creating a global apply lock.

View File

@@ -11,9 +11,9 @@ import (
// StatusController handles the status of Atlantis.
type StatusController struct {
Logger logging.SimpleLogging
Drainer *events.Drainer
AtlantisVersion string
Logger logging.SimpleLogging `validate:"required"`
Drainer *events.Drainer `validate:"required"`
AtlantisVersion string `validate:"required"`
}
type StatusResponse struct {

View File

@@ -18,11 +18,11 @@ import (
// ApplyStepRunner runs `terraform apply`.
type ApplyStepRunner struct {
TerraformExecutor TerraformExec
DefaultTFDistribution terraform.Distribution
DefaultTFVersion *version.Version
CommitStatusUpdater StatusUpdater
AsyncTFExec AsyncTFExec
TerraformExecutor TerraformExec `validate:"required"`
DefaultTFDistribution terraform.Distribution `validate:"required"`
DefaultTFVersion *version.Version `validate:"required"`
CommitStatusUpdater StatusUpdater `validate:"required"`
AsyncTFExec AsyncTFExec `validate:"required"`
}
func (a *ApplyStepRunner) Run(ctx command.ProjectContext, extraArgs []string, path string, envs map[string]string) (string, error) {

View File

@@ -94,7 +94,7 @@ func buildCommentCommandRunner(
// DefaultCommandRunner is the first step when processing a comment command.
type DefaultCommandRunner struct {
VCSClient vcs.Client
VCSClient vcs.Client `validate:"required"`
GithubPullGetter GithubPullGetter
AzureDevopsPullGetter AzureDevopsPullGetter
GitlabMergeRequestGetter GitlabMergeRequestGetter
@@ -105,9 +105,9 @@ type DefaultCommandRunner struct {
EventParser EventParsing
// User config option: Fail and do not run the Atlantis command request if any of the pre workflow hooks error
FailOnPreWorkflowHookError bool
Logger logging.SimpleLogging
GlobalCfg valid.GlobalCfg
StatsScope tally.Scope
Logger logging.SimpleLogging `validate:"required"`
GlobalCfg valid.GlobalCfg `validate:"required"`
StatsScope tally.Scope `validate:"required"`
// User config option: controls whether to operate on pull requests from forks.
AllowForkPRs bool
// ParallelPoolSize controls the size of the wait group used to run
@@ -123,14 +123,14 @@ type DefaultCommandRunner struct {
// this in our error message back to the user on a forked PR so they know
// how to disable error comment
SilenceForkPRErrorsFlag string
CommentCommandRunnerByCmd map[command.Name]CommentCommandRunner
Drainer *Drainer
PreWorkflowHooksCommandRunner PreWorkflowHooksCommandRunner
PostWorkflowHooksCommandRunner PostWorkflowHooksCommandRunner
PullStatusFetcher PullStatusFetcher
TeamAllowlistChecker command.TeamAllowlistChecker
VarFileAllowlistChecker *VarFileAllowlistChecker
CommitStatusUpdater CommitStatusUpdater
CommentCommandRunnerByCmd map[command.Name]CommentCommandRunner `validate:"required"`
Drainer *Drainer `validate:"required"`
PreWorkflowHooksCommandRunner PreWorkflowHooksCommandRunner `validate:"required"`
PostWorkflowHooksCommandRunner PostWorkflowHooksCommandRunner `validate:"required"`
PullStatusFetcher PullStatusFetcher `validate:"required"`
TeamAllowlistChecker command.TeamAllowlistChecker `validate:"required"`
VarFileAllowlistChecker *VarFileAllowlistChecker `validate:"required"`
CommitStatusUpdater CommitStatusUpdater `validate:"required"`
}
// RunAutoplanCommand runs plan and policy_checks when a pull request is opened or updated.

View File

@@ -7,9 +7,9 @@ import (
// Drainer is used to gracefully shut down atlantis by waiting for in-progress
// operations to complete.
type Drainer struct {
status DrainStatus
mutex sync.Mutex
wg sync.WaitGroup
status DrainStatus `validate:"required"`
mutex sync.Mutex `validate:"required"`
wg sync.WaitGroup `validate:"required"`
}
type DrainStatus struct {

View File

@@ -27,13 +27,13 @@ type PostWorkflowHooksCommandRunner interface {
// DefaultPostWorkflowHooksCommandRunner is the first step when processing a workflow hook commands.
type DefaultPostWorkflowHooksCommandRunner struct {
VCSClient vcs.Client
WorkingDirLocker WorkingDirLocker
WorkingDir WorkingDir
GlobalCfg valid.GlobalCfg
PostWorkflowHookRunner runtime.PostWorkflowHookRunner
CommitStatusUpdater CommitStatusUpdater
Router PostWorkflowHookURLGenerator
VCSClient vcs.Client `validate:"required"`
WorkingDirLocker WorkingDirLocker `validate:"required"`
WorkingDir WorkingDir `validate:"required"`
GlobalCfg valid.GlobalCfg `validate:"required"`
PostWorkflowHookRunner runtime.PostWorkflowHookRunner `validate:"required"`
CommitStatusUpdater CommitStatusUpdater `validate:"required"`
Router PostWorkflowHookURLGenerator `validate:"required"`
}
// RunPostHooks runs post_workflow_hooks after a plan/apply has completed

View File

@@ -27,13 +27,13 @@ type PreWorkflowHooksCommandRunner interface {
// DefaultPreWorkflowHooksCommandRunner is the first step when processing a workflow hook commands.
type DefaultPreWorkflowHooksCommandRunner struct {
VCSClient vcs.Client
WorkingDirLocker WorkingDirLocker
WorkingDir WorkingDir
GlobalCfg valid.GlobalCfg
PreWorkflowHookRunner runtime.PreWorkflowHookRunner
CommitStatusUpdater CommitStatusUpdater
Router PreWorkflowHookURLGenerator
VCSClient vcs.Client `validate:"required"`
WorkingDirLocker WorkingDirLocker `validate:"required"`
WorkingDir WorkingDir `validate:"required"`
GlobalCfg valid.GlobalCfg `validate:"required"`
PreWorkflowHookRunner runtime.PreWorkflowHookRunner `validate:"required"`
CommitStatusUpdater CommitStatusUpdater `validate:"required"`
Router PreWorkflowHookURLGenerator `validate:"required"`
}
// RunPreHooks runs pre_workflow_hooks when PR is opened or updated.

View File

@@ -34,6 +34,7 @@ import (
"syscall"
"time"
"github.com/go-playground/validator/v10"
"github.com/mitchellh/go-homedir"
tally "github.com/uber-go/tally/v4"
prometheus "github.com/uber-go/tally/v4/prometheus"
@@ -203,19 +204,19 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) {
}
}
validator := &cfg.ParserValidator{}
parserValidator := &cfg.ParserValidator{}
globalCfg := valid.NewGlobalCfgFromArgs(
valid.GlobalCfgArgs{
PolicyCheckEnabled: userConfig.EnablePolicyChecksFlag,
})
if userConfig.RepoConfig != "" {
globalCfg, err = validator.ParseGlobalCfg(userConfig.RepoConfig, globalCfg)
globalCfg, err = parserValidator.ParseGlobalCfg(userConfig.RepoConfig, globalCfg)
if err != nil {
return nil, errors.Wrapf(err, "parsing %s file", userConfig.RepoConfig)
}
} else if userConfig.RepoConfigJSON != "" {
globalCfg, err = validator.ParseGlobalCfgJSON(userConfig.RepoConfigJSON, globalCfg)
globalCfg, err = parserValidator.ParseGlobalCfgJSON(userConfig.RepoConfigJSON, globalCfg)
if err != nil {
return nil, errors.Wrapf(err, "parsing --%s", config.RepoConfigJSONFlag)
}
@@ -649,7 +650,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) {
projectCommandBuilder := events.NewInstrumentedProjectCommandBuilder(
logger,
policyChecksEnabled,
validator,
parserValidator,
&events.DefaultProjectFinder{},
vcsClient,
workingDir,
@@ -993,7 +994,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) {
GithubOrg: userConfig.GithubOrg,
}
return &Server{
server := &Server{
AtlantisVersion: config.AtlantisVersion,
AtlantisURL: parsedURL,
Router: underlyingRouter,
@@ -1026,7 +1027,16 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) {
WebUsername: userConfig.WebUsername,
WebPassword: userConfig.WebPassword,
ScheduledExecutorService: scheduledExecutorService,
}, nil
}
validate := validator.New(validator.WithRequiredStructEnabled())
err = validate.Struct(server)
if err != nil {
return nil, err
} else {
return server, nil
}
}
// Start creates the routes and starts serving traffic.

View File

@@ -26,6 +26,7 @@ import (
"github.com/gorilla/mux"
. "github.com/petergtz/pegomock/v4"
"github.com/runatlantis/atlantis/cmd"
"github.com/runatlantis/atlantis/server"
"github.com/runatlantis/atlantis/server/controllers/web_templates"
tMocks "github.com/runatlantis/atlantis/server/controllers/web_templates/mocks"
@@ -36,13 +37,28 @@ import (
. "github.com/runatlantis/atlantis/testing"
)
func TestNewServer(t *testing.T) {
const (
testAtlantisVersion = "1.0.0"
testAtlantisUrl = "http://example.com"
testLockingDBType = cmd.DefaultLockingDBType
testGitHubHostName = cmd.DefaultGHHostname
testGitHubUser = "user"
)
func TestNewServer_GitHubUser(t *testing.T) {
t.Log("Run through NewServer constructor")
tmpDir := t.TempDir()
_, err := server.NewServer(server.UserConfig{
DataDir: tmpDir,
AtlantisURL: "http://example.com",
}, server.Config{})
_, err := server.NewServer(
server.UserConfig{
DataDir: tmpDir,
AtlantisURL: testAtlantisUrl,
LockingDBType: testLockingDBType,
GithubHostname: testGitHubHostName,
GithubUser: testGitHubUser,
}, server.Config{
AtlantisVersion: testAtlantisVersion,
},
)
Ok(t, err)
}
@@ -273,12 +289,3 @@ func TestParseAtlantisURL(t *testing.T) {
})
}
}
func TestCommandRunnerVCSClientInitialized(t *testing.T) {
s, _ := server.NewServer(server.UserConfig{
AtlantisURL: "http://example.com",
},
server.Config{},
)
Assert(t, s.CommandRunner.VCSClient != nil, "VCSClient must not be nil.")
}