0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2025-01-06 22:40:28 -05:00
zot/pkg/test/image-utils/upload_test.go
Andrei Aaron ba6f347d8d
refactor(pkg/test): split logic in pkg/test/common.go into multiple packages (#1861)
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>
2023-09-27 11:34:48 -07:00

548 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.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config"
tcommon "zotregistry.io/zot/pkg/test/common"
. "zotregistry.io/zot/pkg/test/image-utils"
"zotregistry.io/zot/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
err := os.Chmod(tempDir, 0o400)
if err != nil {
t.Fatal(err)
}
ctlr := api.NewController(conf)
ctlrManager := tcommon.NewControllerManager(ctlr)
ctlrManager.StartAndWait(port)
defer ctlrManager.StopServer()
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.GetCredString(user1, password1)
htpasswdPath := tcommon.MakeHtpasswdFileFromString(testString1)
defer os.Remove(htpasswdPath)
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.GetCredString(user, password)
htpasswdPath := tcommon.MakeHtpasswdFileFromString(testString)
defer os.Remove(htpasswdPath)
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)
}
})
})
}