diff --git a/server/apply_executor.go b/server/apply_executor.go index b1d5f5de1..c6a00230e 100644 --- a/server/apply_executor.go +++ b/server/apply_executor.go @@ -31,37 +31,6 @@ type ApplyExecutor struct { workspace *Workspace } -/** Result Types **/ -type ApplyFailure struct { - Command string - Output string - ErrorMessage string -} - -func (a ApplyFailure) Template() *CompiledTemplate { - return ApplyFailureTmpl -} - -type ApplySuccess struct { - Output string -} - -func (a ApplySuccess) Template() *CompiledTemplate { - return ApplySuccessTmpl -} - -type PullNotApprovedFailure struct{} - -func (p PullNotApprovedFailure) Template() *CompiledTemplate { - return PullNotApprovedFailureTmpl -} - -type NoPlansFailure struct{} - -func (n NoPlansFailure) Template() *CompiledTemplate { - return NoPlansFailureTmpl -} - // todo: why pass githbub.client here, just use the one on the struct func (a *ApplyExecutor) execute(ctx *CommandContext, github *github.Client) { a.githubStatus.Update(ctx.BaseRepo, ctx.Pull, Pending, ApplyStep) diff --git a/server/command_handler.go b/server/command_handler.go index 75772e091..f53f4039a 100644 --- a/server/command_handler.go +++ b/server/command_handler.go @@ -17,6 +17,30 @@ type CommandHandler struct { logger *logging.SimpleLogger } +type CommandResponse struct { + Error error + Failure string + ProjectResults []ProjectResult + Command CommandType +} + +type ProjectResult struct { + Path string + Error error + Failure string + PlanSuccess *PlanSuccess + ApplySuccess string +} +func (p ProjectResult) Status() Status { + if p.Error != nil { + return Error + } + if p.Failure != "" { + return Failure + } + return Success +} + type CommandType int const ( diff --git a/server/github_comment_renderer.go b/server/github_comment_renderer.go index e45486b30..e5365d43a 100644 --- a/server/github_comment_renderer.go +++ b/server/github_comment_renderer.go @@ -7,149 +7,45 @@ import ( "text/template" ) +var singlePathTmpl = template.Must(template.New("").Parse("{{ range $result := .Results }}{{$result}}{{end}}\n" + logTmpl)) +var multiPathTmpl = template.Must(template.New("").Parse( + "Ran {{.Command}} in {{ len .Results }} directories:\n" + + "{{ range $path, $result := .Results }}" + + " * `{{$path}}`\n" + + "{{end}}\n" + + "{{ range $path, $result := .Results }}" + + "##{{$path}}/\n" + + "{{$result}}\n" + + "---{{end}}" + + logTmpl)) +var planSuccessTmpl = template.Must(template.New("").Parse( + "```diff\n" + + "{{.TerraformOutput}}\n" + + "```\n\n" + + "* To **discard** this plan click [here]({{.LockURL}}).")) +var applySuccessTmpl = template.Must(template.New("").Parse( + "```diff\n" + + "{{.Output}}\n" + + "```")) +var errTmplText = "**{{.Command}} Error**\n" + + "```\n" + + "{{.Error}}\n" + + "```\n" +var errTmpl = template.Must(template.New("").Parse(errTmplText)) +var errWithLogTmpl = template.Must(template.New("").Parse(errTmplText + logTmpl)) +var failureTmplText = "**{{.Command}} Failed**: {{.Failure}}\n" +var failureTmpl = template.Must(template.New("").Parse(failureTmplText)) +var failureWithLogTmpl = template.Must(template.New("").Parse(failureTmplText + logTmpl)) +var logTmpl = "{{if .Verbose}}\n
Log\n

\n\n```\n{{.Log}}```\n

{{end}}\n" + // GithubCommentRenderer renders responses as GitHub comments type GithubCommentRenderer struct{} -// CompiledTemplate represents a single template with both its source text and its compiled -// template. We compile the templates in init() -type CompiledTemplate struct { - text string - Template *template.Template -} - -// PathResultRendered is used as an intermediary data container. We render the individual path results -// into Render and then pass around this struct to be rendered into the main templates -type PathResultRendered struct { - ProjectResult - Rendered string -} - -// todo: once this is in its own package don't need to append Tmpl -var ( - // If you add a template here, be sure to add it to init() so it gets compiled - SetupFailure *CompiledTemplate = &CompiledTemplate{ - text: "**{{.Command}} Failed**:\n{{.Output}}\n" + logTmpl, - } - SinglePath *CompiledTemplate = &CompiledTemplate{ - // we know we'll only have one result - text: "{{ range $result := .Results }}{{$result}}{{end}}\n" + logTmpl, - } - MultiPath *CompiledTemplate = &CompiledTemplate{ - text: "Ran {{.Command}} in {{ len .Results }} directories:\n" + - "{{ range $path, $result := .Results }}" + - " * `{{$path}}`\n" + - "{{end}}\n" + - "{{ range $path, $result := .Results }}" + - "##{{$path}}/\n" + - "{{$result}}\n" + - "---{{end}}" + - logTmpl, - } - PlanSuccessTmpl *CompiledTemplate = &CompiledTemplate{ - text: "```diff\n" + - "{{.TerraformOutput}}\n" + - "```\n\n" + - "* To **discard** this plan click [here]({{.LockURL}}).", - } - RunLockedFailureTmpl *CompiledTemplate = &CompiledTemplate{ - text: "This plan is currently locked by #{{.LockingPullNum}}\n" + - "The locking plan must be applied or discarded before future plans can execute.", - } - TerraformFailureTmpl *CompiledTemplate = &CompiledTemplate{ - text: "**Atlantis encountered an error while running...**\n" + - "```\n" + - "$ {{.Command}}\n" + - "{{.Output}}\n" + - "```", - } - EnvironmentFileNotFoundFailureTmpl *CompiledTemplate = &CompiledTemplate{ - text: "Environment file did not exist {{.Filename}}", - } - EnvironmentErrorTmpl *CompiledTemplate = &CompiledTemplate{ - text: "Please specify environment variable while running plan\n" + - "For example: `atlantis plan {environment_name}`\n" + - "*Environments that are available can be found under the `env/` folder of the terraform stack.*", - } - ApplySuccessTmpl *CompiledTemplate = &CompiledTemplate{ - text: "```diff\n" + - "{{.Output}}\n" + - "```", - } - ApplyFailureTmpl *CompiledTemplate = &CompiledTemplate{ - text: "**Apply Failed**:\n" + - "```bash\n" + - "$ {{.Command}}\n" + - "{{.Output}}\n" + - "{{.ErrorMessage}}\n" + - "```", - } - PullNotApprovedFailureTmpl *CompiledTemplate = &CompiledTemplate{ - text: "Pull Request must be **Approved** before running apply.", - } - NoPlansFailureTmpl *CompiledTemplate = &CompiledTemplate{ - text: "0 plans found", - } - ErrorTmpl *CompiledTemplate = &CompiledTemplate{ - text: "**Atlantis encountered an error:**\n" + - "```\n" + - "{{.Error}}\n" + - "```\n" + - "Log:\n" + - "```\n" + - "{{.Log}}```", - } - GeneralErrorTmpl *CompiledTemplate = &CompiledTemplate{ - text: "{{.Error}}", - } - ErrTmpl *CompiledTemplate = &CompiledTemplate{ - text: "**{{.Command}} Error**\n" + - "```\n" + - "{{.Error}}\n" + - "```\n", - } - ErrWithLogTmpl *CompiledTemplate = &CompiledTemplate{ - text: ErrTmpl.text + logTmpl, - } - FailureTmpl *CompiledTemplate = &CompiledTemplate{ - text: "**{{.Command}} Failed**: {{.Failure}}\n", - } - FailureWithLogTmpl *CompiledTemplate = &CompiledTemplate{ - text: FailureTmpl.text + logTmpl, - } -) - -var logTmpl = "{{if .Verbose}}\n
Log\n

\n\n```\n{{.Log}}```\n

{{end}}\n" - -func init() { - // compile the templates - for _, t := range []*CompiledTemplate{ - SetupFailure, - SinglePath, - MultiPath, - PlanSuccessTmpl, - RunLockedFailureTmpl, - TerraformFailureTmpl, - EnvironmentFileNotFoundFailureTmpl, - EnvironmentErrorTmpl, - ApplySuccessTmpl, - ApplyFailureTmpl, - PullNotApprovedFailureTmpl, - NoPlansFailureTmpl, - ErrorTmpl, - GeneralErrorTmpl, - ErrTmpl, - ErrWithLogTmpl, - FailureTmpl, - FailureWithLogTmpl, - } { - t.Template = template.Must(template.New("").Parse(t.text)) - } -} func (g *GithubCommentRenderer) render(res CommandResponse, log string, verbose bool) string { commandStr := strings.Title(res.Command.String()) if res.Error != nil { - return g.renderTemplate(ErrWithLogTmpl.Template, struct{ + return g.renderTemplate(errWithLogTmpl, struct{ Command string Error string Verbose bool @@ -157,7 +53,7 @@ func (g *GithubCommentRenderer) render(res CommandResponse, log string, verbose }{commandStr, res.Error.Error(), verbose, log}) } if res.Failure != "" { - return g.renderTemplate(FailureWithLogTmpl.Template, struct{ + return g.renderTemplate(failureWithLogTmpl, struct{ Command string Failure string Verbose bool @@ -165,38 +61,13 @@ func (g *GithubCommentRenderer) render(res CommandResponse, log string, verbose }{commandStr, res.Failure, verbose, log}) } return g.renderProjectResults(res.ProjectResults, commandStr, log, verbose) - - - if res.SetupError != nil { - renderedError := g.renderTemplate(res.SetupError.Template().Template, res.SetupError) - return g.renderTemplate(ErrorTmpl.Template, struct { - Error string - Log string - }{renderedError, log}) - } else if res.SetupFailure != nil { - renderedFailure := g.renderTemplate(res.SetupFailure.Template().Template, res.SetupFailure) - return g.renderTemplate(SetupFailure.Template, struct { - Command string - Output string - Log string - Verbose bool - }{commandStr, renderedFailure, log, verbose}) - } else { - hasErrors := false - for _, res := range res.ProjectResults { - if res.Status == Error { - hasErrors = true - } - } - return g.renderProjectResults(res.ProjectResults, commandStr, log, hasErrors || verbose) - } } func (g *GithubCommentRenderer) renderProjectResults(pathResults []ProjectResult, command string, log string, verbose bool) string { renderedOutputs := make(map[string]string) for _, result := range pathResults { if result.Error != nil { - renderedOutputs[result.Path] = g.renderTemplate(ErrTmpl.Template, struct{ + renderedOutputs[result.Path] = g.renderTemplate(errTmpl, struct{ Command string Output string }{ @@ -204,7 +75,7 @@ func (g *GithubCommentRenderer) renderProjectResults(pathResults []ProjectResult Output: result.Error.Error(), }) } else if result.Failure != "" { - renderedOutputs[result.Path] = g.renderTemplate(FailureTmpl.Template, struct{ + renderedOutputs[result.Path] = g.renderTemplate(failureTmpl, struct{ Command string Failure string }{ @@ -212,9 +83,9 @@ func (g *GithubCommentRenderer) renderProjectResults(pathResults []ProjectResult Failure: result.Failure, }) } else if result.PlanSuccess != nil { - renderedOutputs[result.Path] = g.renderTemplate(PlanSuccessTmpl.Template, *result.PlanSuccess) + renderedOutputs[result.Path] = g.renderTemplate(planSuccessTmpl, *result.PlanSuccess) } else if result.ApplySuccess != "" { - renderedOutputs[result.Path] = g.renderTemplate(ApplySuccessTmpl.Template, struct{Output string}{result.ApplySuccess}) + renderedOutputs[result.Path] = g.renderTemplate(applySuccessTmpl, struct{Output string}{result.ApplySuccess}) } else { renderedOutputs[result.Path] = "Found no template. This is a bug!" } @@ -222,9 +93,9 @@ func (g *GithubCommentRenderer) renderProjectResults(pathResults []ProjectResult var tmpl *template.Template if len(renderedOutputs) == 1 { - tmpl = SinglePath.Template + tmpl = singlePathTmpl } else { - tmpl = MultiPath.Template + tmpl = multiPathTmpl } return g.renderTemplate(tmpl, struct { Results map[string]string diff --git a/server/github_status.go b/server/github_status.go index 163a092c3..6d5367fef 100644 --- a/server/github_status.go +++ b/server/github_status.go @@ -47,7 +47,7 @@ func (g *GithubStatus) Update(repo models.Repo, pull models.PullRequest, status func (g *GithubStatus) UpdatePathResult(ctx *CommandContext, pathResults []ProjectResult) error { var statuses []Status for _, p := range pathResults { - statuses = append(statuses, p.Status) + statuses = append(statuses, p.Status()) } worst := g.worstStatus(statuses) return g.Update(ctx.BaseRepo, ctx.Pull, worst, ctx.Command.commandType.String()) diff --git a/server/plan_executor.go b/server/plan_executor.go index 76752a55d..ffc846cb0 100644 --- a/server/plan_executor.go +++ b/server/plan_executor.go @@ -40,41 +40,6 @@ type PlanSuccess struct { LockURL string } -func (p PlanSuccess) Template() *CompiledTemplate { - return PlanSuccessTmpl -} - -type RunLockedFailure struct { - LockingPullNum int -} - -func (r RunLockedFailure) Template() *CompiledTemplate { - return RunLockedFailureTmpl -} - -type EnvironmentFileNotFoundFailure struct { - Filename string -} - -func (e EnvironmentFileNotFoundFailure) Template() *CompiledTemplate { - return EnvironmentFileNotFoundFailureTmpl -} - -type TerraformFailure struct { - Command string - Output string -} - -func (t TerraformFailure) Template() *CompiledTemplate { - return TerraformFailureTmpl -} - -type EnvironmentFailure struct{} - -func (e EnvironmentFailure) Template() *CompiledTemplate { - return EnvironmentErrorTmpl -} - func (p *PlanExecutor) execute(ctx *CommandContext, github *github.Client) { p.githubStatus.Update(ctx.BaseRepo, ctx.Pull, Pending, PlanStep) res := p.setupAndPlan(ctx) diff --git a/server/server.go b/server/server.go index e80b1981a..528ce24b0 100644 --- a/server/server.go +++ b/server/server.go @@ -72,40 +72,6 @@ type CommandContext struct { Log *logging.SimpleLogger } -// todo: These structs have nothing to do with the server. Move to a different file/package #refactor -type CommandResponse struct { - Error error - Failure string - SetupError Templater - SetupFailure Templater - ProjectResults []ProjectResult - Command CommandType -} - -type ProjectResult struct { - Path string - Status Status - Result Templater - Error error - Failure string - PlanSuccess *PlanSuccess - ApplySuccess string -} - -type Templater interface { - Template() *CompiledTemplate -} - -type GeneralError struct { - Error error -} - -func (g GeneralError) Template() *CompiledTemplate { - return GeneralErrorTmpl -} - -// todo: /end - func NewServer(config ServerConfig) (*Server, error) { // if ~ was used in data-dir convert that to actual home directory otherwise we'll // create a directory call "~" instead of actually using home