Skip to content

Commit 8e9de01

Browse files
author
Keerthan Mala
committed
fix(cleaner): remove the usage of watch api
1 parent 40d0e0c commit 8e9de01

4 files changed

Lines changed: 156 additions & 70 deletions

File tree

boot.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func main() {
8989
log.Printf("Starting deleted app cleaner")
9090
cleanerErrCh := make(chan error)
9191
go func() {
92-
if err := cleaner.Run(gitHomeDir, kubeClient.Namespaces(), fs); err != nil {
92+
if err := cleaner.Run(gitHomeDir, kubeClient.Namespaces(), fs, cnf.CleanerPollSleepDuration()); err != nil {
9393
cleanerErrCh <- err
9494
}
9595
}()

pkg/cleaner/cleaner.go

Lines changed: 88 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,110 @@
33
package cleaner
44

55
import (
6+
"io/ioutil"
67
"log"
8+
"path/filepath"
9+
"strings"
10+
"time"
711

812
"k8s.io/kubernetes/pkg/api"
913

1014
"github.com/deis/builder/pkg/k8s"
1115
"github.com/deis/builder/pkg/sys"
16+
"k8s.io/kubernetes/pkg/fields"
17+
"k8s.io/kubernetes/pkg/labels"
1218
)
1319

1420
const (
1521
dotGitSuffix = ".git"
1622
)
1723

18-
// Run starts the deleted app cleaner. This function listens to the Kubernetes event stream for
19-
// events that indicate namespaces that were `DELETED`.
20-
// If the namespace name matches a folder on the local filesystem, this func deletes that folder.
21-
// Note that this function blocks until the watcher returned by `nsLister.Watch` is closed, so
22-
// you should launch it in a goroutine.
23-
func Run(gitHome string, nsLister k8s.NamespaceWatcher, fs sys.FS) error {
24-
watcher, err := nsLister.Watch(nil, nil, "")
24+
// localDirs returns all of the local directories immediately under gitHome that filter returns true for.
25+
// 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
26+
func localDirs(gitHome string, filter func(string) bool) ([]string, error) {
27+
fileInfos, err := ioutil.ReadDir(gitHome)
2528
if err != nil {
26-
log.Printf("unable to get watch events (%s)", err)
29+
return nil, err
2730
}
31+
var ret []string
32+
for _, fileInfo := range fileInfos {
33+
nm := fileInfo.Name()
34+
if len(nm) <= 0 || nm == "." || !fileInfo.IsDir() {
35+
continue
36+
}
37+
if filter(nm) {
38+
ret = append(ret, nm)
39+
}
40+
}
41+
return ret, nil
42+
}
43+
44+
// getDiff gets the directories that are not in namespaceList
45+
func getDiff(namespaceList []api.Namespace, dirs []string) []string {
46+
var ret []string
47+
48+
// create a set of lowercase namespace names
49+
namespacesSet := make(map[string]struct{})
50+
for _, ns := range namespaceList {
51+
lowerName := strings.ToLower(ns.Name)
52+
namespacesSet[lowerName] = struct{}{}
53+
}
54+
55+
// get dirs not in the namespaces set
56+
for _, dir := range dirs {
57+
lowerName := strings.ToLower(dir)
58+
if _, ok := namespacesSet[lowerName]; !ok {
59+
ret = append(ret, lowerName)
60+
}
61+
}
62+
63+
return ret
64+
}
65+
66+
func stripSuffixes(strs []string, suffix string) []string {
67+
ret := make([]string, len(strs))
68+
for i, str := range strs {
69+
idx := strings.LastIndex(str, suffix)
70+
if idx >= 0 {
71+
ret[i] = str[:idx]
72+
} else {
73+
ret[i] = str
74+
}
75+
}
76+
return ret
77+
}
78+
79+
func dirHasGitSuffix(dir string) bool {
80+
return strings.HasSuffix(dir, dotGitSuffix)
81+
}
82+
83+
// 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.
84+
// On any error, it uses log messages to output a human readable description of what happened.
85+
func Run(gitHome string, nsLister k8s.NamespaceLister, fs sys.FS, pollSleepDuration time.Duration) error {
2886
for {
29-
select {
30-
case event, ok := <-watcher.ResultChan():
31-
if !ok {
32-
break
33-
}
34-
if event.Type == "DELETED" {
35-
switch event.Object.(type) {
36-
case (*api.Namespace):
37-
namespace := event.Object.(*api.Namespace)
38-
appToDelete := gitHome + "/" + namespace.ObjectMeta.Name + dotGitSuffix
39-
if err := fs.RemoveAll(appToDelete); err != nil {
40-
log.Printf("Cleaner error removing deleted app %s (%s)", appToDelete, err)
41-
}
42-
default:
43-
}
87+
nsList, err := nsLister.List(labels.Everything(), fields.Everything())
88+
if err != nil {
89+
log.Printf("Cleaner error listing namespaces (%s)", err)
90+
continue
91+
}
92+
93+
gitDirs, err := localDirs(gitHome, dirHasGitSuffix)
94+
if err != nil {
95+
log.Printf("Cleaner error listing local git directories (%s)", err)
96+
continue
97+
}
98+
99+
gitDirs = stripSuffixes(gitDirs, dotGitSuffix)
100+
101+
appsToDelete := getDiff(nsList.Items, gitDirs)
102+
103+
for _, appToDelete := range appsToDelete {
104+
dirToDelete := filepath.Join(gitHome, appToDelete+dotGitSuffix)
105+
if err := fs.RemoveAll(dirToDelete); err != nil {
106+
log.Printf("Cleaner error removing deleted app %s (%s)", dirToDelete, err)
44107
}
45108
}
46-
}
47109

110+
time.Sleep(pollSleepDuration)
111+
}
48112
}

pkg/cleaner/cleaner_test.go

Lines changed: 66 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,82 @@
11
package cleaner
22

33
import (
4+
"os"
5+
"path/filepath"
6+
"strings"
47
"testing"
58

6-
"time"
7-
8-
"github.com/deis/builder/pkg/sys"
9+
"github.com/arschles/assert"
910
"k8s.io/kubernetes/pkg/api"
10-
"k8s.io/kubernetes/pkg/fields"
11-
"k8s.io/kubernetes/pkg/labels"
12-
"k8s.io/kubernetes/pkg/watch"
1311
)
1412

15-
type namespace struct {
13+
func TestGetDiff(t *testing.T) {
14+
nsList := []api.Namespace{
15+
api.Namespace{ObjectMeta: api.ObjectMeta{Name: "app1"}},
16+
api.Namespace{ObjectMeta: api.ObjectMeta{Name: "app2"}},
17+
}
18+
dirList := []string{"app1", "app3"}
19+
diff := getDiff(nsList, dirList)
20+
assert.Equal(t, len(diff), 1, "number of items in the disjunction")
1621
}
1722

18-
// Watch returns a watch.Interface that watches the requested namespaces.
19-
func (r *namespace) Watch(label labels.Selector, field fields.Selector, resourceversion string) (watch.Interface, error) {
20-
nst := watch.NewFake()
21-
go func() {
22-
nst.Add(&api.Namespace{ObjectMeta: api.ObjectMeta{Name: "dir1"}})
23-
nst.Modify(&api.Namespace{ObjectMeta: api.ObjectMeta{Name: "dir1"}})
24-
nst.Delete(&api.Namespace{ObjectMeta: api.ObjectMeta{Name: "dir1"}})
25-
nst.Modify(&api.Namespace{ObjectMeta: api.ObjectMeta{Name: "dir2"}})
26-
nst.Delete(nil)
27-
nst.Add(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "dir3"}})
28-
nst.Delete(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "dir3"}})
29-
nst.Stop()
30-
}()
31-
return nst, nil
23+
func TestDirHasGitSuffix(t *testing.T) {
24+
assert.True(t, dirHasGitSuffix("a.git"), "'a.git' reported no git suffix")
25+
assert.False(t, dirHasGitSuffix("abc"), "'a' reported git suffix")
3226
}
3327

34-
func (r *namespace) IsAnAPIObject() {}
35-
36-
func TestCleanerRun(t *testing.T) {
37-
ns := &namespace{}
38-
fs := sys.NewFakeFS()
39-
dirhome := "/home/git"
40-
fs.Files["/home/git/dir1.git"] = []byte{}
41-
fs.Files["/home/git/dir2.git"] = []byte{}
42-
fs.Files["/home/git/dir3.git"] = []byte{}
43-
go Run(dirhome, ns, fs)
44-
time.Sleep(5 * time.Second)
45-
// Namespace with name dir1 got deleted directory should be deleted
46-
_, ok := fs.Files["/home/git/dir1.git"]
47-
if ok {
48-
t.Fatal("Test failed: Deleting a namespace should delete respective direcotry")
28+
func TestLocalDirs(t *testing.T) {
29+
wd, err := os.Getwd()
30+
assert.NoErr(t, err)
31+
pkgDir, err := filepath.Abs(wd + "/..")
32+
assert.NoErr(t, err)
33+
lDirs, err := localDirs(pkgDir, func(dir string) bool {
34+
// no directories with any dots in them
35+
return len(strings.Split(dir, ".")) == 1
36+
})
37+
assert.NoErr(t, err)
38+
39+
expectedPackages := map[string]int{
40+
"cleaner": 1,
41+
"conf": 1,
42+
"controller": 1,
43+
"env": 1,
44+
"git": 1,
45+
"gitreceive": 1,
46+
"healthsrv": 1,
47+
"k8s": 1,
48+
"sshd": 1,
49+
"storage": 1,
50+
"sys": 1,
4951
}
50-
// Namespace with name dir2 got modified directory should not be deleted
51-
_, ok = fs.Files["/home/git/dir2.git"]
52-
if !ok {
53-
t.Fatal("Test failed: Modyfiying a namespace should not delete respective direcotry")
52+
53+
actualPackages := map[string]int{}
54+
for _, lDir := range lDirs {
55+
actualPackages[lDir]++
5456
}
55-
// Pod with name dir3 got deleted directory should not be deleted
56-
_, ok = fs.Files["/home/git/dir3.git"]
57-
if !ok {
58-
t.Fatal("Test failed: Deleting a pod should not delete respective direcotry")
57+
assert.Equal(t, len(actualPackages), len(expectedPackages), "number of packages")
58+
for actualPackageName, actualNum := range actualPackages {
59+
if actualNum != 1 {
60+
t.Errorf("found %d %s packages", actualNum, actualPackageName)
61+
continue
62+
}
63+
expectedNum, ok := expectedPackages[actualPackageName]
64+
if !ok {
65+
t.Errorf("found unexpected package %s", actualPackageName)
66+
continue
67+
}
68+
if actualNum != expectedNum {
69+
t.Errorf("found %d %s packages, expected %d", actualNum, actualPackageName, expectedNum)
70+
continue
71+
}
72+
}
73+
}
74+
75+
func TestStripSuffixes(t *testing.T) {
76+
strs := []string{"a.git", "b.git", "c.git", "d"}
77+
newStrs := stripSuffixes(strs, dotGitSuffix)
78+
assert.Equal(t, len(newStrs), len(strs), "number of strings")
79+
for _, str := range newStrs {
80+
assert.False(t, strings.HasSuffix(str, dotGitSuffix), "string %s has suffix %s", str, dotGitSuffix)
5981
}
6082
}

pkg/sshd/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ type Config struct {
1010
SSHHostPort int `envconfig:"SSH_HOST_PORT" default:"2223" required:"true"`
1111
HealthSrvPort int `envconfig:"HEALTH_SERVER_PORT" default:"8092"`
1212
HealthSrvTestStorageRegion string `envconfig:"STORAGE_REGION" default:"us-east-1"`
13-
CleanerPollSleepDurationSec int `envconfig:"CLEANER_POLL_SLEEP_DURATION_SEC" default:"1"`
13+
CleanerPollSleepDurationSec int `envconfig:"CLEANER_POLL_SLEEP_DURATION_SEC" default:"5"`
1414
StorageType string `envconfig:"BUILDER_STORAGE" default:"minio"`
1515
SlugBuilderImagePullPolicy string `envconfig:"SLUG_BUILDER_IMAGE_PULL_POLICY" default:"Always"`
1616
DockerBuilderImagePullPolicy string `envconfig:"DOCKER_BUILDER_IMAGE_PULL_POLICY" default:"Always"`

0 commit comments

Comments
 (0)