Skip to content

Instantly share code, notes, and snippets.

@knight42
Last active September 14, 2020 05:45
Show Gist options
  • Save knight42/73c13a0166a5d0884545f78bafc64acc to your computer and use it in GitHub Desktop.
Save knight42/73c13a0166a5d0884545f78bafc64acc to your computer and use it in GitHub Desktop.
scan non-synced shared informer factory in k8s unit tests
package main
import (
"bufio"
"fmt"
"os"
"path/filepath"
"strings"
"sync"
)
func shouldSkipLine(line string) bool {
return len(line) == 0 || strings.HasPrefix(line, "//")
}
type State int
const (
Initial State = iota
ImportBlock
TestFunc
)
func processFile(fp string, output chan<- string) error {
f, err := os.Open(fp)
if err != nil {
return err
}
defer f.Close()
scanner := bufio.NewScanner(f)
s := Initial
informerImported := false
createdSharedFactory := false
startedInformer := false
waitForCache := false
currentTestFunc := ""
for scanner.Scan() {
line := strings.TrimRight(scanner.Text(), " \t")
stripped := strings.TrimSpace(line)
if shouldSkipLine(stripped) {
continue
}
switch s {
case Initial:
if strings.HasPrefix(line, "import (") {
s = ImportBlock
continue
}
if strings.HasPrefix(line, "func Test") {
s = TestFunc
currentTestFunc = strings.TrimPrefix(line, "func ")
idx := strings.IndexByte(currentTestFunc, '(')
currentTestFunc = currentTestFunc[:idx]
continue
}
case ImportBlock:
if stripped == `"k8s.io/client-go/informers"` {
informerImported = true
continue
}
if line == ")" {
if !informerImported {
return nil
}
s = Initial
continue
}
case TestFunc:
if line == "}" {
if startedInformer && !waitForCache {
output <- fp + " " + currentTestFunc
}
s = Initial
createdSharedFactory = false
waitForCache = false
startedInformer = false
currentTestFunc = ""
continue
}
if strings.Contains(line, ".NewSharedInformerFactory(") {
createdSharedFactory = true
continue
}
if createdSharedFactory && strings.Contains(line, ".Start(") {
startedInformer = true
continue
}
if startedInformer && strings.Contains(line, ".WaitForCacheSync(") {
waitForCache = true
}
}
}
return nil
}
func worker(tasks <-chan string, output chan<- string, wg *sync.WaitGroup) {
defer wg.Done()
for fp := range tasks {
err := processFile(fp, output)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to process %s\n", fp)
}
}
}
func main() {
var wg sync.WaitGroup
const workers = 5
tasks := make(chan string, workers)
output := make(chan string, 10)
done := make(chan struct{})
for i := 0; i < workers; i++ {
wg.Add(1)
go worker(tasks, output, &wg)
}
go func() {
for s := range output {
fmt.Println(s)
}
close(done)
}()
filepath.Walk(os.Args[1], func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
name := info.Name()
isDir := info.IsDir()
if isDir && strings.HasPrefix(name, ".") {
return filepath.SkipDir
}
if isDir || !strings.HasSuffix(name, "_test.go") {
return nil
}
if info.Mode().IsRegular() {
tasks <- path
}
return nil
})
close(tasks)
wg.Wait()
close(output)
<-done
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment