mirror of
https://github.com/project-zot/zot.git
synced 2024-12-16 21:56:37 -05:00
17d1338af1
This change introduces OpenID authn by using providers such as Github, Gitlab, Google and Dex. User sessions are now used for web clients to identify and persist an authenticated users session, thus not requiring every request to use credentials. Another change is apikey feature, users can create/revoke their api keys and use them to authenticate when using cli clients such as skopeo. eg: login: /auth/login?provider=github /auth/login?provider=gitlab and so on logout: /auth/logout redirectURL: /auth/callback/github /auth/callback/gitlab and so on If network policy doesn't allow inbound connections, this callback wont work! for more info read documentation added in this commit. Signed-off-by: Alex Stan <alexandrustan96@yahoo.ro> Signed-off-by: Petu Eusebiu <peusebiu@cisco.com> Co-authored-by: Alex Stan <alexandrustan96@yahoo.ro>
981 lines
22 KiB
Go
981 lines
22 KiB
Go
//go:build sync && scrub && metrics && search && apikey
|
|
// +build sync,scrub,metrics,search,apikey
|
|
|
|
package cli_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
"gopkg.in/resty.v1"
|
|
|
|
"zotregistry.io/zot/pkg/cli"
|
|
. "zotregistry.io/zot/pkg/test"
|
|
)
|
|
|
|
const readLogFileTimeout = 5 * time.Second
|
|
|
|
func TestServeExtensions(t *testing.T) {
|
|
oldArgs := os.Args
|
|
|
|
defer func() { os.Args = oldArgs }()
|
|
|
|
Convey("config file with no extensions", t, func(c C) {
|
|
port := GetFreePort()
|
|
baseURL := GetBaseURL(port)
|
|
logFile, err := os.CreateTemp("", "zot-log*.txt")
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(logFile.Name()) // clean up
|
|
|
|
content := fmt.Sprintf(`{
|
|
"storage": {
|
|
"rootDirectory": "/tmp/zot"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
}
|
|
}`, 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)
|
|
|
|
os.Args = []string{"cli_test", "serve", cfgfile.Name()}
|
|
go func() {
|
|
err = cli.NewServerRootCmd().Execute()
|
|
So(err, ShouldBeNil)
|
|
}()
|
|
|
|
WaitTillServerReady(baseURL)
|
|
data, err := os.ReadFile(logFile.Name())
|
|
So(err, ShouldBeNil)
|
|
So(string(data), ShouldContainSubstring, "\"Extensions\":null")
|
|
})
|
|
|
|
Convey("config file with empty extensions", t, func(c C) {
|
|
port := GetFreePort()
|
|
baseURL := GetBaseURL(port)
|
|
logFile, err := os.CreateTemp("", "zot-log*.txt")
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(logFile.Name()) // clean up
|
|
|
|
content := fmt.Sprintf(`{
|
|
"storage": {
|
|
"rootDirectory": "/tmp/zot"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
}
|
|
}`, 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)
|
|
|
|
os.Args = []string{"cli_test", "serve", cfgfile.Name()}
|
|
go func() {
|
|
err = cli.NewServerRootCmd().Execute()
|
|
So(err, ShouldBeNil)
|
|
}()
|
|
|
|
WaitTillServerReady(baseURL)
|
|
data, err := os.ReadFile(logFile.Name())
|
|
So(err, ShouldBeNil)
|
|
So(string(data), ShouldContainSubstring,
|
|
"\"Extensions\":{\"Search\":null,\"Sync\":null,\"Metrics\":null,\"Scrub\":null,\"Lint\":null,\"UI\":null,\"Mgmt\":null") //nolint:lll // gofumpt conflicts with lll
|
|
})
|
|
}
|
|
|
|
func testWithMetricsEnabled(cfgContentFormat string) {
|
|
port := GetFreePort()
|
|
baseURL := GetBaseURL(port)
|
|
logFile, err := os.CreateTemp("", "zot-log*.txt")
|
|
So(err, ShouldBeNil)
|
|
|
|
defer os.Remove(logFile.Name()) // clean up
|
|
|
|
content := fmt.Sprintf(cfgContentFormat, 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)
|
|
|
|
os.Args = []string{"cli_test", "serve", cfgfile.Name()}
|
|
|
|
go func() {
|
|
err = cli.NewServerRootCmd().Execute()
|
|
So(err, ShouldBeNil)
|
|
}()
|
|
WaitTillServerReady(baseURL)
|
|
|
|
resp, err := resty.R().Get(baseURL + "/metrics")
|
|
So(err, ShouldBeNil)
|
|
So(resp, ShouldNotBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
|
|
|
respStr := string(resp.Body())
|
|
So(respStr, ShouldContainSubstring, "zot_info")
|
|
|
|
data, err := os.ReadFile(logFile.Name())
|
|
So(err, ShouldBeNil)
|
|
So(string(data), ShouldContainSubstring,
|
|
"\"Metrics\":{\"Enable\":true,\"Prometheus\":{\"Path\":\"/metrics\"}}")
|
|
}
|
|
|
|
func TestServeMetricsExtension(t *testing.T) {
|
|
oldArgs := os.Args
|
|
|
|
defer func() { os.Args = oldArgs }()
|
|
|
|
Convey("no explicit enable", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "/tmp/zot"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"metrics": {
|
|
}
|
|
}
|
|
}`
|
|
testWithMetricsEnabled(content)
|
|
})
|
|
|
|
Convey("no explicit enable but with prometheus parameter", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "/tmp/zot"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"metrics": {
|
|
"prometheus": {
|
|
"path": "/metrics"
|
|
}
|
|
}
|
|
}
|
|
}`
|
|
testWithMetricsEnabled(content)
|
|
})
|
|
|
|
Convey("with explicit enable, but without prometheus parameter", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "/tmp/zot"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"metrics": {
|
|
"enable": true
|
|
}
|
|
}
|
|
}`
|
|
testWithMetricsEnabled(content)
|
|
})
|
|
|
|
Convey("with explicit disable", t, func(c C) {
|
|
port := GetFreePort()
|
|
baseURL := GetBaseURL(port)
|
|
logFile, err := os.CreateTemp("", "zot-log*.txt")
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(logFile.Name()) // clean up
|
|
|
|
content := fmt.Sprintf(`{
|
|
"storage": {
|
|
"rootDirectory": "/tmp/zot"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"metrics": {
|
|
"enable": false
|
|
}
|
|
}
|
|
}`, 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)
|
|
|
|
os.Args = []string{"cli_test", "serve", cfgfile.Name()}
|
|
go func() {
|
|
err = cli.NewServerRootCmd().Execute()
|
|
So(err, ShouldBeNil)
|
|
}()
|
|
WaitTillServerReady(baseURL)
|
|
|
|
resp, err := resty.R().Get(baseURL + "/metrics")
|
|
So(err, ShouldBeNil)
|
|
So(resp, ShouldNotBeNil)
|
|
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
|
|
|
|
data, err := os.ReadFile(logFile.Name())
|
|
So(err, ShouldBeNil)
|
|
So(string(data), ShouldContainSubstring,
|
|
"\"Metrics\":{\"Enable\":false,\"Prometheus\":{\"Path\":\"/metrics\"}}") //nolint:lll // gofumpt conflicts with lll
|
|
})
|
|
}
|
|
|
|
func TestServeSyncExtension(t *testing.T) {
|
|
oldArgs := os.Args
|
|
|
|
defer func() { os.Args = oldArgs }()
|
|
|
|
Convey("sync implicitly enabled", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "%s"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"sync": {
|
|
"registries": [{
|
|
"urls": ["http://localhost:8080"],
|
|
"tlsVerify": false,
|
|
"onDemand": true,
|
|
"maxRetries": 3,
|
|
"retryDelay": "15m",
|
|
"certDir": "",
|
|
"content":[
|
|
{
|
|
"prefix": "zot-test",
|
|
"tags": {
|
|
"regex": ".*",
|
|
"semver": true
|
|
}
|
|
}
|
|
]
|
|
}]
|
|
}
|
|
}
|
|
}`
|
|
|
|
logPath, err := runCLIWithConfig(t.TempDir(), content)
|
|
So(err, ShouldBeNil)
|
|
data, err := os.ReadFile(logPath)
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(logPath) // clean up
|
|
So(string(data), ShouldContainSubstring,
|
|
"\"Extensions\":{\"Search\":null,\"Sync\":{\"Enable\":true")
|
|
})
|
|
|
|
Convey("sync explicitly enabled", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "%s"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"sync": {
|
|
"enable": true,
|
|
"registries": [{
|
|
"urls": ["http://localhost:8080"],
|
|
"tlsVerify": false,
|
|
"onDemand": true,
|
|
"maxRetries": 3,
|
|
"retryDelay": "15m",
|
|
"certDir": "",
|
|
"content":[
|
|
{
|
|
"prefix": "zot-test",
|
|
"tags": {
|
|
"regex": ".*",
|
|
"semver": true
|
|
}
|
|
}
|
|
]
|
|
}]
|
|
}
|
|
}
|
|
}`
|
|
|
|
logPath, err := runCLIWithConfig(t.TempDir(), content)
|
|
So(err, ShouldBeNil)
|
|
data, err := os.ReadFile(logPath)
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(logPath) // clean up
|
|
So(string(data), ShouldContainSubstring,
|
|
"\"Extensions\":{\"Search\":null,\"Sync\":{\"Enable\":true")
|
|
})
|
|
|
|
Convey("sync explicitly disabled", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "%s"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"sync": {
|
|
"enable": false,
|
|
"registries": [{
|
|
"urls": ["http://127.0.0.1:8080"],
|
|
"tlsVerify": false,
|
|
"certDir": "",
|
|
"maxRetries": 3,
|
|
"retryDelay": "15m"
|
|
}]
|
|
}
|
|
}
|
|
}`
|
|
|
|
logPath, err := runCLIWithConfig(t.TempDir(), content)
|
|
So(err, ShouldBeNil)
|
|
data, err := os.ReadFile(logPath)
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(logPath) // clean up
|
|
So(string(data), ShouldContainSubstring,
|
|
"\"Extensions\":{\"Search\":null,\"Sync\":{\"Enable\":false")
|
|
})
|
|
}
|
|
|
|
func TestServeScrubExtension(t *testing.T) {
|
|
oldArgs := os.Args
|
|
|
|
defer func() { os.Args = oldArgs }()
|
|
|
|
Convey("scrub implicitly enabled", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "%s"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"scrub": {
|
|
}
|
|
}
|
|
}`
|
|
|
|
logPath, err := runCLIWithConfig(t.TempDir(), content)
|
|
So(err, ShouldBeNil)
|
|
data, err := os.ReadFile(logPath)
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(logPath) // clean up
|
|
dataStr := string(data)
|
|
So(dataStr, ShouldContainSubstring,
|
|
"\"Extensions\":{\"Search\":null,\"Sync\":null,\"Metrics\":null,\"Scrub\":{\"Enable\":true,\"Interval\":86400000000000},\"Lint\":null") //nolint:lll // gofumpt conflicts with lll
|
|
So(dataStr, ShouldNotContainSubstring,
|
|
"Scrub interval set to too-short interval < 2h, changing scrub duration to 2 hours and continuing.")
|
|
})
|
|
|
|
Convey("scrub implicitly enabled, but with scrub interval param set", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "%s"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"scrub": {
|
|
"interval": "1h"
|
|
}
|
|
}
|
|
}`
|
|
|
|
logPath, err := runCLIWithConfig(t.TempDir(), content)
|
|
So(err, ShouldBeNil)
|
|
data, err := os.ReadFile(logPath)
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(logPath) // clean up
|
|
// Even if in config we specified scrub interval=1h, the minimum interval is 2h
|
|
dataStr := string(data)
|
|
So(dataStr, ShouldContainSubstring, "\"Scrub\":{\"Enable\":true,\"Interval\":3600000000000}")
|
|
So(dataStr, ShouldContainSubstring,
|
|
"Scrub interval set to too-short interval < 2h, changing scrub duration to 2 hours and continuing.")
|
|
})
|
|
|
|
Convey("scrub explicitly enabled, but without scrub interval param set", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "%s"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"scrub": {
|
|
"enable": true
|
|
}
|
|
}
|
|
}`
|
|
|
|
logPath, err := runCLIWithConfig(t.TempDir(), content)
|
|
So(err, ShouldBeNil)
|
|
data, err := os.ReadFile(logPath)
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(logPath) // clean up
|
|
dataStr := string(data)
|
|
So(dataStr, ShouldContainSubstring,
|
|
"\"Extensions\":{\"Search\":null,\"Sync\":null,\"Metrics\":null,\"Scrub\":{\"Enable\":true,\"Interval\":86400000000000},\"Lint\":null") //nolint:lll // gofumpt conflicts with lll
|
|
So(dataStr, ShouldNotContainSubstring,
|
|
"Scrub interval set to too-short interval < 2h, changing scrub duration to 2 hours and continuing.")
|
|
})
|
|
|
|
Convey("scrub explicitly disabled", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "%s"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"scrub": {
|
|
"enable": false
|
|
}
|
|
}
|
|
}`
|
|
|
|
logPath, err := runCLIWithConfig(t.TempDir(), content)
|
|
So(err, ShouldBeNil)
|
|
data, err := os.ReadFile(logPath)
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(logPath) // clean up
|
|
dataStr := string(data)
|
|
So(dataStr, ShouldContainSubstring, "\"Scrub\":{\"Enable\":false,\"Interval\":86400000000000}")
|
|
So(dataStr, ShouldContainSubstring, "Scrub config not provided, skipping scrub")
|
|
So(dataStr, ShouldNotContainSubstring,
|
|
"Scrub interval set to too-short interval < 2h, changing scrub duration to 2 hours and continuing.")
|
|
})
|
|
}
|
|
|
|
func TestServeLintExtension(t *testing.T) {
|
|
oldArgs := os.Args
|
|
|
|
defer func() { os.Args = oldArgs }()
|
|
|
|
Convey("lint enabled", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "%s"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"lint": {
|
|
"enable": "true",
|
|
"mandatoryAnnotations": ["annot1"]
|
|
}
|
|
}
|
|
}`
|
|
|
|
logPath, err := runCLIWithConfig(t.TempDir(), content)
|
|
So(err, ShouldBeNil)
|
|
data, err := os.ReadFile(logPath)
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(logPath) // clean up
|
|
So(string(data), ShouldContainSubstring,
|
|
"\"Extensions\":{\"Search\":null,\"Sync\":null,\"Metrics\":null,\"Scrub\":null,\"Lint\":{\"Enable\":true,\"MandatoryAnnotations\":") //nolint:lll // gofumpt conflicts with lll
|
|
})
|
|
|
|
Convey("lint enabled", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "%s"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"lint": {
|
|
"enable": "false"
|
|
}
|
|
}
|
|
}`
|
|
|
|
logPath, err := runCLIWithConfig(t.TempDir(), content)
|
|
So(err, ShouldBeNil)
|
|
data, err := os.ReadFile(logPath)
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(logPath) // clean up
|
|
So(string(data), ShouldContainSubstring,
|
|
"\"Extensions\":{\"Search\":null,\"Sync\":null,\"Metrics\":null,\"Scrub\":null,\"Lint\":{\"Enable\":false,\"MandatoryAnnotations\":null}") //nolint:lll // gofumpt conflicts with lll
|
|
})
|
|
}
|
|
|
|
func TestServeSearchEnabled(t *testing.T) {
|
|
oldArgs := os.Args
|
|
|
|
defer func() { os.Args = oldArgs }()
|
|
|
|
Convey("search implicitly enabled", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "%s"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"search": {
|
|
}
|
|
}
|
|
}`
|
|
|
|
tempDir := t.TempDir()
|
|
logPath, err := runCLIWithConfig(tempDir, content)
|
|
So(err, ShouldBeNil)
|
|
// to avoid data race when multiple go routines write to trivy DB instance.
|
|
defer os.Remove(logPath) // clean up
|
|
|
|
substring := `"Extensions":{"Search":{"Enable":true,"CVE":null}`
|
|
|
|
found, err := readLogFileAndSearchString(logPath, substring, readLogFileTimeout)
|
|
|
|
if !found {
|
|
data, err := os.ReadFile(logPath)
|
|
So(err, ShouldBeNil)
|
|
t.Log(string(data))
|
|
}
|
|
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
})
|
|
}
|
|
|
|
func TestServeSearchEnabledCVE(t *testing.T) {
|
|
oldArgs := os.Args
|
|
|
|
defer func() { os.Args = oldArgs }()
|
|
|
|
Convey("search implicitly enabled with CVE param set", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "%s"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"search": {
|
|
"cve": {
|
|
"updateInterval": "1h"
|
|
}
|
|
}
|
|
}
|
|
}`
|
|
|
|
tempDir := t.TempDir()
|
|
logPath, err := runCLIWithConfig(tempDir, content)
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(logPath) // clean up
|
|
// to avoid data race when multiple go routines write to trivy DB instance.
|
|
WaitTillTrivyDBDownloadStarted(tempDir)
|
|
|
|
// The default config handling logic will convert the 1h interval to a 2h interval
|
|
substring := "\"Search\":{\"Enable\":true,\"CVE\":{\"UpdateInterval\":7200000000000,\"Trivy\":" +
|
|
"{\"DBRepository\":\"ghcr.io/aquasecurity/trivy-db\",\"JavaDBRepository\":\"ghcr.io/aquasecurity/trivy-java-db\"}}}"
|
|
|
|
found, err := readLogFileAndSearchString(logPath, substring, readLogFileTimeout)
|
|
|
|
defer func() {
|
|
if !found {
|
|
data, err := os.ReadFile(logPath)
|
|
So(err, ShouldBeNil)
|
|
t.Log(string(data))
|
|
}
|
|
}()
|
|
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
|
|
found, err = readLogFileAndSearchString(logPath, "updating the CVE database", readLogFileTimeout)
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
})
|
|
}
|
|
|
|
func TestServeSearchEnabledNoCVE(t *testing.T) {
|
|
oldArgs := os.Args
|
|
|
|
defer func() { os.Args = oldArgs }()
|
|
|
|
Convey("search explicitly enabled, but CVE parameter not set", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "%s"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"search": {
|
|
"enable": true
|
|
}
|
|
}
|
|
}`
|
|
|
|
tempDir := t.TempDir()
|
|
logPath, err := runCLIWithConfig(tempDir, content)
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(logPath) // clean up
|
|
|
|
substring := `"Extensions":{"Search":{"Enable":true,"CVE":null}` //nolint:lll // gofumpt conflicts with lll
|
|
found, err := readLogFileAndSearchString(logPath, substring, readLogFileTimeout)
|
|
|
|
if !found {
|
|
data, err := os.ReadFile(logPath)
|
|
So(err, ShouldBeNil)
|
|
t.Log(string(data))
|
|
}
|
|
|
|
So(found, ShouldBeTrue)
|
|
So(err, ShouldBeNil)
|
|
})
|
|
}
|
|
|
|
func TestServeSearchDisabled(t *testing.T) {
|
|
oldArgs := os.Args
|
|
|
|
defer func() { os.Args = oldArgs }()
|
|
|
|
Convey("search explicitly disabled", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "%s"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"search": {
|
|
"enable": false,
|
|
"cve": {
|
|
"updateInterval": "3h"
|
|
}
|
|
}
|
|
}
|
|
}`
|
|
|
|
logPath, err := runCLIWithConfig(t.TempDir(), content)
|
|
So(err, ShouldBeNil)
|
|
data, err := os.ReadFile(logPath)
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(logPath) // clean up
|
|
dataStr := string(data)
|
|
So(dataStr, ShouldContainSubstring,
|
|
"\"Search\":{\"Enable\":false,\"CVE\":{\"UpdateInterval\":10800000000000,\"Trivy\":null}")
|
|
So(dataStr, ShouldContainSubstring, "CVE config not provided, skipping CVE update")
|
|
So(dataStr, ShouldNotContainSubstring,
|
|
"CVE update interval set to too-short interval < 2h, changing update duration to 2 hours and continuing.")
|
|
})
|
|
}
|
|
|
|
func TestServeMgmtExtension(t *testing.T) {
|
|
oldArgs := os.Args
|
|
|
|
defer func() { os.Args = oldArgs }()
|
|
|
|
Convey("Mgmt implicitly enabled", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "%s"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"Mgmt": {
|
|
}
|
|
}
|
|
}`
|
|
|
|
logPath, err := runCLIWithConfig(t.TempDir(), content)
|
|
So(err, ShouldBeNil)
|
|
data, err := os.ReadFile(logPath)
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(logPath) // clean up
|
|
So(string(data), ShouldContainSubstring, "\"Mgmt\":{\"Enable\":true}")
|
|
})
|
|
|
|
Convey("Mgmt disabled", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "%s"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"Mgmt": {
|
|
"enable": "false"
|
|
}
|
|
}
|
|
}`
|
|
|
|
logPath, err := runCLIWithConfig(t.TempDir(), content)
|
|
So(err, ShouldBeNil)
|
|
data, err := os.ReadFile(logPath)
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(logPath) // clean up
|
|
So(string(data), ShouldContainSubstring, "\"Mgmt\":{\"Enable\":false}")
|
|
})
|
|
}
|
|
|
|
func TestServeAPIKeyExtension(t *testing.T) {
|
|
oldArgs := os.Args
|
|
|
|
defer func() { os.Args = oldArgs }()
|
|
|
|
Convey("apikey implicitly enabled", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "%s"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"apikey": {
|
|
}
|
|
}
|
|
}`
|
|
|
|
logPath, err := runCLIWithConfig(t.TempDir(), content)
|
|
So(err, ShouldBeNil)
|
|
data, err := os.ReadFile(logPath)
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(logPath) // clean up
|
|
So(string(data), ShouldContainSubstring, "\"APIKey\":{\"Enable\":true}")
|
|
})
|
|
|
|
Convey("apikey disabled", t, func(c C) {
|
|
content := `{
|
|
"storage": {
|
|
"rootDirectory": "%s"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "%s"
|
|
},
|
|
"log": {
|
|
"level": "debug",
|
|
"output": "%s"
|
|
},
|
|
"extensions": {
|
|
"apikey": {
|
|
"enable": "false"
|
|
}
|
|
}
|
|
}`
|
|
|
|
logPath, err := runCLIWithConfig(t.TempDir(), content)
|
|
So(err, ShouldBeNil)
|
|
data, err := os.ReadFile(logPath)
|
|
So(err, ShouldBeNil)
|
|
defer os.Remove(logPath) // clean up
|
|
So(string(data), ShouldContainSubstring, "\"APIKey\":{\"Enable\":false}")
|
|
})
|
|
}
|
|
|
|
func readLogFileAndSearchString(logPath string, stringToMatch string, timeout time.Duration) (bool, error) { //nolint:unparam,lll
|
|
ctx, cancelFunc := context.WithTimeout(context.Background(), timeout)
|
|
defer cancelFunc()
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return false, nil
|
|
default:
|
|
content, err := os.ReadFile(logPath)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if strings.Contains(string(content), stringToMatch) {
|
|
return true, nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// run cli and return output.
|
|
func runCLIWithConfig(tempDir string, config string) (string, error) {
|
|
port := GetFreePort()
|
|
baseURL := GetBaseURL(port)
|
|
|
|
logFile, err := os.CreateTemp(tempDir, "zot-log*.txt")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
cfgfile, err := os.CreateTemp(tempDir, "zot-test*.json")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
config = fmt.Sprintf(config, tempDir, port, logFile.Name())
|
|
|
|
_, err = cfgfile.Write([]byte(config))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
err = cfgfile.Close()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
os.Args = []string{"cli_test", "serve", cfgfile.Name()}
|
|
|
|
go func() {
|
|
err = cli.NewServerRootCmd().Execute()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}()
|
|
|
|
WaitTillServerReady(baseURL)
|
|
|
|
return logFile.Name(), nil
|
|
}
|