Added storage interface

This commit is contained in:
Petu Eusebiu
2021-09-30 16:27:13 +03:00
committed by Ramkumar Chinchani
parent 20f4051446
commit 7d077eaf5a
12 changed files with 1754 additions and 1389 deletions
+17 -13
View File
@@ -157,48 +157,52 @@ func TestImageFormat(t *testing.T) {
Convey("Test valid image", t, func() {
log := log.NewLogger("debug", "")
dbDir := "../../../../test/data"
olu := common.NewOciLayoutUtils(log)
isValidImage, err := olu.IsValidImageFormat(path.Join(dbDir, "zot-test"))
defaultStore := storage.NewImageStore(dbDir, false, false, log)
storeController := storage.StoreController{DefaultStore: defaultStore}
olu := common.NewOciLayoutUtils(storeController, log)
isValidImage, err := olu.IsValidImageFormat("zot-test")
So(err, ShouldBeNil)
So(isValidImage, ShouldEqual, true)
isValidImage, err = olu.IsValidImageFormat(path.Join(dbDir, "zot-test:0.0.1"))
isValidImage, err = olu.IsValidImageFormat("zot-test:0.0.1")
So(err, ShouldBeNil)
So(isValidImage, ShouldEqual, true)
isValidImage, err = olu.IsValidImageFormat(path.Join(dbDir, "zot-test:0.0."))
isValidImage, err = olu.IsValidImageFormat("zot-test:0.0.")
So(err, ShouldBeNil)
So(isValidImage, ShouldEqual, false)
isValidImage, err = olu.IsValidImageFormat(path.Join(dbDir, "zot-noindex-test"))
isValidImage, err = olu.IsValidImageFormat("zot-noindex-test")
So(err, ShouldNotBeNil)
So(isValidImage, ShouldEqual, false)
isValidImage, err = olu.IsValidImageFormat(path.Join(dbDir, "zot--tet"))
isValidImage, err = olu.IsValidImageFormat("zot--tet")
So(err, ShouldNotBeNil)
So(isValidImage, ShouldEqual, false)
isValidImage, err = olu.IsValidImageFormat(path.Join(dbDir, "zot-noindex-test"))
isValidImage, err = olu.IsValidImageFormat("zot-noindex-test")
So(err, ShouldNotBeNil)
So(isValidImage, ShouldEqual, false)
isValidImage, err = olu.IsValidImageFormat(path.Join(dbDir, "zot-squashfs-noblobs"))
isValidImage, err = olu.IsValidImageFormat("zot-squashfs-noblobs")
So(err, ShouldNotBeNil)
So(isValidImage, ShouldEqual, false)
isValidImage, err = olu.IsValidImageFormat(path.Join(dbDir, "zot-squashfs-invalid-index"))
isValidImage, err = olu.IsValidImageFormat("zot-squashfs-invalid-index")
So(err, ShouldNotBeNil)
So(isValidImage, ShouldEqual, false)
isValidImage, err = olu.IsValidImageFormat(path.Join(dbDir, "zot-squashfs-invalid-blob"))
isValidImage, err = olu.IsValidImageFormat("zot-squashfs-invalid-blob")
So(err, ShouldNotBeNil)
So(isValidImage, ShouldEqual, false)
isValidImage, err = olu.IsValidImageFormat(path.Join(dbDir, "zot-squashfs-test:0.3.22-squashfs"))
isValidImage, err = olu.IsValidImageFormat("zot-squashfs-test:0.3.22-squashfs")
So(err, ShouldNotBeNil)
So(isValidImage, ShouldEqual, false)
isValidImage, err = olu.IsValidImageFormat(path.Join(dbDir, "zot-nonreadable-test"))
isValidImage, err = olu.IsValidImageFormat("zot-nonreadable-test")
So(err, ShouldNotBeNil)
So(isValidImage, ShouldEqual, false)
})
@@ -443,7 +447,7 @@ func TestUtilsMethod(t *testing.T) {
subStore := storage.NewImageStore(subRootDir, false, false, log)
subStoreMap := make(map[string]*storage.ImageStore)
subStoreMap := make(map[string]storage.ImageStore)
subStoreMap["/b"] = subStore
+22 -30
View File
@@ -3,14 +3,15 @@ package common
import (
"encoding/json"
"io/ioutil"
"os"
"path"
"strings"
"time"
goerrors "errors"
"github.com/anuvu/zot/errors"
"github.com/anuvu/zot/pkg/log"
"github.com/anuvu/zot/pkg/storage"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/types"
godigest "github.com/opencontainers/go-digest"
@@ -19,21 +20,22 @@ import (
// OciLayoutInfo ...
type OciLayoutUtils struct {
Log log.Logger
Log log.Logger
StoreController storage.StoreController
}
// NewOciLayoutUtils initializes a new OciLayoutUtils object.
func NewOciLayoutUtils(log log.Logger) *OciLayoutUtils {
return &OciLayoutUtils{Log: log}
func NewOciLayoutUtils(storeController storage.StoreController, log log.Logger) *OciLayoutUtils {
return &OciLayoutUtils{Log: log, StoreController: storeController}
}
// Below method will return image path including root dir, root dir is determined by splitting.
func (olu OciLayoutUtils) GetImageManifests(imagePath string) ([]ispec.Descriptor, error) {
buf, err := ioutil.ReadFile(path.Join(imagePath, "index.json"))
func (olu OciLayoutUtils) GetImageManifests(image string) ([]ispec.Descriptor, error) {
imageStore := olu.StoreController.GetImageStore(image)
buf, err := imageStore.GetIndexContent(image)
if err != nil {
if os.IsNotExist(err) {
if goerrors.Is(errors.ErrRepoNotFound, err) {
olu.Log.Error().Err(err).Msg("index.json doesn't exist")
return nil, errors.ErrRepoNotFound
@@ -47,17 +49,20 @@ func (olu OciLayoutUtils) GetImageManifests(imagePath string) ([]ispec.Descripto
var index ispec.Index
if err := json.Unmarshal(buf, &index); err != nil {
olu.Log.Error().Err(err).Str("dir", imagePath).Msg("invalid JSON")
olu.Log.Error().Err(err).Str("dir", path.Join(imageStore.RootDir(), image)).Msg("invalid JSON")
return nil, errors.ErrRepoNotFound
}
return index.Manifests, nil
}
//nolint: interfacer
func (olu OciLayoutUtils) GetImageBlobManifest(imageDir string, digest godigest.Digest) (v1.Manifest, error) {
var blobIndex v1.Manifest
blobBuf, err := ioutil.ReadFile(path.Join(imageDir, "blobs", digest.Algorithm().String(), digest.Encoded()))
imageStore := olu.StoreController.GetImageStore(imageDir)
blobBuf, err := imageStore.GetBlobContent(imageDir, digest.String())
if err != nil {
olu.Log.Error().Err(err).Msg("unable to open image metadata file")
@@ -73,10 +78,13 @@ func (olu OciLayoutUtils) GetImageBlobManifest(imageDir string, digest godigest.
return blobIndex, nil
}
//nolint: interfacer
func (olu OciLayoutUtils) GetImageInfo(imageDir string, hash v1.Hash) (ispec.Image, error) {
var imageInfo ispec.Image
blobBuf, err := ioutil.ReadFile(path.Join(imageDir, "blobs", hash.Algorithm, hash.Hex))
imageStore := olu.StoreController.GetImageStore(imageDir)
blobBuf, err := imageStore.GetBlobContent(imageDir, hash.String())
if err != nil {
olu.Log.Error().Err(err).Msg("unable to open image layers file")
@@ -92,17 +100,10 @@ func (olu OciLayoutUtils) GetImageInfo(imageDir string, hash v1.Hash) (ispec.Ima
return imageInfo, err
}
func (olu OciLayoutUtils) IsValidImageFormat(imagePath string) (bool, error) {
imageDir, inputTag := GetImageDirAndTag(imagePath)
if !DirExists(imageDir) {
olu.Log.Error().Msg("image directory doesn't exist")
return false, errors.ErrRepoNotFound
}
func (olu OciLayoutUtils) IsValidImageFormat(image string) (bool, error) {
imageDir, inputTag := GetImageDirAndTag(image)
manifests, err := olu.GetImageManifests(imageDir)
if err != nil {
return false, err
}
@@ -181,15 +182,6 @@ func (olu OciLayoutUtils) GetImageTagsWithTimestamp(repo string) ([]TagInfo, err
return tagsInfo, nil
}
func DirExists(d string) bool {
fi, err := os.Stat(d)
if err != nil && os.IsNotExist(err) {
return false
}
return fi.IsDir()
}
func GetImageDirAndTag(imageName string) (string, string) {
var imageDir string
+3 -3
View File
@@ -40,7 +40,7 @@ func ScanImage(config *config.Config) (report.Results, error) {
func GetCVEInfo(storeController storage.StoreController, log log.Logger) (*CveInfo, error) {
cveController := CveTrivyController{}
layoutUtils := common.NewOciLayoutUtils(log)
layoutUtils := common.NewOciLayoutUtils(storeController, log)
subCveConfig := make(map[string]*config.Config)
@@ -118,7 +118,7 @@ func (cveinfo CveInfo) GetTrivyConfig(image string) *config.Config {
return trivyConfig
}
func (cveinfo CveInfo) GetImageListForCVE(repo string, id string, imgStore *storage.ImageStore,
func (cveinfo CveInfo) GetImageListForCVE(repo string, id string, imgStore storage.ImageStore,
trivyConfig *config.Config) ([]*string, error) {
tags := make([]*string, 0)
@@ -134,7 +134,7 @@ func (cveinfo CveInfo) GetImageListForCVE(repo string, id string, imgStore *stor
for _, tag := range tagList {
trivyConfig.TrivyConfig.Input = fmt.Sprintf("%s:%s", path.Join(rootDir, repo), tag)
isValidImage, _ := cveinfo.LayoutUtils.IsValidImageFormat(trivyConfig.TrivyConfig.Input)
isValidImage, _ := cveinfo.LayoutUtils.IsValidImageFormat(fmt.Sprintf("%s:%s", repo, tag))
if !isValidImage {
cveinfo.Log.Debug().Str("image", repo+":"+tag).Msg("image media type not supported for scanning")
+2 -2
View File
@@ -96,7 +96,7 @@ func testSetup() error {
storeController := storage.StoreController{DefaultStore: storage.NewImageStore(dir, false, false, log)}
layoutUtils := common.NewOciLayoutUtils(log)
layoutUtils := common.NewOciLayoutUtils(storeController, log)
cve = &cveinfo.CveInfo{Log: log, StoreController: storeController, LayoutUtils: layoutUtils}
@@ -421,7 +421,7 @@ func TestMultipleStoragePath(t *testing.T) {
storeController.DefaultStore = firstStore
subStore := make(map[string]*storage.ImageStore)
subStore := make(map[string]storage.ImageStore)
subStore["/a"] = secondStore
subStore["/b"] = thirdStore
+3 -7
View File
@@ -3,9 +3,9 @@ package digestinfo
import (
"strings"
"github.com/anuvu/zot/errors"
"github.com/anuvu/zot/pkg/extensions/search/common"
"github.com/anuvu/zot/pkg/log"
"github.com/anuvu/zot/pkg/storage"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
)
@@ -16,8 +16,8 @@ type DigestInfo struct {
}
// NewDigestInfo initializes a new DigestInfo object.
func NewDigestInfo(log log.Logger) *DigestInfo {
layoutUtils := common.NewOciLayoutUtils(log)
func NewDigestInfo(storeController storage.StoreController, log log.Logger) *DigestInfo {
layoutUtils := common.NewOciLayoutUtils(storeController, log)
return &DigestInfo{Log: log, LayoutUtils: layoutUtils}
}
@@ -26,10 +26,6 @@ func NewDigestInfo(log log.Logger) *DigestInfo {
func (digestinfo DigestInfo) GetImageTagsByDigest(repo string, digest string) ([]*string, error) {
uniqueTags := []*string{}
if !common.DirExists(repo) {
return nil, errors.ErrRepoNotFound
}
manifests, err := digestinfo.LayoutUtils.GetImageManifests(repo)
if err != nil {
+96 -6
View File
@@ -15,6 +15,7 @@ import (
ext "github.com/anuvu/zot/pkg/extensions"
digestinfo "github.com/anuvu/zot/pkg/extensions/search/digest"
"github.com/anuvu/zot/pkg/log"
"github.com/anuvu/zot/pkg/storage"
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/resty.v1"
)
@@ -23,6 +24,7 @@ import (
var (
digestInfo *digestinfo.DigestInfo
rootDir string
subRootDir string
)
const (
@@ -62,8 +64,15 @@ func testSetup() error {
return err
}
subDir, err := ioutil.TempDir("", "sub_digest_test")
if err != nil {
return err
}
rootDir = dir
subRootDir = subDir
// Test images used/copied:
// IMAGE NAME TAG DIGEST CONFIG LAYERS SIZE
// zot-test 0.0.1 2bacca16 adf3bb6c 76MB
@@ -71,14 +80,26 @@ func testSetup() error {
// zot-cve-test 0.0.1 63a795ca 8dd57e17 75MB
// 7a0437f0 75MB
err = os.Mkdir(subDir+"/a", 0700)
if err != nil {
return err
}
err = copyFiles("../../../../test/data", rootDir)
if err != nil {
return err
}
err = copyFiles("../../../../test/data", subDir+"/a/")
if err != nil {
return err
}
log := log.NewLogger("debug", "")
digestInfo = digestinfo.NewDigestInfo(log)
storeController := storage.StoreController{DefaultStore: storage.NewImageStore(rootDir, false, false, log)}
digestInfo = digestinfo.NewDigestInfo(storeController, log)
return nil
}
@@ -131,30 +152,30 @@ func copyFiles(sourceDir string, destDir string) error {
func TestDigestInfo(t *testing.T) {
Convey("Test image tag", t, func() {
// Search by manifest digest
imageTags, err := digestInfo.GetImageTagsByDigest(path.Join(rootDir, "zot-cve-test"), "63a795ca")
imageTags, err := digestInfo.GetImageTagsByDigest("zot-cve-test", "63a795ca")
So(err, ShouldBeNil)
So(len(imageTags), ShouldEqual, 1)
So(*imageTags[0], ShouldEqual, "0.0.1")
// Search by config digest
imageTags, err = digestInfo.GetImageTagsByDigest(path.Join(rootDir, "zot-test"), "adf3bb6c")
imageTags, err = digestInfo.GetImageTagsByDigest("zot-test", "adf3bb6c")
So(err, ShouldBeNil)
So(len(imageTags), ShouldEqual, 1)
So(*imageTags[0], ShouldEqual, "0.0.1")
// Search by layer digest
imageTags, err = digestInfo.GetImageTagsByDigest(path.Join(rootDir, "zot-cve-test"), "7a0437f0")
imageTags, err = digestInfo.GetImageTagsByDigest("zot-cve-test", "7a0437f0")
So(err, ShouldBeNil)
So(len(imageTags), ShouldEqual, 1)
So(*imageTags[0], ShouldEqual, "0.0.1")
// Search by non-existent image
imageTags, err = digestInfo.GetImageTagsByDigest(path.Join(rootDir, "zot-tes"), "63a795ca")
imageTags, err = digestInfo.GetImageTagsByDigest("zot-tes", "63a795ca")
So(err, ShouldNotBeNil)
So(len(imageTags), ShouldEqual, 0)
// Search by non-existent digest
imageTags, err = digestInfo.GetImageTagsByDigest(path.Join(rootDir, "zot-test"), "111")
imageTags, err = digestInfo.GetImageTagsByDigest("zot-test", "111")
So(err, ShouldBeNil)
So(len(imageTags), ShouldEqual, 0)
})
@@ -286,6 +307,75 @@ func TestDigestSearchHTTP(t *testing.T) {
})
}
func TestDigestSearchHTTPSubPaths(t *testing.T) {
Convey("Test image search by digest scanning using storage subpaths", t, func() {
config := api.NewConfig()
config.HTTP.Port = Port1
config.Extensions = &ext.ExtensionConfig{
Search: &ext.SearchConfig{Enable: true},
}
c := api.NewController(config)
globalDir, err := ioutil.TempDir("", "digest_test")
if err != nil {
panic(err)
}
defer os.RemoveAll(globalDir)
c.Config.Storage.RootDirectory = globalDir
subPathMap := make(map[string]api.StorageConfig)
subPathMap["/a"] = api.StorageConfig{RootDirectory: subRootDir}
c.Config.Storage.SubPaths = subPathMap
go func() {
// this blocks
if err := c.Run(); err != nil {
return
}
}()
// wait till ready
for {
_, err := resty.R().Get(BaseURL1)
if err == nil {
break
}
time.Sleep(100 * time.Millisecond)
}
// shut down server
defer func() {
ctx := context.Background()
_ = c.Server.Shutdown(ctx)
}()
resp, err := resty.R().Get(BaseURL1 + "/v2/")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, err = resty.R().Get(BaseURL1 + "/query")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, err = resty.R().Get(BaseURL1 + "/query?query={ImageListForDigest(id:\"sha\"){Name%20Tags}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
var responseStruct ImgResponseForDigest
err = json.Unmarshal(resp.Body(), &responseStruct)
So(err, ShouldBeNil)
So(len(responseStruct.Errors), ShouldEqual, 0)
So(len(responseStruct.ImgListForDigest.Images), ShouldEqual, 2)
})
}
func TestDigestSearchDisabled(t *testing.T) {
Convey("Test disabling image search", t, func() {
dir, err := ioutil.TempDir("", "digest_test")
+14 -21
View File
@@ -5,7 +5,6 @@ package search
import (
"context"
"fmt"
"path"
"strconv"
"strings"
@@ -54,7 +53,7 @@ func GetResolverConfig(log log.Logger, storeController storage.StoreController,
}
}
digestInfo := digestinfo.NewDigestInfo(log)
digestInfo := digestinfo.NewDigestInfo(storeController, log)
resConfig := &Resolver{cveInfo: cveInfo, storeController: storeController, digestInfo: digestInfo, log: log}
@@ -69,7 +68,7 @@ func (r *queryResolver) CVEListForImage(ctx context.Context, image string) (*CVE
r.log.Info().Str("image", image).Msg("scanning image")
isValidImage, err := r.cveInfo.LayoutUtils.IsValidImageFormat(trivyConfig.TrivyConfig.Input)
isValidImage, err := r.cveInfo.LayoutUtils.IsValidImageFormat(image)
if !isValidImage {
r.log.Debug().Str("image", image).Msg("image media type not supported for scanning")
@@ -201,7 +200,7 @@ func (r *queryResolver) ImageListForCve(ctx context.Context, id string) ([]*ImgR
return finalCveResult, nil
}
func (r *queryResolver) getImageListForCVE(repoList []string, id string, imgStore *storage.ImageStore,
func (r *queryResolver) getImageListForCVE(repoList []string, id string, imgStore storage.ImageStore,
trivyConfig *config.Config) ([]*ImgResultForCve, error) {
cveResult := []*ImgResultForCve{}
@@ -238,7 +237,7 @@ func (r *queryResolver) ImageListWithCVEFixed(ctx context.Context, id string, im
r.log.Info().Str("image", image).Msg("extracting list of tags available in image")
tagsInfo, err := r.cveInfo.LayoutUtils.GetImageTagsWithTimestamp(imagePath)
tagsInfo, err := r.cveInfo.LayoutUtils.GetImageTagsWithTimestamp(image)
if err != nil {
r.log.Error().Err(err).Msg("unable to read image tags")
@@ -252,7 +251,7 @@ func (r *queryResolver) ImageListWithCVEFixed(ctx context.Context, id string, im
for _, tag := range tagsInfo {
trivyConfig.TrivyConfig.Input = fmt.Sprintf("%s:%s", imagePath, tag.Name)
isValidImage, _ := r.cveInfo.LayoutUtils.IsValidImageFormat(trivyConfig.TrivyConfig.Input)
isValidImage, _ := r.cveInfo.LayoutUtils.IsValidImageFormat(fmt.Sprintf("%s:%s", image, tag.Name))
if !isValidImage {
r.log.Debug().Str("image",
fmt.Sprintf("%s:%s", image, tag.Name)).
@@ -325,9 +324,7 @@ func (r *queryResolver) ImageListForDigest(ctx context.Context, id string) ([]*I
r.log.Info().Msg("scanning each global repository")
rootDir := defaultStore.RootDir()
partialImgResultForDigest, err := r.getImageListForDigest(rootDir, repoList, id)
partialImgResultForDigest, err := r.getImageListForDigest(repoList, id)
if err != nil {
r.log.Error().Err(err).Msg("unable to get image and tag list for global repositories")
@@ -338,8 +335,6 @@ func (r *queryResolver) ImageListForDigest(ctx context.Context, id string) ([]*I
subStore := r.storeController.SubStore
for _, store := range subStore {
rootDir := store.RootDir()
subRepoList, err := store.GetRepositories()
if err != nil {
r.log.Error().Err(err).Msg("unable to search sub-repositories")
@@ -347,7 +342,7 @@ func (r *queryResolver) ImageListForDigest(ctx context.Context, id string) ([]*I
return imgResultForDigest, err
}
partialImgResultForDigest, err = r.getImageListForDigest(rootDir, subRepoList, id)
partialImgResultForDigest, err = r.getImageListForDigest(subRepoList, id)
if err != nil {
r.log.Error().Err(err).Msg("unable to get image and tag list for sub-repositories")
@@ -360,7 +355,7 @@ func (r *queryResolver) ImageListForDigest(ctx context.Context, id string) ([]*I
return imgResultForDigest, nil
}
func (r *queryResolver) getImageListForDigest(rootDir string, repoList []string,
func (r *queryResolver) getImageListForDigest(repoList []string,
digest string) ([]*ImgResultForDigest, error) {
imgResultForDigest := []*ImgResultForDigest{}
@@ -369,7 +364,7 @@ func (r *queryResolver) getImageListForDigest(rootDir string, repoList []string,
for _, repo := range repoList {
r.log.Info().Str("repo", repo).Msg("filtering list of tags in image repo by digest")
tags, err := r.digestInfo.GetImageTagsByDigest(path.Join(rootDir, repo), digest)
tags, err := r.digestInfo.GetImageTagsByDigest(repo, digest)
if err != nil {
r.log.Error().Err(err).Msg("unable to get filtered list of image tags")
@@ -424,7 +419,7 @@ func (r *queryResolver) ImageListWithLatestTag(ctx context.Context) ([]*ImageInf
return imageList, nil
}
func (r *queryResolver) getImageListWithLatestTag(store *storage.ImageStore) ([]*ImageInfo, error) {
func (r *queryResolver) getImageListWithLatestTag(store storage.ImageStore) ([]*ImageInfo, error) {
results := make([]*ImageInfo, 0)
repoList, err := store.GetRepositories()
@@ -438,12 +433,10 @@ func (r *queryResolver) getImageListWithLatestTag(store *storage.ImageStore) ([]
r.log.Info().Msg("no repositories found")
}
dir := store.RootDir()
layoutUtils := common.NewOciLayoutUtils(r.log)
layoutUtils := common.NewOciLayoutUtils(r.storeController, r.log)
for _, repo := range repoList {
tagsInfo, err := layoutUtils.GetImageTagsWithTimestamp(path.Join(dir, repo))
tagsInfo, err := layoutUtils.GetImageTagsWithTimestamp(repo)
if err != nil {
r.log.Error().Err(err).Msg("extension api: error getting tag timestamp info")
@@ -460,7 +453,7 @@ func (r *queryResolver) getImageListWithLatestTag(store *storage.ImageStore) ([]
digest := godigest.Digest(latestTag.Digest)
manifest, err := layoutUtils.GetImageBlobManifest(path.Join(dir, repo), digest)
manifest, err := layoutUtils.GetImageBlobManifest(repo, digest)
if err != nil {
r.log.Error().Err(err).Msg("extension api: error reading manifest")
@@ -471,7 +464,7 @@ func (r *queryResolver) getImageListWithLatestTag(store *storage.ImageStore) ([]
name := repo
imageConfig, err := layoutUtils.GetImageInfo(path.Join(dir, repo), manifest.Config.Digest)
imageConfig, err := layoutUtils.GetImageInfo(repo, manifest.Config.Digest)
if err != nil {
r.log.Error().Err(err).Msg("extension api: error reading image config")