github.com/khulnasoft-lab/khulnasoft@v26.0.1-0.20240328202558-330a6f959fe0+incompatible/internal/testutils/specialimage/multilayer.go (about)

     1  package specialimage
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  
    10  	"github.com/containerd/containerd/platforms"
    11  	"github.com/distribution/reference"
    12  	"github.com/docker/docker/pkg/archive"
    13  	"github.com/google/uuid"
    14  	"github.com/opencontainers/go-digest"
    15  	"github.com/opencontainers/image-spec/specs-go"
    16  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    17  )
    18  
    19  type SingleFileLayer struct {
    20  	Name    string
    21  	Content []byte
    22  }
    23  
    24  func MultiLayer(dir string) (*ocispec.Index, error) {
    25  	return MultiLayerCustom(dir, "multilayer:latest", []SingleFileLayer{
    26  		{Name: "foo", Content: []byte("1")},
    27  		{Name: "bar", Content: []byte("2")},
    28  		{Name: "hello", Content: []byte("world")},
    29  	})
    30  }
    31  
    32  func MultiLayerCustom(dir string, imageRef string, layers []SingleFileLayer) (*ocispec.Index, error) {
    33  	var layerDescs []ocispec.Descriptor
    34  	var layerDgsts []digest.Digest
    35  	var layerBlobs []string
    36  	for _, layer := range layers {
    37  		layerDesc, err := writeLayerWithOneFile(dir, layer.Name, layer.Content)
    38  		if err != nil {
    39  			return nil, err
    40  		}
    41  
    42  		layerDescs = append(layerDescs, layerDesc)
    43  		layerDgsts = append(layerDgsts, layerDesc.Digest)
    44  		layerBlobs = append(layerBlobs, blobPath(layerDesc))
    45  	}
    46  
    47  	configDesc, err := writeJsonBlob(dir, ocispec.MediaTypeImageConfig, ocispec.Image{
    48  		Platform: platforms.DefaultSpec(),
    49  		Config: ocispec.ImageConfig{
    50  			Env: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},
    51  		},
    52  		RootFS: ocispec.RootFS{
    53  			Type:    "layers",
    54  			DiffIDs: layerDgsts,
    55  		},
    56  	})
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  
    61  	manifest := ocispec.Manifest{
    62  		MediaType: ocispec.MediaTypeImageManifest,
    63  		Config:    configDesc,
    64  		Layers:    layerDescs,
    65  	}
    66  
    67  	legacyManifests := []manifestItem{
    68  		{
    69  			Config:   blobPath(configDesc),
    70  			RepoTags: []string{imageRef},
    71  			Layers:   layerBlobs,
    72  		},
    73  	}
    74  
    75  	ref, err := reference.ParseNormalizedNamed(imageRef)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	return singlePlatformImage(dir, ref, manifest, legacyManifests)
    80  }
    81  
    82  // Legacy manifest item (manifests.json)
    83  type manifestItem struct {
    84  	Config   string
    85  	RepoTags []string
    86  	Layers   []string
    87  }
    88  
    89  func singlePlatformImage(dir string, ref reference.Named, manifest ocispec.Manifest, legacyManifests []manifestItem) (*ocispec.Index, error) {
    90  	manifestDesc, err := writeJsonBlob(dir, ocispec.MediaTypeImageManifest, manifest)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	if ref != nil {
    96  		manifestDesc.Annotations = map[string]string{
    97  			"io.containerd.image.name": ref.String(),
    98  		}
    99  
   100  		if tagged, ok := ref.(reference.Tagged); ok {
   101  			manifestDesc.Annotations[ocispec.AnnotationRefName] = tagged.Tag()
   102  		}
   103  	}
   104  
   105  	idx := ocispec.Index{
   106  		Versioned: specs.Versioned{SchemaVersion: 2},
   107  		MediaType: ocispec.MediaTypeImageIndex,
   108  		Manifests: []ocispec.Descriptor{manifestDesc},
   109  	}
   110  	if err := writeJson(idx, filepath.Join(dir, "index.json")); err != nil {
   111  		return nil, err
   112  	}
   113  	if err := writeJson(legacyManifests, filepath.Join(dir, "manifest.json")); err != nil {
   114  		return nil, err
   115  	}
   116  
   117  	err = os.WriteFile(filepath.Join(dir, "oci-layout"), []byte(`{"imageLayoutVersion": "1.0.0"}`), 0o644)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  
   122  	return &idx, nil
   123  }
   124  
   125  func fileArchive(dir string, name string, content []byte) (io.ReadCloser, error) {
   126  	tmp, err := os.MkdirTemp("", "")
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  
   131  	if err := os.WriteFile(filepath.Join(tmp, name), content, 0o644); err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	return archive.Tar(tmp, archive.Uncompressed)
   136  }
   137  
   138  func writeLayerWithOneFile(dir string, filename string, content []byte) (ocispec.Descriptor, error) {
   139  	rd, err := fileArchive(dir, filename, content)
   140  	if err != nil {
   141  		return ocispec.Descriptor{}, err
   142  	}
   143  	defer rd.Close()
   144  
   145  	return writeBlob(dir, ocispec.MediaTypeImageLayer, rd)
   146  }
   147  
   148  func writeJsonBlob(dir string, mt string, obj any) (ocispec.Descriptor, error) {
   149  	b, err := json.Marshal(obj)
   150  	if err != nil {
   151  		return ocispec.Descriptor{}, err
   152  	}
   153  
   154  	return writeBlob(dir, mt, bytes.NewReader(b))
   155  }
   156  
   157  func writeJson(obj any, path string) error {
   158  	b, err := json.Marshal(obj)
   159  	if err != nil {
   160  		return err
   161  	}
   162  
   163  	return os.WriteFile(path, b, 0o644)
   164  }
   165  
   166  func writeBlob(dir string, mt string, rd io.Reader) (_ ocispec.Descriptor, outErr error) {
   167  	digester := digest.Canonical.Digester()
   168  	hashTee := io.TeeReader(rd, digester.Hash())
   169  
   170  	blobsPath := filepath.Join(dir, "blobs", "sha256")
   171  	if err := os.MkdirAll(blobsPath, 0o755); err != nil {
   172  		return ocispec.Descriptor{}, err
   173  	}
   174  
   175  	tmpPath := filepath.Join(blobsPath, uuid.New().String())
   176  	file, err := os.Create(tmpPath)
   177  	if err != nil {
   178  		return ocispec.Descriptor{}, err
   179  	}
   180  
   181  	defer func() {
   182  		if outErr != nil {
   183  			file.Close()
   184  			os.Remove(tmpPath)
   185  		}
   186  	}()
   187  
   188  	if _, err := io.Copy(file, hashTee); err != nil {
   189  		return ocispec.Descriptor{}, err
   190  	}
   191  
   192  	digest := digester.Digest()
   193  
   194  	stat, err := os.Stat(tmpPath)
   195  	if err != nil {
   196  		return ocispec.Descriptor{}, err
   197  	}
   198  
   199  	file.Close()
   200  	if err := os.Rename(tmpPath, filepath.Join(blobsPath, digest.Encoded())); err != nil {
   201  		return ocispec.Descriptor{}, err
   202  	}
   203  
   204  	return ocispec.Descriptor{
   205  		MediaType: mt,
   206  		Digest:    digest,
   207  		Size:      stat.Size(),
   208  	}, nil
   209  }
   210  
   211  func blobPath(desc ocispec.Descriptor) string {
   212  	return "blobs/sha256/" + desc.Digest.Encoded()
   213  }