3333package integration
3434
3535import (
36+ "archive/tar"
3637 "bytes"
3738 "crypto/rand"
3839 "crypto/rsa"
@@ -43,8 +44,10 @@ import (
4344 "encoding/pem"
4445 "errors"
4546 "fmt"
47+ "io"
4648 "math/big"
4749 "os"
50+ "os/exec"
4851 "path/filepath"
4952 "runtime"
5053 "sort"
@@ -1040,3 +1043,125 @@ func withContentStoreConfig(opts ...store.Option) snapshotterConfigOpt {
10401043 c .ServiceConfig .FSConfig .ContentStoreConfig = store .NewStoreConfig (opts ... ).ContentStoreConfig
10411044 }
10421045}
1046+
1047+ type pigzImageInfo struct {
1048+ ref string
1049+ files map [string ]string
1050+ layerCount int
1051+ }
1052+
1053+ // buildPigzImage constructs a minimal OCI image with a single pigz-compressed layer
1054+ // and imports it into containerd via "ctr images import"
1055+ func buildPigzImage (t * testing.T , sh * shell.Shell , imageName string ) pigzImageInfo {
1056+ t .Helper ()
1057+
1058+ pigzPath , err := exec .LookPath ("pigz" )
1059+ if err != nil {
1060+ t .Fatal ("pigz is required but not installed" )
1061+ }
1062+
1063+ r := testutil .NewTestRand (t )
1064+ testFiles := map [string ]string {
1065+ "testfile1.txt" : "pigz-test-content-alpha-" + string (r .RandomByteData (1 << 20 )), // ~1 MB
1066+ "testfile2.txt" : "pigz-test-content-beta-" + string (r .RandomByteData (1 << 20 )), // ~1 MB
1067+ }
1068+
1069+ entries := []testutil.TarEntry {
1070+ testutil .File ("testfile1.txt" , testFiles ["testfile1.txt" ]),
1071+ testutil .File ("padding1.bin" , string (r .RandomByteData (1 << 23 ))), // 8 MB
1072+ testutil .File ("testfile2.txt" , testFiles ["testfile2.txt" ]),
1073+ testutil .File ("padding2.bin" , string (r .RandomByteData (1 << 23 ))), // 8 MB
1074+ }
1075+ tarData , err := io .ReadAll (testutil .BuildTar (entries ))
1076+ if err != nil {
1077+ t .Fatalf ("failed to build tar: %v" , err )
1078+ }
1079+
1080+ // Compress with pigz using 128KB block size (produces concatenated gzip members).
1081+ pigzCmd := exec .Command (pigzPath , "-b" , "128" , "-c" )
1082+ pigzCmd .Stdin = bytes .NewReader (tarData )
1083+ compressedData , err := pigzCmd .Output ()
1084+ if err != nil {
1085+ t .Fatalf ("pigz compression failed: %v" , err )
1086+ }
1087+ layerDigest := digest .FromBytes (compressedData )
1088+
1089+ platform := spec.Platform {Architecture : runtime .GOARCH , OS : "linux" }
1090+
1091+ // Build OCI image config.
1092+ configBytes , err := json .Marshal (spec.Image {
1093+ Platform : platform ,
1094+ RootFS : spec.RootFS {Type : "layers" , DiffIDs : []digest.Digest {digest .FromBytes (tarData )}},
1095+ })
1096+ if err != nil {
1097+ t .Fatalf ("failed to marshal config: %v" , err )
1098+ }
1099+
1100+ configDigest := digest .FromBytes (configBytes )
1101+ manifest := spec.Manifest {
1102+ MediaType : spec .MediaTypeImageManifest ,
1103+ Config : spec.Descriptor {MediaType : spec .MediaTypeImageConfig , Digest : configDigest , Size : int64 (len (configBytes ))},
1104+ Layers : []spec.Descriptor {{MediaType : spec .MediaTypeImageLayerGzip , Digest : layerDigest , Size : int64 (len (compressedData ))}},
1105+ }
1106+ manifest .SchemaVersion = 2
1107+ manifestBytes , err := json .Marshal (manifest )
1108+ if err != nil {
1109+ t .Fatalf ("failed to marshal manifest: %v" , err )
1110+ }
1111+ manifestDigest := digest .FromBytes (manifestBytes )
1112+
1113+ // Build OCI index.
1114+ index := spec.Index {
1115+ MediaType : spec .MediaTypeImageIndex ,
1116+ Manifests : []spec.Descriptor {
1117+ {
1118+ MediaType : spec .MediaTypeImageManifest ,
1119+ Digest : manifestDigest ,
1120+ Size : int64 (len (manifestBytes )),
1121+ Platform : & platform ,
1122+ Annotations : map [string ]string {"io.containerd.image.name" : imageName },
1123+ },
1124+ },
1125+ }
1126+ index .SchemaVersion = 2
1127+ indexBytes , err := json .Marshal (index )
1128+ if err != nil {
1129+ t .Fatalf ("failed to marshal index: %v" , err )
1130+ }
1131+
1132+ // Assemble OCI image layout as a tar archive.
1133+ var buf bytes.Buffer
1134+ tw := tar .NewWriter (& buf )
1135+ for name , data := range map [string ][]byte {
1136+ "oci-layout" : []byte (`{"imageLayoutVersion":"1.0.0"}` ),
1137+ "index.json" : indexBytes ,
1138+ "blobs/sha256/" + layerDigest .Encoded (): compressedData ,
1139+ "blobs/sha256/" + configDigest .Encoded (): configBytes ,
1140+ "blobs/sha256/" + manifestDigest .Encoded (): manifestBytes ,
1141+ } {
1142+ if err := tw .WriteHeader (& tar.Header {Name : name , Mode : 0644 , Size : int64 (len (data ))}); err != nil {
1143+ t .Fatalf ("failed to write tar header for %s: %v" , name , err )
1144+ }
1145+ if _ , err := tw .Write (data ); err != nil {
1146+ t .Fatalf ("failed to write tar data for %s: %v" , name , err )
1147+ }
1148+ }
1149+ if err := tw .Close (); err != nil {
1150+ t .Fatalf ("failed to close tar writer: %v" , err )
1151+ }
1152+ ociTar := buf .Bytes ()
1153+
1154+ // Write OCI tar to test container and import into containerd.
1155+ tmpPath := "/tmp/pigz-image-" + xid .New ().String () + ".tar"
1156+ if err := testutil .WriteFileContents (sh , tmpPath , ociTar , 0644 ); err != nil {
1157+ t .Fatalf ("failed to write OCI tar to container: %v" , err )
1158+ }
1159+ sh .X ("ctr" , "images" , "import" , tmpPath )
1160+ sh .X ("rm" , "-f" , tmpPath )
1161+
1162+ return pigzImageInfo {
1163+ ref : imageName ,
1164+ files : testFiles ,
1165+ layerCount : 1 ,
1166+ }
1167+ }
0 commit comments