mirror of
https://github.com/project-zot/zot.git
synced 2026-06-17 21:17:58 +08:00
9dfa7c3ae6
Replace MakeTempFile usage with MakeTempFilePath and MakeTempFileWithContent helpers that automatically handle file lifecycle. This prevents resource leaks by ensuring temporary files are properly closed. Shoudld also make the tests easier to read. Signed-off-by: Andrei Aaron <andreifdaaron@gmail.com>
559 lines
13 KiB
Go
559 lines
13 KiB
Go
package image_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path"
|
|
"testing"
|
|
|
|
godigest "github.com/opencontainers/go-digest"
|
|
"github.com/opencontainers/image-spec/specs-go"
|
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
|
|
"zotregistry.dev/zot/v2/pkg/api"
|
|
"zotregistry.dev/zot/v2/pkg/api/config"
|
|
tcommon "zotregistry.dev/zot/v2/pkg/test/common"
|
|
. "zotregistry.dev/zot/v2/pkg/test/image-utils"
|
|
"zotregistry.dev/zot/v2/pkg/test/inject"
|
|
)
|
|
|
|
func TestUploadImage(t *testing.T) {
|
|
Convey("Manifest without schemaVersion should fail validation", t, func() {
|
|
port := tcommon.GetFreePort()
|
|
baseURL := tcommon.GetBaseURL(port)
|
|
|
|
conf := config.New()
|
|
conf.HTTP.Port = port
|
|
conf.Storage.RootDirectory = t.TempDir()
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
ctlrManager := tcommon.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
layerBlob := []byte("test")
|
|
|
|
img := Image{
|
|
Layers: [][]byte{
|
|
layerBlob,
|
|
},
|
|
Manifest: ispec.Manifest{
|
|
Layers: []ispec.Descriptor{
|
|
{
|
|
Digest: godigest.FromBytes(layerBlob),
|
|
Size: int64(len(layerBlob)),
|
|
MediaType: ispec.MediaTypeImageLayerGzip,
|
|
},
|
|
},
|
|
Config: ispec.DescriptorEmptyJSON,
|
|
},
|
|
Config: ispec.Image{},
|
|
}
|
|
|
|
err := UploadImage(img, baseURL, "test", img.DigestStr())
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("Post request results in an error", t, func() {
|
|
port := tcommon.GetFreePort()
|
|
baseURL := tcommon.GetBaseURL(port)
|
|
|
|
conf := config.New()
|
|
conf.HTTP.Port = port
|
|
conf.Storage.RootDirectory = t.TempDir()
|
|
|
|
img := Image{
|
|
Layers: make([][]byte, 10),
|
|
}
|
|
|
|
err := UploadImage(img, baseURL, "test", "")
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("Post request status differs from accepted", t, func() {
|
|
port := tcommon.GetFreePort()
|
|
baseURL := tcommon.GetBaseURL(port)
|
|
|
|
tempDir := t.TempDir()
|
|
conf := config.New()
|
|
conf.HTTP.Port = port
|
|
conf.Storage.RootDirectory = tempDir
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
ctlrManager := tcommon.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
err := os.Chmod(tempDir, 0o400)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
defer func() {
|
|
err := os.Chmod(tempDir, 0o700)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}()
|
|
|
|
img := Image{
|
|
Layers: make([][]byte, 10),
|
|
}
|
|
|
|
err = UploadImage(img, baseURL, "test", "")
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("Put request results in an error", t, func() {
|
|
port := tcommon.GetFreePort()
|
|
baseURL := tcommon.GetBaseURL(port)
|
|
|
|
conf := config.New()
|
|
conf.HTTP.Port = port
|
|
conf.Storage.RootDirectory = t.TempDir()
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
ctlrManager := tcommon.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
img := Image{
|
|
Layers: make([][]byte, 10), // invalid format that will result in an error
|
|
Config: ispec.Image{},
|
|
}
|
|
|
|
err := UploadImage(img, baseURL, "test", "")
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("Image uploaded successfully", t, func() {
|
|
port := tcommon.GetFreePort()
|
|
baseURL := tcommon.GetBaseURL(port)
|
|
|
|
conf := config.New()
|
|
conf.HTTP.Port = port
|
|
conf.Storage.RootDirectory = t.TempDir()
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
ctlrManager := tcommon.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
layerBlob := []byte("test")
|
|
|
|
img := Image{
|
|
Layers: [][]byte{
|
|
layerBlob,
|
|
},
|
|
Manifest: ispec.Manifest{
|
|
Versioned: specs.Versioned{
|
|
SchemaVersion: 2,
|
|
},
|
|
Layers: []ispec.Descriptor{
|
|
{
|
|
Digest: godigest.FromBytes(layerBlob),
|
|
Size: int64(len(layerBlob)),
|
|
MediaType: ispec.MediaTypeImageLayerGzip,
|
|
},
|
|
},
|
|
Config: ispec.DescriptorEmptyJSON,
|
|
},
|
|
Config: ispec.Image{},
|
|
}
|
|
|
|
err := UploadImage(img, baseURL, "test", img.DigestStr())
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("Upload image with authentification", t, func() {
|
|
tempDir := t.TempDir()
|
|
conf := config.New()
|
|
port := tcommon.GetFreePort()
|
|
baseURL := tcommon.GetBaseURL(port)
|
|
|
|
user1 := "test"
|
|
password1 := "test"
|
|
testString1 := tcommon.GetBcryptCredString(user1, password1)
|
|
|
|
htpasswdPath := tcommon.MakeHtpasswdFileFromString(t, testString1)
|
|
conf.HTTP.Auth = &config.AuthConfig{
|
|
HTPasswd: config.AuthHTPasswd{
|
|
Path: htpasswdPath,
|
|
},
|
|
}
|
|
|
|
conf.HTTP.Port = port
|
|
|
|
conf.HTTP.AccessControl = &config.AccessControlConfig{
|
|
Repositories: config.Repositories{
|
|
"repo": config.PolicyGroup{
|
|
Policies: []config.Policy{
|
|
{
|
|
Users: []string{user1},
|
|
Actions: []string{"read", "create"},
|
|
},
|
|
},
|
|
DefaultPolicy: []string{},
|
|
},
|
|
"inaccessibleRepo": config.PolicyGroup{
|
|
Policies: []config.Policy{
|
|
{
|
|
Users: []string{user1},
|
|
Actions: []string{"create"},
|
|
},
|
|
},
|
|
DefaultPolicy: []string{},
|
|
},
|
|
},
|
|
AdminPolicy: config.Policy{
|
|
Users: []string{},
|
|
Actions: []string{},
|
|
},
|
|
}
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
ctlr.Config.Storage.RootDirectory = tempDir
|
|
|
|
ctlrManager := tcommon.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
Convey("Request fail while pushing layer", func() {
|
|
err := UploadImageWithBasicAuth(Image{Layers: [][]byte{{1, 2, 3}}}, "badURL", "", "", "", "")
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
Convey("Request status is not StatusOk while pushing layer", func() {
|
|
err := UploadImageWithBasicAuth(Image{Layers: [][]byte{{1, 2, 3}}}, baseURL, "", "repo", "", "")
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
Convey("Request fail while pushing config", func() {
|
|
err := UploadImageWithBasicAuth(Image{}, "badURL", "", "", "", "")
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
Convey("Request status is not StatusOk while pushing config", func() {
|
|
err := UploadImageWithBasicAuth(Image{}, baseURL, "", "repo", "", "")
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
})
|
|
|
|
Convey("Blob upload wrong response status code", t, func() {
|
|
port := tcommon.GetFreePort()
|
|
baseURL := tcommon.GetBaseURL(port)
|
|
|
|
tempDir := t.TempDir()
|
|
conf := config.New()
|
|
conf.HTTP.Port = port
|
|
conf.Storage.RootDirectory = tempDir
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
ctlrManager := tcommon.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
layerBlob := []byte("test")
|
|
layerBlobDigest := godigest.FromBytes(layerBlob)
|
|
layerPath := path.Join(tempDir, "test", "blobs", "sha256")
|
|
|
|
if _, err := os.Stat(layerPath); os.IsNotExist(err) {
|
|
err = os.MkdirAll(layerPath, 0o700)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
file, err := os.Create(path.Join(layerPath, layerBlobDigest.Encoded()))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = os.Chmod(layerPath, 0o000)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
defer func() {
|
|
err = os.Chmod(layerPath, 0o700)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
os.RemoveAll(file.Name())
|
|
}()
|
|
}
|
|
|
|
img := Image{
|
|
Layers: [][]byte{
|
|
layerBlob,
|
|
}, // invalid format that will result in an error
|
|
Config: ispec.Image{},
|
|
}
|
|
|
|
err := UploadImage(img, baseURL, "test", "")
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("CreateBlobUpload wrong response status code", t, func() {
|
|
port := tcommon.GetFreePort()
|
|
baseURL := tcommon.GetBaseURL(port)
|
|
|
|
tempDir := t.TempDir()
|
|
conf := config.New()
|
|
conf.HTTP.Port = port
|
|
conf.Storage.RootDirectory = tempDir
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
ctlrManager := tcommon.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
layerBlob := []byte("test")
|
|
|
|
img := Image{
|
|
Layers: [][]byte{
|
|
layerBlob,
|
|
}, // invalid format that will result in an error
|
|
Config: ispec.Image{},
|
|
}
|
|
|
|
Convey("CreateBlobUpload", func() {
|
|
injected := inject.InjectFailure(2)
|
|
if injected {
|
|
err := UploadImage(img, baseURL, "test", img.DigestStr())
|
|
So(err, ShouldNotBeNil)
|
|
}
|
|
})
|
|
Convey("UpdateBlobUpload", func() {
|
|
injected := inject.InjectFailure(4)
|
|
if injected {
|
|
err := UploadImage(img, baseURL, "test", img.DigestStr())
|
|
So(err, ShouldNotBeNil)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestInjectUploadImage(t *testing.T) {
|
|
Convey("Inject failures for unreachable lines", t, func() {
|
|
port := tcommon.GetFreePort()
|
|
baseURL := tcommon.GetBaseURL(port)
|
|
|
|
tempDir := t.TempDir()
|
|
conf := config.New()
|
|
conf.HTTP.Port = port
|
|
conf.Storage.RootDirectory = tempDir
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
ctlrManager := tcommon.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
layerBlob := []byte("test")
|
|
layerPath := path.Join(tempDir, "test", ".uploads")
|
|
|
|
if _, err := os.Stat(layerPath); os.IsNotExist(err) {
|
|
err = os.MkdirAll(layerPath, 0o700)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
img := Image{
|
|
Layers: [][]byte{
|
|
layerBlob,
|
|
}, // invalid format that will result in an error
|
|
Config: ispec.Image{},
|
|
}
|
|
|
|
Convey("first marshal", func() {
|
|
injected := inject.InjectFailure(0)
|
|
if injected {
|
|
err := UploadImage(img, baseURL, "test", img.DigestStr())
|
|
So(err, ShouldNotBeNil)
|
|
}
|
|
})
|
|
Convey("CreateBlobUpload POST call", func() {
|
|
injected := inject.InjectFailure(1)
|
|
if injected {
|
|
err := UploadImage(img, baseURL, "test", img.DigestStr())
|
|
So(err, ShouldNotBeNil)
|
|
}
|
|
})
|
|
Convey("UpdateBlobUpload PUT call", func() {
|
|
injected := inject.InjectFailure(3)
|
|
if injected {
|
|
err := UploadImage(img, baseURL, "test", img.DigestStr())
|
|
So(err, ShouldNotBeNil)
|
|
}
|
|
})
|
|
Convey("second marshal", func() {
|
|
injected := inject.InjectFailure(5)
|
|
if injected {
|
|
err := UploadImage(img, baseURL, "test", img.DigestStr())
|
|
So(err, ShouldNotBeNil)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestUploadMultiarchImage(t *testing.T) {
|
|
Convey("make controller", t, func() {
|
|
port := tcommon.GetFreePort()
|
|
baseURL := tcommon.GetBaseURL(port)
|
|
|
|
conf := config.New()
|
|
conf.HTTP.Port = port
|
|
conf.Storage.RootDirectory = t.TempDir()
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
ctlrManager := tcommon.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
layerBlob := []byte("test")
|
|
|
|
img := Image{
|
|
Layers: [][]byte{
|
|
layerBlob,
|
|
},
|
|
Manifest: ispec.Manifest{
|
|
Versioned: specs.Versioned{
|
|
SchemaVersion: 2,
|
|
},
|
|
Layers: []ispec.Descriptor{
|
|
{
|
|
Digest: godigest.FromBytes(layerBlob),
|
|
Size: int64(len(layerBlob)),
|
|
MediaType: ispec.MediaTypeImageLayerGzip,
|
|
},
|
|
},
|
|
Config: ispec.DescriptorEmptyJSON,
|
|
},
|
|
Config: ispec.Image{},
|
|
}
|
|
|
|
manifestBuf, err := json.Marshal(img.Manifest)
|
|
So(err, ShouldBeNil)
|
|
|
|
Convey("Multiarch image uploaded successfully", func() {
|
|
err = UploadMultiarchImage(MultiarchImage{
|
|
Index: ispec.Index{
|
|
Versioned: specs.Versioned{
|
|
SchemaVersion: 2,
|
|
},
|
|
MediaType: ispec.MediaTypeImageIndex,
|
|
Manifests: []ispec.Descriptor{
|
|
{
|
|
MediaType: ispec.MediaTypeImageManifest,
|
|
Digest: godigest.FromBytes(manifestBuf),
|
|
Size: int64(len(manifestBuf)),
|
|
},
|
|
},
|
|
},
|
|
Images: []Image{img},
|
|
}, baseURL, "test", "index")
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("Multiarch image without schemaVersion should fail validation", func() {
|
|
err = UploadMultiarchImage(MultiarchImage{
|
|
Index: ispec.Index{
|
|
MediaType: ispec.MediaTypeImageIndex,
|
|
Manifests: []ispec.Descriptor{
|
|
{
|
|
MediaType: ispec.MediaTypeImageManifest,
|
|
Digest: godigest.FromBytes(manifestBuf),
|
|
Size: int64(len(manifestBuf)),
|
|
},
|
|
},
|
|
},
|
|
Images: []Image{img},
|
|
}, baseURL, "test", "index")
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestInjectUploadImageWithBasicAuth(t *testing.T) {
|
|
Convey("Inject failures for unreachable lines", t, func() {
|
|
port := tcommon.GetFreePort()
|
|
baseURL := tcommon.GetBaseURL(port)
|
|
|
|
tempDir := t.TempDir()
|
|
conf := config.New()
|
|
conf.HTTP.Port = port
|
|
conf.Storage.RootDirectory = tempDir
|
|
|
|
user := "user"
|
|
password := "password"
|
|
testString := tcommon.GetBcryptCredString(user, password)
|
|
|
|
htpasswdPath := tcommon.MakeHtpasswdFileFromString(t, testString)
|
|
conf.HTTP.Auth = &config.AuthConfig{
|
|
HTPasswd: config.AuthHTPasswd{
|
|
Path: htpasswdPath,
|
|
},
|
|
}
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
ctlrManager := tcommon.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
layerBlob := []byte("test")
|
|
layerPath := path.Join(tempDir, "test", ".uploads")
|
|
|
|
if _, err := os.Stat(layerPath); os.IsNotExist(err) {
|
|
err = os.MkdirAll(layerPath, 0o700)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
img := Image{
|
|
Layers: [][]byte{
|
|
layerBlob,
|
|
}, // invalid format that will result in an error
|
|
Config: ispec.Image{},
|
|
}
|
|
|
|
Convey("first marshal", func() {
|
|
injected := inject.InjectFailure(0)
|
|
if injected {
|
|
err := UploadImageWithBasicAuth(img, baseURL, "test", img.DigestStr(), "user", "password")
|
|
So(err, ShouldNotBeNil)
|
|
}
|
|
})
|
|
Convey("CreateBlobUpload POST call", func() {
|
|
injected := inject.InjectFailure(1)
|
|
if injected {
|
|
err := UploadImageWithBasicAuth(img, baseURL, "test", img.DigestStr(), "user", "password")
|
|
So(err, ShouldNotBeNil)
|
|
}
|
|
})
|
|
Convey("UpdateBlobUpload PUT call", func() {
|
|
injected := inject.InjectFailure(3)
|
|
if injected {
|
|
err := UploadImageWithBasicAuth(img, baseURL, "test", img.DigestStr(), "user", "password")
|
|
So(err, ShouldNotBeNil)
|
|
}
|
|
})
|
|
Convey("second marshal", func() {
|
|
injected := inject.InjectFailure(5)
|
|
if injected {
|
|
err := UploadImageWithBasicAuth(img, baseURL, "test", img.DigestStr(), "user", "password")
|
|
So(err, ShouldNotBeNil)
|
|
}
|
|
})
|
|
})
|
|
}
|