@@ -109,9 +109,9 @@ func TestDockerPackage_ExportToCache_Integration(t *testing.T) {
109109 name : "export without image config" ,
110110 exportToCache : true ,
111111 hasImages : false ,
112- expectFiles : []string {"content" },
113- expectError : true , // OCI layout export requires an image tag
114- expectErrorMatch : "(?i)(not found|failed)" , // Build fails without image config in OCI mode
112+ expectFiles : []string {"content" }, // Without image config, extracts container filesystem
113+ expectError : false ,
114+ expectErrorMatch : "" ,
115115 },
116116 }
117117
@@ -122,6 +122,26 @@ func TestDockerPackage_ExportToCache_Integration(t *testing.T) {
122122 t .Skip (tt .skipReason )
123123 }
124124
125+ // Create docker-container builder for OCI export if needed
126+ if tt .exportToCache {
127+ builderName := "leeway-export-test-builder"
128+ createBuilder := exec .Command ("docker" , "buildx" , "create" , "--name" , builderName , "--driver" , "docker-container" , "--bootstrap" )
129+ if err := createBuilder .Run (); err != nil {
130+ // Builder might already exist, try to use it
131+ t .Logf ("Builder creation failed (might already exist): %v" , err )
132+ }
133+ defer func () {
134+ removeBuilder := exec .Command ("docker" , "buildx" , "rm" , builderName )
135+ _ = removeBuilder .Run ()
136+ }()
137+
138+ // Set builder as default for this test
139+ useBuilder := exec .Command ("docker" , "buildx" , "use" , builderName )
140+ if err := useBuilder .Run (); err != nil {
141+ t .Fatalf ("Failed to use builder: %v" , err )
142+ }
143+ }
144+
125145 // Create temporary workspace
126146 tmpDir := t .TempDir ()
127147
@@ -334,6 +354,22 @@ func TestDockerPackage_CacheRoundTrip_Integration(t *testing.T) {
334354 t .Skip ("Docker not available, skipping integration test" )
335355 }
336356
357+ // Create docker-container builder for OCI export
358+ builderName := "leeway-roundtrip-test-builder"
359+ createBuilder := exec .Command ("docker" , "buildx" , "create" , "--name" , builderName , "--driver" , "docker-container" , "--bootstrap" )
360+ if err := createBuilder .Run (); err != nil {
361+ t .Logf ("Builder creation failed (might already exist): %v" , err )
362+ }
363+ defer func () {
364+ removeBuilder := exec .Command ("docker" , "buildx" , "rm" , builderName )
365+ _ = removeBuilder .Run ()
366+ }()
367+
368+ useBuilder := exec .Command ("docker" , "buildx" , "use" , builderName )
369+ if err := useBuilder .Run (); err != nil {
370+ t .Fatalf ("Failed to use builder: %v" , err )
371+ }
372+
337373 // This test verifies that a Docker image can be:
338374 // 1. Built and exported to cache
339375 // 2. Extracted from cache
@@ -969,3 +1005,164 @@ CMD ["cat", "/build-time.txt"]
9691005 t .Logf ("✅ No 'docker inspect' error occurred" )
9701006 t .Logf ("✅ This confirms the fix works: digest extracted from OCI layout instead of Docker daemon" )
9711007}
1008+
1009+ // TestDockerPackage_ContainerExtraction_Integration tests container filesystem extraction
1010+ // with both Docker daemon and OCI layout paths
1011+ func TestDockerPackage_ContainerExtraction_Integration (t * testing.T ) {
1012+ if testing .Short () {
1013+ t .Skip ("Skipping integration test in short mode" )
1014+ }
1015+
1016+ // Ensure Docker is available
1017+ if err := exec .Command ("docker" , "version" ).Run (); err != nil {
1018+ t .Skip ("Docker not available, skipping integration test" )
1019+ }
1020+
1021+ // Ensure buildx is available
1022+ if err := exec .Command ("docker" , "buildx" , "version" ).Run (); err != nil {
1023+ t .Skip ("Docker buildx not available, skipping integration test" )
1024+ }
1025+
1026+ // Create docker-container builder for OCI export
1027+ builderName := "leeway-extract-test-builder"
1028+ createBuilder := exec .Command ("docker" , "buildx" , "create" , "--name" , builderName , "--driver" , "docker-container" , "--bootstrap" )
1029+ if err := createBuilder .Run (); err != nil {
1030+ t .Logf ("Warning: failed to create builder (might already exist): %v" , err )
1031+ }
1032+ defer func () {
1033+ exec .Command ("docker" , "buildx" , "rm" , builderName ).Run ()
1034+ }()
1035+
1036+ useBuilder := exec .Command ("docker" , "buildx" , "use" , builderName )
1037+ if err := useBuilder .Run (); err != nil {
1038+ t .Fatalf ("Failed to use builder: %v" , err )
1039+ }
1040+ defer func () {
1041+ exec .Command ("docker" , "buildx" , "use" , "default" ).Run ()
1042+ }()
1043+
1044+ // Test both paths
1045+ testCases := []struct {
1046+ name string
1047+ exportToCache bool
1048+ expectedMessage string
1049+ }{
1050+ {
1051+ name : "with_docker_daemon" ,
1052+ exportToCache : false ,
1053+ expectedMessage : "Image found in Docker daemon" ,
1054+ },
1055+ {
1056+ name : "with_oci_layout" ,
1057+ exportToCache : true ,
1058+ expectedMessage : "OCI layout image.tar found and valid" ,
1059+ },
1060+ }
1061+
1062+ for _ , tc := range testCases {
1063+ t .Run (tc .name , func (t * testing.T ) {
1064+ tmpDir := t .TempDir ()
1065+ wsDir := filepath .Join (tmpDir , "workspace" )
1066+ if err := os .MkdirAll (wsDir , 0755 ); err != nil {
1067+ t .Fatal (err )
1068+ }
1069+
1070+ // Create WORKSPACE.yaml
1071+ workspaceYAML := `defaultTarget: ":test-extract"`
1072+ if err := os .WriteFile (filepath .Join (wsDir , "WORKSPACE.yaml" ), []byte (workspaceYAML ), 0644 ); err != nil {
1073+ t .Fatal (err )
1074+ }
1075+
1076+ // Create Dockerfile
1077+ dockerfile := `FROM alpine:3.18
1078+ RUN echo "test content" > /test.txt
1079+ `
1080+ if err := os .WriteFile (filepath .Join (wsDir , "Dockerfile" ), []byte (dockerfile ), 0644 ); err != nil {
1081+ t .Fatal (err )
1082+ }
1083+
1084+ // Create BUILD.yaml with container extraction
1085+ buildYAML := fmt .Sprintf (`packages:
1086+ - name: test-extract
1087+ type: docker
1088+ config:
1089+ dockerfile: Dockerfile
1090+ exportToCache: %v
1091+ ` , tc .exportToCache )
1092+ if err := os .WriteFile (filepath .Join (wsDir , "BUILD.yaml" ), []byte (buildYAML ), 0644 ); err != nil {
1093+ t .Fatal (err )
1094+ }
1095+
1096+ // Initialize git repo
1097+ gitInit := exec .Command ("git" , "init" )
1098+ gitInit .Dir = wsDir
1099+ if err := gitInit .Run (); err != nil {
1100+ t .Fatal (err )
1101+ }
1102+
1103+ gitConfigName := exec .Command ("git" , "config" , "user.name" , "Test User" )
1104+ gitConfigName .Dir = wsDir
1105+ if err := gitConfigName .Run (); err != nil {
1106+ t .Fatal (err )
1107+ }
1108+
1109+ gitConfigEmail := exec .Command ("git" , "config" , "user.email" , "test@example.com" )
1110+ gitConfigEmail .Dir = wsDir
1111+ if err := gitConfigEmail .Run (); err != nil {
1112+ t .Fatal (err )
1113+ }
1114+
1115+ gitAdd := exec .Command ("git" , "add" , "." )
1116+ gitAdd .Dir = wsDir
1117+ if err := gitAdd .Run (); err != nil {
1118+ t .Fatal (err )
1119+ }
1120+
1121+ gitCommit := exec .Command ("git" , "commit" , "-m" , "initial" )
1122+ gitCommit .Dir = wsDir
1123+ gitCommit .Env = append (os .Environ (),
1124+ "GIT_AUTHOR_DATE=2021-01-01T00:00:00Z" ,
1125+ "GIT_COMMITTER_DATE=2021-01-01T00:00:00Z" ,
1126+ )
1127+ if err := gitCommit .Run (); err != nil {
1128+ t .Fatal (err )
1129+ }
1130+
1131+ // Build
1132+ cacheDir := filepath .Join (tmpDir , "cache" )
1133+ cache , err := local .NewFilesystemCache (cacheDir )
1134+ if err != nil {
1135+ t .Fatal (err )
1136+ }
1137+
1138+ buildCtx , err := newBuildContext (buildOptions {
1139+ LocalCache : cache ,
1140+ DockerExportToCache : tc .exportToCache ,
1141+ DockerExportSet : true ,
1142+ Reporter : NewConsoleReporter (),
1143+ })
1144+ if err != nil {
1145+ t .Fatal (err )
1146+ }
1147+
1148+ ws , err := FindWorkspace (wsDir , Arguments {}, "" , "" )
1149+ if err != nil {
1150+ t .Fatal (err )
1151+ }
1152+
1153+ pkg , ok := ws .Packages ["//:test-extract" ]
1154+ if ! ok {
1155+ t .Fatal ("package //:test-extract not found" )
1156+ }
1157+
1158+ // Build the package - this should extract the container filesystem
1159+ if err := pkg .build (buildCtx ); err != nil {
1160+ t .Fatalf ("build failed: %v" , err )
1161+ }
1162+
1163+ t .Logf ("✅ Build succeeded with exportToCache=%v" , tc .exportToCache )
1164+ t .Logf ("✅ Container filesystem extraction completed" )
1165+ t .Logf ("✅ No 'image not found' error occurred" )
1166+ })
1167+ }
1168+ }
0 commit comments