mirror of
https://github.com/project-zot/zot.git
synced 2026-06-16 20:38:08 +08:00
storage: improve/fix oci image validation
Signed-off-by: Ramkumar Chinchani <rchincha@cisco.com>
This commit is contained in:
committed by
Ramkumar Chinchani
parent
8d6b36a61b
commit
87084f286b
@@ -0,0 +1,206 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/big"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
godigest "github.com/opencontainers/go-digest"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/phayes/freeport"
|
||||
"gopkg.in/resty.v1"
|
||||
)
|
||||
|
||||
const (
|
||||
BaseURL = "http://127.0.0.1:%s"
|
||||
BaseSecureURL = "https://127.0.0.1:%s"
|
||||
SleepTime = 100 * time.Millisecond
|
||||
)
|
||||
|
||||
func GetFreePort() string {
|
||||
port, err := freeport.GetFreePort()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return fmt.Sprint(port)
|
||||
}
|
||||
|
||||
func GetBaseURL(port string) string {
|
||||
return fmt.Sprintf(BaseURL, port)
|
||||
}
|
||||
|
||||
func GetSecureBaseURL(port string) string {
|
||||
return fmt.Sprintf(BaseSecureURL, port)
|
||||
}
|
||||
|
||||
func MakeHtpasswdFile() string {
|
||||
// bcrypt(username="test", passwd="test")
|
||||
content := "test:$2y$05$hlbSXDp6hzDLu6VwACS39ORvVRpr3OMR4RlJ31jtlaOEGnPjKZI1m\n"
|
||||
|
||||
return MakeHtpasswdFileFromString(content)
|
||||
}
|
||||
|
||||
func MakeHtpasswdFileFromString(fileContent string) string {
|
||||
htpasswdFile, err := ioutil.TempFile("", "htpasswd-")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// bcrypt(username="test", passwd="test")
|
||||
content := []byte(fileContent)
|
||||
if err := ioutil.WriteFile(htpasswdFile.Name(), content, 0o600); err != nil { //nolint:gomnd
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return htpasswdFile.Name()
|
||||
}
|
||||
|
||||
func Location(baseURL string, resp *resty.Response) string {
|
||||
// For some API responses, the Location header is set and is supposed to
|
||||
// indicate an opaque value. However, it is not clear if this value is an
|
||||
// absolute URL (https://server:port/v2/...) or just a path (/v2/...)
|
||||
// zot implements the latter as per the spec, but some registries appear to
|
||||
// return the former - this needs to be clarified
|
||||
loc := resp.Header().Get("Location")
|
||||
|
||||
uloc, err := url.Parse(loc)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
path := uloc.Path
|
||||
if query := uloc.RawQuery; query != "" {
|
||||
path += "?" + query
|
||||
}
|
||||
|
||||
return baseURL + path
|
||||
}
|
||||
|
||||
func CopyFiles(sourceDir string, destDir string) error {
|
||||
sourceMeta, err := os.Stat(sourceDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CopyFiles os.Stat failed: %w", err)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(destDir, sourceMeta.Mode()); err != nil {
|
||||
return fmt.Errorf("CopyFiles os.MkdirAll failed: %w", err)
|
||||
}
|
||||
|
||||
files, err := ioutil.ReadDir(sourceDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CopyFiles ioutil.ReadDir failed: %w", err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
sourceFilePath := path.Join(sourceDir, file.Name())
|
||||
destFilePath := path.Join(destDir, file.Name())
|
||||
|
||||
if file.IsDir() {
|
||||
if err = CopyFiles(sourceFilePath, destFilePath); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
sourceFile, err := os.Open(sourceFilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CopyFiles os.Open failed: %w", err)
|
||||
}
|
||||
defer sourceFile.Close()
|
||||
|
||||
destFile, err := os.Create(destFilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CopyFiles os.Create failed: %w", err)
|
||||
}
|
||||
defer destFile.Close()
|
||||
|
||||
if _, err = io.Copy(destFile, sourceFile); err != nil {
|
||||
return fmt.Errorf("io.Copy failed: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func WaitTillServerReady(url string) {
|
||||
for {
|
||||
_, err := resty.R().Get(url)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(SleepTime)
|
||||
}
|
||||
}
|
||||
|
||||
// Adapted from https://gist.github.com/dopey/c69559607800d2f2f90b1b1ed4e550fb
|
||||
func randomString(n int) string {
|
||||
const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"
|
||||
|
||||
ret := make([]byte, n)
|
||||
|
||||
for count := 0; count < n; count++ {
|
||||
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ret[count] = letters[num.Int64()]
|
||||
}
|
||||
|
||||
return string(ret)
|
||||
}
|
||||
|
||||
func GetRandomImageConfig() ([]byte, godigest.Digest) {
|
||||
const maxLen = 16
|
||||
|
||||
randomAuthor := randomString(maxLen)
|
||||
|
||||
config := imagespec.Image{
|
||||
Architecture: "amd64",
|
||||
OS: "linux",
|
||||
RootFS: imagespec.RootFS{
|
||||
Type: "layers",
|
||||
DiffIDs: []godigest.Digest{},
|
||||
},
|
||||
Author: randomAuthor,
|
||||
}
|
||||
|
||||
configBlobContent, err := json.MarshalIndent(&config, "", "\t")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
configBlobDigestRaw := godigest.FromBytes(configBlobContent)
|
||||
|
||||
return configBlobContent, configBlobDigestRaw
|
||||
}
|
||||
|
||||
func GetImageConfig() ([]byte, godigest.Digest) {
|
||||
config := imagespec.Image{
|
||||
Architecture: "amd64",
|
||||
OS: "linux",
|
||||
RootFS: imagespec.RootFS{
|
||||
Type: "layers",
|
||||
DiffIDs: []godigest.Digest{},
|
||||
},
|
||||
Author: "some author",
|
||||
}
|
||||
|
||||
configBlobContent, err := json.MarshalIndent(&config, "", "\t")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
configBlobDigestRaw := godigest.FromBytes(configBlobContent)
|
||||
|
||||
return configBlobContent, configBlobDigestRaw
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
//go:build extended
|
||||
// +build extended
|
||||
|
||||
package test_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"zotregistry.io/zot/pkg/test"
|
||||
)
|
||||
|
||||
func TestCopyFiles(t *testing.T) {
|
||||
Convey("sourceDir does not exist", t, func() {
|
||||
err := test.CopyFiles("/path/to/some/unexisting/directory", os.TempDir())
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
Convey("destDir is a file", t, func() {
|
||||
dir, err := ioutil.TempDir("", "copy-files-test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = test.CopyFiles("../../test/data", dir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defer os.RemoveAll(dir)
|
||||
err = test.CopyFiles(dir, "/etc/passwd")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
Convey("sourceDir does not have read permissions", t, func() {
|
||||
dir, err := ioutil.TempDir("", "copy-files-test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
err = os.Chmod(dir, 0o300)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = test.CopyFiles(dir, os.TempDir())
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
Convey("sourceDir has a subfolder that does not have read permissions", t, func() {
|
||||
dir, err := ioutil.TempDir("", "copy-files-test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
sdir := "subdir"
|
||||
err = os.Mkdir(path.Join(dir, sdir), 0o300)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = test.CopyFiles(dir, os.TempDir())
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
Convey("sourceDir has a file that does not have read permissions", t, func() {
|
||||
dir, err := ioutil.TempDir("", "copy-files-test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
filePath := path.Join(dir, "file.txt")
|
||||
err = ioutil.WriteFile(filePath, []byte("some dummy file content"), 0o644) //nolint: gosec
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = os.Chmod(filePath, 0o300)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = test.CopyFiles(dir, os.TempDir())
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user