mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-07-28 22:18:30 +00:00
* Initial build out of redis backend * Regenerate locking mocks and matchers * More test cleanup * More test fixes * Added tests for redis * Test fix * Linting fix * Dcos update * Update redis.go * Update server.go * Adding nolint to RedisDB struct
167 lines
5.3 KiB
Go
167 lines
5.3 KiB
Go
// Copyright 2017 HootSuite Media 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.
|
|
// Modified hereafter by contributors to runatlantis/atlantis.
|
|
|
|
package events
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"sort"
|
|
"strings"
|
|
"text/template"
|
|
|
|
"github.com/runatlantis/atlantis/server/logging"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/runatlantis/atlantis/server/core/locking"
|
|
"github.com/runatlantis/atlantis/server/events/models"
|
|
"github.com/runatlantis/atlantis/server/events/vcs"
|
|
"github.com/runatlantis/atlantis/server/jobs"
|
|
)
|
|
|
|
//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_resource_cleaner.go ResourceCleaner
|
|
|
|
type ResourceCleaner interface {
|
|
CleanUp(pullInfo jobs.PullInfo)
|
|
}
|
|
|
|
//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_pull_cleaner.go PullCleaner
|
|
|
|
// PullCleaner cleans up pull requests after they're closed/merged.
|
|
type PullCleaner interface {
|
|
// CleanUpPull deletes the workspaces used by the pull request on disk
|
|
// and deletes any locks associated with this pull request for all workspaces.
|
|
CleanUpPull(repo models.Repo, pull models.PullRequest) error
|
|
}
|
|
|
|
// PullClosedExecutor executes the tasks required to clean up a closed pull
|
|
// request.
|
|
type PullClosedExecutor struct {
|
|
Locker locking.Locker
|
|
VCSClient vcs.Client
|
|
WorkingDir WorkingDir
|
|
Logger logging.SimpleLogging
|
|
Backend locking.Backend
|
|
PullClosedTemplate PullCleanupTemplate
|
|
LogStreamResourceCleaner ResourceCleaner
|
|
}
|
|
|
|
type templatedProject struct {
|
|
RepoRelDir string
|
|
Workspaces string
|
|
}
|
|
|
|
var pullClosedTemplate = template.Must(template.New("").Parse(
|
|
"Locks and plans deleted for the projects and workspaces modified in this pull request:\n" +
|
|
"{{ range . }}\n" +
|
|
"- dir: `{{ .RepoRelDir }}` {{ .Workspaces }}{{ end }}"))
|
|
|
|
type PullCleanupTemplate interface {
|
|
Execute(wr io.Writer, data interface{}) error
|
|
}
|
|
|
|
type PullClosedEventTemplate struct{}
|
|
|
|
func (t *PullClosedEventTemplate) Execute(wr io.Writer, data interface{}) error {
|
|
return pullClosedTemplate.Execute(wr, data)
|
|
}
|
|
|
|
// CleanUpPull cleans up after a closed pull request.
|
|
func (p *PullClosedExecutor) CleanUpPull(repo models.Repo, pull models.PullRequest) error {
|
|
pullStatus, err := p.Backend.GetPullStatus(pull)
|
|
if err != nil {
|
|
// Log and continue to clean up other resources.
|
|
p.Logger.Err("retrieving pull status: %s", err)
|
|
}
|
|
|
|
if pullStatus != nil {
|
|
for _, project := range pullStatus.Projects {
|
|
jobContext := jobs.PullInfo{
|
|
PullNum: pull.Num,
|
|
Repo: pull.BaseRepo.Name,
|
|
Workspace: project.Workspace,
|
|
ProjectName: project.ProjectName,
|
|
}
|
|
p.LogStreamResourceCleaner.CleanUp(jobContext)
|
|
}
|
|
}
|
|
|
|
if err := p.WorkingDir.Delete(repo, pull); err != nil {
|
|
return errors.Wrap(err, "cleaning workspace")
|
|
}
|
|
|
|
// Finally, delete locks. We do this last because when someone
|
|
// unlocks a project, right now we don't actually delete the plan
|
|
// so we might have plans laying around but no locks.
|
|
locks, err := p.Locker.UnlockByPull(repo.FullName, pull.Num)
|
|
if err != nil {
|
|
return errors.Wrap(err, "cleaning up locks")
|
|
}
|
|
|
|
// Delete pull from DB.
|
|
if err := p.Backend.DeletePullStatus(pull); err != nil {
|
|
p.Logger.Err("deleting pull from db: %s", err)
|
|
}
|
|
|
|
// If there are no locks then there's no need to comment.
|
|
if len(locks) == 0 {
|
|
return nil
|
|
}
|
|
|
|
templateData := p.buildTemplateData(locks)
|
|
var buf bytes.Buffer
|
|
if err = pullClosedTemplate.Execute(&buf, templateData); err != nil {
|
|
return errors.Wrap(err, "rendering template for comment")
|
|
}
|
|
return p.VCSClient.CreateComment(repo, pull.Num, buf.String(), "")
|
|
}
|
|
|
|
// buildTemplateData formats the lock data into a slice that can easily be
|
|
// templated for the VCS comment. We organize all the workspaces by their
|
|
// respective project paths so the comment can look like:
|
|
// dir: {dir}, workspaces: {all-workspaces}
|
|
func (p *PullClosedExecutor) buildTemplateData(locks []models.ProjectLock) []templatedProject {
|
|
workspacesByPath := make(map[string][]string)
|
|
for _, l := range locks {
|
|
path := l.Project.Path
|
|
workspacesByPath[path] = append(workspacesByPath[path], l.Workspace)
|
|
}
|
|
|
|
// sort keys so we can write deterministic tests
|
|
var sortedPaths []string
|
|
for p := range workspacesByPath {
|
|
sortedPaths = append(sortedPaths, p)
|
|
}
|
|
sort.Strings(sortedPaths)
|
|
|
|
var projects []templatedProject
|
|
for _, p := range sortedPaths {
|
|
workspace := workspacesByPath[p]
|
|
workspacesStr := fmt.Sprintf("`%s`", strings.Join(workspace, "`, `"))
|
|
if len(workspace) == 1 {
|
|
projects = append(projects, templatedProject{
|
|
RepoRelDir: p,
|
|
Workspaces: "workspace: " + workspacesStr,
|
|
})
|
|
} else {
|
|
projects = append(projects, templatedProject{
|
|
RepoRelDir: p,
|
|
Workspaces: "workspaces: " + workspacesStr,
|
|
})
|
|
|
|
}
|
|
}
|
|
return projects
|
|
}
|