Skip to content

Commit 65f7f76

Browse files
author
Aaron Schlesinger
committed
fix(boot.go,pkg): switching to a global mutex for the cleaner to use
1 parent db9dd7a commit 65f7f76

5 files changed

Lines changed: 62 additions & 41 deletions

File tree

boot.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func main() {
4949
os.Exit(1)
5050
}
5151
pushLock := sshd.NewInMemoryRepositoryLock()
52-
deleteLock := sshd.NewInMemoryRepositoryLock()
52+
cleanerRef := cleaner.NewRef()
5353
circ := sshd.NewCircuit()
5454

5555
s3Client, err := storage.GetClient(cnf.HealthSrvTestStorageRegion)
@@ -72,15 +72,15 @@ func main() {
7272
log.Printf("Starting deleted app cleaner")
7373
cleanerErrCh := make(chan error)
7474
go func() {
75-
if err := cleaner.Run(gitHomeDir, kubeClient.Namespaces(), deleteLock, cnf.CleanerPollSleepDuration()); err != nil {
75+
if err := cleanerRef.Run(gitHomeDir, kubeClient.Namespaces(), cleanerRef, cnf.CleanerPollSleepDuration()); err != nil {
7676
cleanerErrCh <- err
7777
}
7878
}()
7979

8080
log.Printf("Starting SSH server on %s:%d", cnf.SSHHostIP, cnf.SSHHostPort)
8181
sshCh := make(chan int)
8282
go func() {
83-
sshCh <- pkg.RunBuilder(cnf.SSHHostIP, cnf.SSHHostPort, gitHomeDir, circ, pushLock, deleteLock)
83+
sshCh <- pkg.RunBuilder(cnf.SSHHostIP, cnf.SSHHostPort, gitHomeDir, circ, pushLock, cleanerRef)
8484
}()
8585

8686
select {

pkg/builder.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"github.com/Masterminds/cookoo"
1212
clog "github.com/Masterminds/cookoo/log"
13+
"github.com/deis/builder/pkg/cleaner"
1314
"github.com/deis/builder/pkg/sshd"
1415
)
1516

@@ -27,7 +28,7 @@ const (
2728
// Git.
2829
//
2930
// Run returns on of the Status* status code constants.
30-
func RunBuilder(sshHostIP string, sshHostPort int, gitHomeDir string, sshServerCircuit *sshd.Circuit, pushLock sshd.RepositoryLock, deleteLock sshd.RepositoryLock) int {
31+
func RunBuilder(sshHostIP string, sshHostPort int, gitHomeDir string, sshServerCircuit *sshd.Circuit, pushLock sshd.RepositoryLock, cleanerRef cleaner.Ref) int {
3132
reg, router, ocxt := cookoo.Cookoo()
3233
log.SetFlags(0) // Time is captured elsewhere.
3334

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

pkg/cleaner/cleaner.go

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import (
77
"os"
88
"path/filepath"
99
"strings"
10+
"sync"
1011
"time"
1112

1213
"github.com/deis/builder/pkg/k8s"
13-
"github.com/deis/builder/pkg/sshd"
1414
"k8s.io/kubernetes/pkg/api"
1515
"k8s.io/kubernetes/pkg/fields"
1616
"k8s.io/kubernetes/pkg/labels"
@@ -20,6 +20,22 @@ const (
2020
dotGitSuffix = ".git"
2121
)
2222

23+
type Ref struct {
24+
mut *sync.Mutex
25+
}
26+
27+
func NewRef() Ref {
28+
return Ref{mut: new(sync.Mutex)}
29+
}
30+
31+
func (c Ref) Lock() {
32+
c.mut.Lock()
33+
}
34+
35+
func (c Ref) Unlock() {
36+
c.mut.Unlock()
37+
}
38+
2339
// localDirs returns all of the local directories immediately under gitHome that filter returns true for. filter will receive only the names of each of the top level directories (not their fully qualified paths), and should return true if it should be included in the output
2440
func localDirs(gitHome string, filter func(string) bool) ([]string, error) {
2541
fileInfos, err := ioutil.ReadDir(gitHome)
@@ -79,7 +95,7 @@ func dirHasGitSuffix(dir string) bool {
7995
}
8096

8197
// 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 messages to output a human readable description of what happened.
82-
func Run(gitHome string, nsLister k8s.NamespaceLister, repoLock sshd.RepositoryLock, pollSleepDuration time.Duration) error {
98+
func (c Ref) Run(gitHome string, nsLister k8s.NamespaceLister, ref Ref, pollSleepDuration time.Duration) error {
8399
for {
84100
nsList, err := nsLister.List(labels.Everything(), fields.Everything())
85101
if err != nil {
@@ -104,21 +120,11 @@ func Run(gitHome string, nsLister k8s.NamespaceLister, repoLock sshd.RepositoryL
104120

105121
for _, appToDelete := range appsToDelete {
106122
dirToDelete := appToDelete + dotGitSuffix
107-
// this value must be the same as what's in sshd/server.go. search for repoName := parts[0]
108-
lockValue := "/" + filepath.Base(dirToDelete)
109-
if err := repoLock.Lock(lockValue, time.Duration(0)); err != nil {
110-
log.Printf("Cleaner error locking directory %s for deletion (%s)", dirToDelete, err)
111-
continue
112-
}
113-
123+
ref.Lock()
114124
if err := os.RemoveAll(dirToDelete); err != nil {
115125
log.Printf("Cleaner error removing deleted app %s (%s)", dirToDelete, err)
116126
}
117-
118-
if err := repoLock.Unlock(lockValue, time.Duration(0)); err != nil {
119-
log.Printf("Cleaner error unlocking directory %s for deletion (%s)", dirToDelete, err)
120-
continue
121-
}
127+
ref.Unlock()
122128
}
123129

124130
time.Sleep(pollSleepDuration)

pkg/sshd/lock.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,27 @@ type RepositoryLock interface {
2020
Unlock(repoName string, timeout time.Duration) error
2121
}
2222

23+
type mutexLock struct {
24+
mut *sync.Mutex
25+
}
26+
27+
func (m *mutexLock) Lock(string, time.Duration) error {
28+
m.mut.Lock()
29+
return nil
30+
}
31+
32+
func (m *mutexLock) Unlock(string, time.Duration) error {
33+
m.mut.Unlock()
34+
return nil
35+
}
36+
2337
// NewInMemoryRepositoryLock returns a new instance of a RepositoryLock
2438
func NewInMemoryRepositoryLock() RepositoryLock {
25-
return &inMemoryRepoLock{
26-
mutex: &sync.RWMutex{},
27-
dataMap: make(map[string]bool),
28-
}
39+
return &mutexLock{mut: new(sync.Mutex)}
40+
// return &inMemoryRepoLock{
41+
// mutex: &sync.RWMutex{},
42+
// dataMap: make(map[string]bool),
43+
// }
2944
}
3045

3146
type inMemoryRepoLock struct {

pkg/sshd/server.go

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/Masterminds/cookoo"
1818
"github.com/Masterminds/cookoo/log"
1919
"github.com/Masterminds/cookoo/safely"
20+
"github.com/deis/builder/pkg/cleaner"
2021
"golang.org/x/crypto/ssh"
2122
)
2223

@@ -50,7 +51,15 @@ const (
5051
//
5152
// This puts the following variables into the context:
5253
// - ssh.Closer (chan interface{}): Send a message to this to shutdown the server.
53-
func Serve(reg *cookoo.Registry, router *cookoo.Router, serverCircuit *Circuit, gitHomeDir string, concurrentPushLock RepositoryLock, concurrentDeleteLock RepositoryLock, c cookoo.Context) cookoo.Interrupt {
54+
func Serve(
55+
reg *cookoo.Registry,
56+
router *cookoo.Router,
57+
serverCircuit *Circuit,
58+
gitHomeDir string,
59+
concurrentPushLock RepositoryLock,
60+
cleanerRef cleaner.Ref,
61+
c cookoo.Context) cookoo.Interrupt {
62+
5463
hostkeys := c.Get(HostKeys, []ssh.Signer{}).([]ssh.Signer)
5564
addr := c.Get(Address, "0.0.0.0:2223").(string)
5665
cfg := c.Get(ServerConfig, &ssh.ServerConfig{}).(*ssh.ServerConfig)
@@ -69,7 +78,7 @@ func Serve(reg *cookoo.Registry, router *cookoo.Router, serverCircuit *Circuit,
6978
c: c,
7079
gitHome: gitHomeDir,
7180
pushLock: concurrentPushLock,
72-
deleteLock: concurrentDeleteLock,
81+
cleanerRef: cleanerRef,
7382
}
7483

7584
closer := make(chan interface{}, 1)
@@ -87,7 +96,7 @@ type server struct {
8796
c cookoo.Context
8897
gitHome string
8998
pushLock RepositoryLock
90-
deleteLock RepositoryLock
99+
cleanerRef cleaner.Ref
91100
}
92101

93102
// listen handles accepting and managing connections. However, since closer
@@ -223,16 +232,8 @@ func (s *server) answer(channel ssh.Channel, requests <-chan *ssh.Request, sshCo
223232
}
224233

225234
repoName := parts[1]
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-
}
235+
fmt.Printf("Server locking %s for delete\n", repoName)
236+
s.cleanerRef.Lock()
236237
if err := s.pushLock.Lock(repoName, time.Duration(0)); err != nil {
237238
log.Errf(s.c, multiplePush)
238239
// The error must be in git format
@@ -241,6 +242,7 @@ func (s *server) answer(channel ssh.Channel, requests <-chan *ssh.Request, sshCo
241242
}
242243
sendExitStatus(1, channel)
243244
req.Reply(false, nil)
245+
s.cleanerRef.Unlock()
244246
return nil
245247
}
246248

@@ -257,11 +259,8 @@ func (s *server) answer(channel ssh.Channel, requests <-chan *ssh.Request, sshCo
257259
// TODO: this is an important error case that needs to be covered
258260
// Probably the best solution is to change the lock into a lease so that even on unlock failures, RepositoryLock will eventually yield
259261
}
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-
}
262+
s.cleanerRef.Unlock()
263+
fmt.Printf("Server unlocked %s for delete\n", repoName)
265264
var xs uint32
266265
if err != nil {
267266
log.Errf(s.c, "Failed git receive: %v", err)

0 commit comments

Comments
 (0)