mirror of
https://github.com/project-zot/zot.git
synced 2025-01-06 22:40:28 -05:00
ba6f347d8d
Which could be imported independently. See more details: 1. "zotregistry.io/zot/pkg/test/common" - currently used as tcommon "zotregistry.io/zot/pkg/test/common" - inside pkg/test test "zotregistry.io/zot/pkg/test/common" - in tests . "zotregistry.io/zot/pkg/test/common" - in tests Decouple zb from code in test/pkg in order to keep the size small. 2. "zotregistry.io/zot/pkg/test/image-utils" - curently used as . "zotregistry.io/zot/pkg/test/image-utils" 3. "zotregistry.io/zot/pkg/test/deprecated" - curently used as "zotregistry.io/zot/pkg/test/deprecated" This one will bre replaced gradually by image-utils in the future. 4. "zotregistry.io/zot/pkg/test/signature" - (cosign + notation) use as "zotregistry.io/zot/pkg/test/signature" 5. "zotregistry.io/zot/pkg/test/auth" - (bearer + oidc) curently used as authutils "zotregistry.io/zot/pkg/test/auth" 6. "zotregistry.io/zot/pkg/test/oci-utils" - curently used as ociutils "zotregistry.io/zot/pkg/test/oci-utils" Some unused functions were removed, some were replaced, and in a few cases specific funtions were moved to the files they were used in. Added an interface for the StoreController, this reduces the number of imports of the entire image store, decreasing binary size for tests. If the zb code was still coupled with pkg/test, this would have reflected in zb size. Signed-off-by: Andrei Aaron <aaaron@luxoft.com>
214 lines
5.2 KiB
Go
214 lines
5.2 KiB
Go
//go:build stress
|
|
// +build stress
|
|
|
|
package server_test
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"sync"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
|
|
"zotregistry.io/zot/pkg/api"
|
|
"zotregistry.io/zot/pkg/api/config"
|
|
cli "zotregistry.io/zot/pkg/cli/server"
|
|
test "zotregistry.io/zot/pkg/test/common"
|
|
)
|
|
|
|
const (
|
|
MaxFileDescriptors = 100
|
|
WorkerRunningTime = 60 * time.Second
|
|
)
|
|
|
|
func TestSressTooManyOpenFiles(t *testing.T) {
|
|
oldArgs := os.Args
|
|
|
|
defer func() { os.Args = oldArgs }()
|
|
|
|
Convey("configure zot with dedupe=false", t, func(c C) {
|
|
// In case one of the So()-assertions will fail it will allow us to print
|
|
// all the log files to figure out what happened in this test (zot log file, scrub output, storage rootFS tree)
|
|
SetDefaultFailureMode(FailureContinues)
|
|
|
|
initialLimit, err := setMaxOpenFilesLimit(MaxFileDescriptors)
|
|
So(err, ShouldBeNil)
|
|
|
|
port := test.GetFreePort()
|
|
conf := config.New()
|
|
conf.HTTP.Port = port
|
|
conf.Storage.Dedupe = false
|
|
conf.Storage.GC = true
|
|
|
|
logFile, err := os.CreateTemp("", "zot-log*.txt")
|
|
So(err, ShouldBeNil)
|
|
|
|
defer func() {
|
|
data, err := os.ReadFile(logFile.Name())
|
|
if err != nil {
|
|
t.Logf("error when reading zot log file:\n%s\n", err)
|
|
}
|
|
t.Logf("\n\n Zot log file content:\n%s\n", string(data))
|
|
os.Remove(logFile.Name())
|
|
}()
|
|
t.Log("Log file is: ", logFile.Name())
|
|
conf.Log.Output = logFile.Name()
|
|
|
|
ctlr := api.NewController(conf)
|
|
dir := t.TempDir()
|
|
|
|
defer func() {
|
|
// list the content of the directory (useful in case of test fail)
|
|
out, err := exec.Command("du", "-ab", dir).Output()
|
|
if err != nil {
|
|
t.Logf("error when listing storage files:\n%s\n", err)
|
|
}
|
|
t.Logf("Listing Storage root FS:\n%s\n", out)
|
|
}()
|
|
|
|
t.Log("Storage root dir is: ", dir)
|
|
ctlr.Config.Storage.RootDirectory = dir
|
|
|
|
ctrlManager := test.NewControllerManager(ctlr)
|
|
ctrlManager.StartAndWait(port)
|
|
|
|
content := fmt.Sprintf(`{
|
|
"storage": {
|
|
"rootDirectory": "%s",
|
|
"dedupe": %t,
|
|
"gc": %t
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
}
|
|
}`, dir, conf.Storage.Dedupe, conf.Storage.GC, port, logFile.Name())
|
|
|
|
cfgfile, err := os.CreateTemp("", "zot-test*.json")
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(cfgfile.Name()) // clean up
|
|
_, err = cfgfile.Write([]byte(content))
|
|
So(err, ShouldBeNil)
|
|
err = cfgfile.Close()
|
|
So(err, ShouldBeNil)
|
|
|
|
skopeoArgs := []string{
|
|
"copy", "--format=oci", "--insecure-policy", "--dest-tls-verify=false",
|
|
"docker://public.ecr.aws/zomato/alpine:3.11.3", fmt.Sprintf("oci:%s:alpine", dir),
|
|
}
|
|
out, err := exec.Command("skopeo", skopeoArgs...).Output()
|
|
if err != nil {
|
|
t.Logf("\nerror on skopeo copy:\n%s\n", err)
|
|
}
|
|
So(err, ShouldBeNil)
|
|
t.Logf("\nCopy test image locally:\n%s\n", out)
|
|
|
|
var wg sync.WaitGroup
|
|
for i := 1; i <= MaxFileDescriptors; i++ {
|
|
wg.Add(1)
|
|
|
|
i := i
|
|
|
|
go func() {
|
|
defer wg.Done()
|
|
worker(i, port, dir)
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
|
|
_, err = setMaxOpenFilesLimit(initialLimit)
|
|
So(err, ShouldBeNil)
|
|
|
|
data, err := os.ReadFile(logFile.Name())
|
|
So(err, ShouldBeNil)
|
|
So(string(data), ShouldContainSubstring, "too many open files")
|
|
|
|
ctrlManager.StopServer()
|
|
time.Sleep(2 * time.Second)
|
|
|
|
scrubFile, err := os.CreateTemp("", "zot-scrub*.txt")
|
|
So(err, ShouldBeNil)
|
|
|
|
defer func() {
|
|
data, err := os.ReadFile(scrubFile.Name())
|
|
if err != nil {
|
|
t.Logf("error when reading zot scrub file:\n%s\n", err)
|
|
}
|
|
t.Logf("\n\n Zot scrub file content:\n%s\n", string(data))
|
|
os.Remove(scrubFile.Name())
|
|
}()
|
|
t.Log("Scrub file is: ", scrubFile.Name())
|
|
|
|
os.Args = []string{"cli_test", "scrub", cfgfile.Name()}
|
|
cobraCmd := cli.NewServerRootCmd()
|
|
cobraCmd.SetOut(scrubFile)
|
|
err = cobraCmd.Execute()
|
|
So(err, ShouldBeNil)
|
|
|
|
data, err = os.ReadFile(scrubFile.Name())
|
|
So(err, ShouldBeNil)
|
|
So(string(data), ShouldNotContainSubstring, "affected")
|
|
})
|
|
}
|
|
|
|
func worker(id int, zotPort, rootDir string) {
|
|
start := time.Now()
|
|
|
|
for i := 0; ; i++ {
|
|
sourceImg := fmt.Sprintf("oci:%s:alpine", rootDir)
|
|
destImg := fmt.Sprintf("docker://localhost:%s/client%d:%d", zotPort, id, i)
|
|
|
|
skopeoArgs := []string{
|
|
"copy", "--format=oci", "--insecure-policy", "--dest-tls-verify=false",
|
|
sourceImg, destImg,
|
|
}
|
|
err := exec.Command("skopeo", skopeoArgs...).Run()
|
|
if err != nil { //nolint: wsl
|
|
continue // we expect clients to receive errors due to FD limit reached on server
|
|
}
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
end := time.Now()
|
|
latency := end.Sub(start)
|
|
|
|
if latency > WorkerRunningTime {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
func setMaxOpenFilesLimit(limit uint64) (uint64, error) {
|
|
var rLimit syscall.Rlimit
|
|
|
|
err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
fmt.Println("Current max. open files ", rLimit.Cur)
|
|
initialLimit := rLimit.Cur
|
|
rLimit.Cur = limit
|
|
fmt.Println("Changing max. open files to ", limit)
|
|
|
|
err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
|
|
if err != nil {
|
|
return initialLimit, err
|
|
}
|
|
|
|
err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
|
|
if err != nil {
|
|
return initialLimit, err
|
|
}
|
|
|
|
fmt.Println("Max. open files is set to", rLimit.Cur)
|
|
|
|
return initialLimit, nil
|
|
}
|