Skip to content

Commit c40c5b8

Browse files
author
Aaron Schlesinger
committed
ref(pkg/sshd,pkg/cleaner,pkg/builder.go,boot.go): surfacing the repository push lock, adding a repo delete lock
The deleted app cleaner uses the repo delete lock, and this commit surfaces the concurrent push lock for symmetry with the delete lock
1 parent fdf2e2c commit c40c5b8

4 files changed

Lines changed: 45 additions & 18 deletions

File tree

boot.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ func main() {
4848
pkglog.Err("getting config for %s [%s]", serverConfAppName, err)
4949
os.Exit(1)
5050
}
51+
pushLock := sshd.NewInMemoryRepositoryLock()
52+
deleteLock := sshd.NewInMemoryRepositoryLock()
5153
circ := sshd.NewCircuit()
5254

5355
s3Client, err := storage.GetClient(cnf.HealthSrvTestStorageRegion)
@@ -70,15 +72,15 @@ func main() {
7072
log.Printf("Starting deleted app cleaner")
7173
cleanerErrCh := make(chan error)
7274
go func() {
73-
if err := cleaner.Run(gitHomeDir, kubeClient.Namespaces(), cnf.CleanerPollSleepDuration); err != nil {
75+
if err := cleaner.Run(gitHomeDir, kubeClient.Namespaces(), deleteLock, cnf.CleanerPollSleepDuration); err != nil {
7476
cleanerErrCh <- err
7577
}
7678
}()
7779

7880
log.Printf("Starting SSH server on %s:%d", cnf.SSHHostIP, cnf.SSHHostPort)
7981
sshCh := make(chan int)
8082
go func() {
81-
sshCh <- pkg.RunBuilder(cnf.SSHHostIP, cnf.SSHHostPort, gitHomeDir, circ)
83+
sshCh <- pkg.RunBuilder(cnf.SSHHostIP, cnf.SSHHostPort, gitHomeDir, circ, pushLock, deleteLock)
8284
}()
8385

8486
select {

pkg/builder.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const (
2727
// Git.
2828
//
2929
// Run returns on of the Status* status code constants.
30-
func RunBuilder(sshHostIP string, sshHostPort int, gitHomeDir string, sshServerCircuit *sshd.Circuit) int {
30+
func RunBuilder(sshHostIP string, sshHostPort int, gitHomeDir string, sshServerCircuit *sshd.Circuit, pushLock sshd.RepositoryLock, deleteLock sshd.RepositoryLock) int {
3131
reg, router, ocxt := cookoo.Cookoo()
3232
log.SetFlags(0) // Time is captured elsewhere.
3333

@@ -58,7 +58,7 @@ func RunBuilder(sshHostIP string, sshHostPort int, gitHomeDir string, sshServerC
5858
// Start the SSH service.
5959
// TODO: We could refactor Serve to be a command, and then run this as
6060
// a route.
61-
if err := sshd.Serve(reg, router, sshServerCircuit, gitHomeDir, cxt); err != nil {
61+
if err := sshd.Serve(reg, router, sshServerCircuit, gitHomeDir, pushLock, deleteLock, cxt); err != nil {
6262
clog.Errf(cxt, "SSH server failed: %s", err)
6363
return StatusLocalError
6464
}

pkg/cleaner/cleaner.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"time"
99

1010
"github.com/deis/builder/pkg/k8s"
11+
"github.com/deis/builder/pkg/sshd"
1112
"github.com/deis/pkg/log"
1213
"k8s.io/kubernetes/pkg/api"
1314
"k8s.io/kubernetes/pkg/fields"
@@ -70,8 +71,7 @@ func getDisjunction(namespaceList []api.Namespace, dirs []string) []string {
7071
}
7172

7273
// Run starts the deleted app cleaner. Every pollSleepDuration, it compares the result of nsLister.List with the directories in the top level of gitHome on the local file system. On any error, it uses log.Debug to output a human readable description of what happened.
73-
// TODO: locking mechanism on repositories. Nobody should be able to push to a repo while one is being deleted
74-
func Run(gitHome string, nsLister k8s.NamespaceLister, pollSleepDuration time.Duration) error {
74+
func Run(gitHome string, nsLister k8s.NamespaceLister, repoLock sshd.RepositoryLock, pollSleepDuration time.Duration) error {
7575
for {
7676
nsList, err := nsLister.List(labels.Everything(), fields.Everything())
7777
if err != nil {
@@ -86,9 +86,17 @@ func Run(gitHome string, nsLister k8s.NamespaceLister, pollSleepDuration time.Du
8686

8787
disjunctions := getDisjunction(nsList.Items, gitDirs)
8888
for _, disj := range disjunctions {
89+
if err := repoLock.Lock(disj, time.Duration(0)); err != nil {
90+
log.Debug("Cleaner error locking repository %s for deletion (%s)", disj, err)
91+
continue
92+
}
8993
if err := os.RemoveAll(disj); err != nil {
9094
log.Debug("Cleaner error removing deleted app %s (%s)", disj, err)
9195
}
96+
if err := repoLock.Unlock(disj, time.Duration(0)); err != nil {
97+
log.Debug("Cleaner error unlocking repository %s for deletion (%s)", disj, err)
98+
continue
99+
}
92100
}
93101

94102
time.Sleep(pollSleepDuration)

pkg/sshd/server.go

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import (
1212
"io"
1313
"net"
1414
"strings"
15-
"sync"
16-
"text/template"
1715
"time"
1816

1917
"github.com/Masterminds/cookoo"
@@ -31,10 +29,8 @@ const (
3129
ServerConfig string = "ssh.ServerConfig"
3230

3331
multiplePush string = "Another git push is ongoing"
34-
)
3532

36-
var (
37-
buildingRepos = NewInMemoryRepositoryLock()
33+
inProgressDelete string = "This app was deleted and is being cleaned up. Please re-create it with 'deis create your_app'"
3834
)
3935

4036
// Serve starts a native SSH server.
@@ -54,7 +50,7 @@ var (
5450
//
5551
// This puts the following variables into the context:
5652
// - ssh.Closer (chan interface{}): Send a message to this to shutdown the server.
57-
func Serve(reg *cookoo.Registry, router *cookoo.Router, serverCircuit *Circuit, gitHomeDir string, c cookoo.Context) cookoo.Interrupt {
53+
func Serve(reg *cookoo.Registry, router *cookoo.Router, serverCircuit *Circuit, gitHomeDir string, concurrentPushLock RepositoryLock, concurrentDeleteLock RepositoryLock, c cookoo.Context) cookoo.Interrupt {
5854
hostkeys := c.Get(HostKeys, []ssh.Signer{}).([]ssh.Signer)
5955
addr := c.Get(Address, "0.0.0.0:2223").(string)
6056
cfg := c.Get(ServerConfig, &ssh.ServerConfig{}).(*ssh.ServerConfig)
@@ -70,8 +66,10 @@ func Serve(reg *cookoo.Registry, router *cookoo.Router, serverCircuit *Circuit,
7066
}
7167

7268
srv := &server{
73-
c: c,
74-
gitHome: gitHomeDir,
69+
c: c,
70+
gitHome: gitHomeDir,
71+
pushLock: concurrentPushLock,
72+
deleteLock: concurrentDeleteLock,
7573
}
7674

7775
closer := make(chan interface{}, 1)
@@ -88,8 +86,8 @@ func Serve(reg *cookoo.Registry, router *cookoo.Router, serverCircuit *Circuit,
8886
type server struct {
8987
c cookoo.Context
9088
gitHome string
91-
hookTpl *template.Template
92-
createLock sync.Mutex
89+
pushLock RepositoryLock
90+
deleteLock RepositoryLock
9391
}
9492

9593
// listen handles accepting and managing connections. However, since closer
@@ -225,7 +223,17 @@ func (s *server) answer(channel ssh.Channel, requests <-chan *ssh.Request, sshCo
225223
}
226224

227225
repoName := parts[1]
228-
if err := buildingRepos.Lock(repoName, time.Duration(0)); err != nil {
226+
if err := s.deleteLock.Lock(repoName, time.Duration(0)); err != nil {
227+
log.Errf(s.c, inProgressDelete)
228+
// The error must be in git format
229+
if err := gitPktLine(channel, fmt.Sprintf("ERR %v\n", inProgressDelete)); err != nil {
230+
log.Errf(s.c, "Failed to write to channel: %s", err)
231+
}
232+
sendExitStatus(1, channel)
233+
req.Reply(false, nil)
234+
return nil
235+
}
236+
if err := s.pushLock.Lock(repoName, time.Duration(0)); err != nil {
229237
log.Errf(s.c, multiplePush)
230238
// The error must be in git format
231239
if err := gitPktLine(channel, fmt.Sprintf("ERR %v\n", multiplePush)); err != nil {
@@ -244,7 +252,16 @@ func (s *server) answer(channel ssh.Channel, requests <-chan *ssh.Request, sshCo
244252
cxt.Put("repository", parts[1])
245253
sshGitReceive := cxt.Get("route.sshd.sshGitReceive", "sshGitReceive").(string)
246254
err := router.HandleRequest(sshGitReceive, cxt, true)
247-
buildingRepos.Unlock(repoName, time.Duration(0))
255+
if err := s.pushLock.Unlock(repoName, time.Duration(0)); err != nil {
256+
log.Errf(s.c, "unable to unlock repository lock for %s (%s)", repoName, err)
257+
// TODO: this is an important error case that needs to be covered
258+
// Probably the best solution is to change the lock into a lease so that even on unlock failures, RepositoryLock will eventually yield
259+
}
260+
if err := s.deleteLock.Unlock(repoName, time.Duration(0)); err != nil {
261+
log.Errf(s.c, "unable to unlock delete lock for %s (%s)", repoName, err)
262+
// TODO: this is an important error case that needs to be covered
263+
// Probably the best solution is to change the lock into a lease so that even on unlock failures, RepositoryLock will eventually yield
264+
}
248265
var xs uint32
249266
if err != nil {
250267
log.Errf(s.c, "Failed git receive: %v", err)

0 commit comments

Comments
 (0)