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 }