github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/boskos/janitor/janitor_test.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "errors" 21 "fmt" 22 "math/rand" 23 "sync" 24 "sync/atomic" 25 "testing" 26 "time" 27 28 "k8s.io/test-infra/boskos/common" 29 ) 30 31 type fakeBoskos struct { 32 lock sync.Mutex 33 wg sync.WaitGroup 34 resources []common.Resource 35 } 36 37 // Create a fake client 38 func CreateFakeBoskos(resources int, types []string) *fakeBoskos { 39 fb := &fakeBoskos{} 40 r := rand.New(rand.NewSource(99)) 41 for i := 0; i < resources; i++ { 42 fb.resources = append(fb.resources, 43 common.Resource{ 44 Name: fmt.Sprintf("res-%d", i), 45 Type: types[r.Intn(len(types))], 46 State: "dirty", 47 }) 48 } 49 50 return fb 51 } 52 53 func (fb *fakeBoskos) Acquire(rtype string, state string, dest string) (string, error) { 54 fb.lock.Lock() 55 defer fb.lock.Unlock() 56 57 for idx := range fb.resources { 58 r := &fb.resources[idx] 59 if r.State == state { 60 r.State = dest 61 fb.wg.Add(1) 62 return r.Name, nil 63 } 64 } 65 66 return "", nil 67 } 68 69 func (fb *fakeBoskos) ReleaseOne(name string, dest string) error { 70 fb.lock.Lock() 71 defer fb.lock.Unlock() 72 73 for idx := range fb.resources { 74 r := &fb.resources[idx] 75 if r.Name == name { 76 r.State = dest 77 fb.wg.Done() 78 return nil 79 } 80 } 81 82 return fmt.Errorf("no resource %v", name) 83 } 84 85 // waitTimeout waits for the waitgroup for the specified max timeout. 86 // Returns true if waiting timed out. 87 func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool { 88 c := make(chan struct{}) 89 go func() { 90 defer close(c) 91 wg.Wait() 92 }() 93 select { 94 case <-c: 95 return false // completed normally 96 case <-time.After(timeout): 97 return true // timed out 98 } 99 } 100 101 func TestNormal(t *testing.T) { 102 var totalClean int32 = 0 103 104 fakeClean := func(p string) error { 105 atomic.AddInt32(&totalClean, 1) 106 return nil 107 } 108 109 types := []string{"a", "b", "c", "d"} 110 fb := CreateFakeBoskos(1000, types) 111 112 buffer := setup(fb, poolSize, bufferSize, fakeClean) 113 totalAcquire := run(fb, buffer, []string{"t"}) 114 115 if totalAcquire != len(fb.resources) { 116 t.Errorf("expect to acquire all resources(%d) from fake boskos, got %d", len(fb.resources), totalAcquire) 117 } 118 119 if waitTimeout(&fb.wg, time.Second) { 120 t.Fatal("expect janitor to finish!") 121 } 122 123 if int(totalClean) != len(fb.resources) { 124 t.Errorf("expect to clean all resources(%d) from fake boskos, got %d", len(fb.resources), totalClean) 125 } 126 127 for _, r := range fb.resources { 128 if r.State != "free" { 129 t.Errorf("resource %v, expect state free, got state %v", r.Name, r.State) 130 } 131 } 132 } 133 134 func FakeRun(fb *fakeBoskos, buffer chan string, res string) (int, error) { 135 timeout := time.NewTimer(5 * time.Second).C 136 137 totalClean := 0 138 139 for { 140 select { 141 case <-timeout: 142 return totalClean, errors.New("should not timedout") 143 default: 144 if proj, err := fb.Acquire(res, "dirty", "cleaning"); err != nil { 145 return totalClean, fmt.Errorf("acquire failed with %v", err) 146 } else if proj == "" { 147 return totalClean, errors.New("not expect to run out of resources") 148 } else { 149 if totalClean > poolSize+bufferSize+1 { 150 // poolSize in janitor, bufferSize more in janitor pool, 1 more hanging and will exit the loop 151 return totalClean, errors.New("should not acquire more than 12 projects") 152 } 153 boom := time.After(50 * time.Millisecond) 154 select { 155 case buffer <- proj: // normal case 156 totalClean++ 157 case <-boom: 158 return totalClean, nil 159 } 160 } 161 } 162 } 163 } 164 165 func TestMalfunctionJanitor(t *testing.T) { 166 167 stuck := make(chan string, 1) 168 fakeClean := func(p string) error { 169 <-stuck 170 return nil 171 } 172 173 fb := CreateFakeBoskos(200, []string{"t"}) 174 175 buffer := setup(fb, poolSize, bufferSize, fakeClean) 176 177 if totalClean, err := FakeRun(fb, buffer, "t"); err != nil { 178 t.Fatalf("run failed unexpectedly : %v", err) 179 } else if totalClean != poolSize+1 { 180 t.Errorf("expect to clean %d from fake boskos, got %d", poolSize+1, totalClean) 181 } 182 }