@@ -1485,22 +1485,26 @@ RUN date --utc > /root/date.txt`, testImageAlpine),
1485
1485
})
1486
1486
1487
1487
t .Run ("CacheAndPushMultistage" , func (t * testing.T ) {
1488
- // Currently fails with:
1489
- // /home/coder/src/coder/envbuilder/integration/integration_test.go:1417: "error: unable to get cached image: error fake building stage: failed to optimize instructions: failed to get files used from context: failed to get fileinfo for /.envbuilder/0/root/date.txt: lstat /.envbuilder/0/root/date.txt: no such file or directory"
1490
- // /home/coder/src/coder/envbuilder/integration/integration_test.go:1156:
1491
- // Error Trace: /home/coder/src/coder/envbuilder/integration/integration_test.go:1156
1492
- // Error: Received unexpected error:
1493
- // error: unable to get cached image: error fake building stage: failed to optimize instructions: failed to get files used from context: failed to get fileinfo for /.envbuilder/0/root/date.txt: lstat /.envbuilder/0/root/date.txt: no such file or directory
1494
- // Test: TestPushImage/CacheAndPushMultistage
1495
- t .Skip ("TODO: https://github.com/coder/envbuilder/issues/230" )
1496
1488
t .Parallel ()
1497
1489
1498
1490
srv := gittest .CreateGitServer (t , gittest.Options {
1499
1491
Files : map [string ]string {
1500
- "Dockerfile" : fmt .Sprintf (`FROM %s AS a
1501
- RUN date --utc > /root/date.txt
1502
- FROM %s as b
1503
- COPY --from=a /root/date.txt /date.txt` , testImageAlpine , testImageAlpine ),
1492
+ "Dockerfile" : fmt .Sprintf (`
1493
+ FROM %[1]s AS prebuild
1494
+ RUN mkdir /the-past /the-future \
1495
+ && echo "hello from the past" > /the-past/hello.txt \
1496
+ && cd /the-past \
1497
+ && ln -s hello.txt hello.link \
1498
+ && echo "hello from the future" > /the-future/hello.txt
1499
+
1500
+ FROM %[1]s
1501
+ USER root
1502
+ ARG WORKDIR=/
1503
+ WORKDIR $WORKDIR
1504
+ ENV FOO=bar
1505
+ COPY --from=prebuild /the-past /the-past
1506
+ COPY --from=prebuild /the-future/hello.txt /the-future/hello.txt
1507
+ ` , testImageAlpine ),
1504
1508
},
1505
1509
})
1506
1510
@@ -1525,16 +1529,122 @@ COPY --from=a /root/date.txt /date.txt`, testImageAlpine, testImageAlpine),
1525
1529
require .ErrorContains (t , err , "NAME_UNKNOWN" , "expected image to not be present before build + push" )
1526
1530
1527
1531
// When: we run envbuilder with PUSH_IMAGE set
1532
+ _ , err = runEnvbuilder (t , runOpts {env : []string {
1533
+ envbuilderEnv ("GIT_URL" , srv .URL ),
1534
+ envbuilderEnv ("CACHE_REPO" , testRepo ),
1535
+ envbuilderEnv ("PUSH_IMAGE" , "1" ),
1536
+ envbuilderEnv ("DOCKERFILE_PATH" , "Dockerfile" ),
1537
+ }})
1538
+ require .NoError (t , err )
1539
+
1540
+ // Then: the image should be pushed
1541
+ _ , err = remote .Image (ref )
1542
+ require .NoError (t , err , "expected image to be present after build + push" )
1543
+
1544
+ // Then: re-running envbuilder with GET_CACHED_IMAGE should succeed
1528
1545
ctrID , err := runEnvbuilder (t , runOpts {env : []string {
1546
+ envbuilderEnv ("GIT_URL" , srv .URL ),
1547
+ envbuilderEnv ("CACHE_REPO" , testRepo ),
1548
+ envbuilderEnv ("GET_CACHED_IMAGE" , "1" ),
1549
+ envbuilderEnv ("DOCKERFILE_PATH" , "Dockerfile" ),
1550
+ }})
1551
+ require .NoError (t , err )
1552
+
1553
+ // Then: the cached image ref should be emitted in the container logs
1554
+ ctx , cancel := context .WithCancel (context .Background ())
1555
+ t .Cleanup (cancel )
1556
+ cli , err := client .NewClientWithOpts (client .FromEnv , client .WithAPIVersionNegotiation ())
1557
+ require .NoError (t , err )
1558
+ defer cli .Close ()
1559
+ logs , err := cli .ContainerLogs (ctx , ctrID , container.LogsOptions {
1560
+ ShowStdout : true ,
1561
+ ShowStderr : true ,
1562
+ })
1563
+ require .NoError (t , err )
1564
+ defer logs .Close ()
1565
+ logBytes , err := io .ReadAll (logs )
1566
+ require .NoError (t , err )
1567
+ require .Regexp (t , `ENVBUILDER_CACHED_IMAGE=(\S+)` , string (logBytes ))
1568
+
1569
+ // When: we pull the image we just built
1570
+ rc , err := cli .ImagePull (ctx , ref .String (), image.PullOptions {})
1571
+ require .NoError (t , err )
1572
+ t .Cleanup (func () { _ = rc .Close () })
1573
+ _ , err = io .ReadAll (rc )
1574
+ require .NoError (t , err )
1575
+
1576
+ // When: we run the image we just built
1577
+ ctr , err := cli .ContainerCreate (ctx , & container.Config {
1578
+ Image : ref .String (),
1579
+ Entrypoint : []string {"sleep" , "infinity" },
1580
+ Labels : map [string ]string {
1581
+ testContainerLabel : "true" ,
1582
+ },
1583
+ }, nil , nil , nil , "" )
1584
+ require .NoError (t , err )
1585
+ t .Cleanup (func () {
1586
+ _ = cli .ContainerRemove (ctx , ctr .ID , container.RemoveOptions {
1587
+ RemoveVolumes : true ,
1588
+ Force : true ,
1589
+ })
1590
+ })
1591
+ err = cli .ContainerStart (ctx , ctr .ID , container.StartOptions {})
1592
+ require .NoError (t , err )
1593
+
1594
+ // Then: The files from the prebuild stage are present.
1595
+ out := execContainer (t , ctr .ID , "/bin/sh -c 'cat /the-past/hello.txt /the-future/hello.txt; readlink -f /the-past/hello.link'" )
1596
+ require .Equal (t , "hello from the past\n hello from the future\n /the-past/hello.txt" , strings .TrimSpace (out ))
1597
+ })
1598
+
1599
+ t .Run ("MultistgeCacheMissAfterChange" , func (t * testing.T ) {
1600
+ t .Parallel ()
1601
+ dockerfilePrebuildContents := fmt .Sprintf (`
1602
+ FROM %[1]s AS prebuild
1603
+ RUN mkdir /the-past /the-future \
1604
+ && echo "hello from the past" > /the-past/hello.txt \
1605
+ && cd /the-past \
1606
+ && ln -s hello.txt hello.link \
1607
+ && echo "hello from the future" > /the-future/hello.txt
1608
+
1609
+ # Workaround for https://github.com/coder/envbuilder/issues/231
1610
+ FROM %[1]s
1611
+ ` , testImageAlpine )
1612
+
1613
+ dockerfileContents := fmt .Sprintf (`
1614
+ FROM %s
1615
+ USER root
1616
+ ARG WORKDIR=/
1617
+ WORKDIR $WORKDIR
1618
+ ENV FOO=bar
1619
+ COPY --from=prebuild /the-past /the-past
1620
+ COPY --from=prebuild /the-future/hello.txt /the-future/hello.txt
1621
+ RUN echo $FOO > /root/foo.txt
1622
+ RUN date --utc > /root/date.txt
1623
+ ` , testImageAlpine )
1624
+
1625
+ newServer := func (dockerfile string ) * httptest.Server {
1626
+ return gittest .CreateGitServer (t , gittest.Options {
1627
+ Files : map [string ]string {"Dockerfile" : dockerfile },
1628
+ })
1629
+ }
1630
+ srv := newServer (dockerfilePrebuildContents + dockerfileContents )
1631
+
1632
+ // Given: an empty registry
1633
+ testReg := setupInMemoryRegistry (t , setupInMemoryRegistryOpts {})
1634
+ testRepo := testReg + "/test"
1635
+ ref , err := name .ParseReference (testRepo + ":latest" )
1636
+ require .NoError (t , err )
1637
+ _ , err = remote .Image (ref )
1638
+ require .ErrorContains (t , err , "NAME_UNKNOWN" , "expected image to not be present before build + push" )
1639
+
1640
+ // When: we run envbuilder with PUSH_IMAGE set
1641
+ _ , err = runEnvbuilder (t , runOpts {env : []string {
1529
1642
envbuilderEnv ("GIT_URL" , srv .URL ),
1530
1643
envbuilderEnv ("CACHE_REPO" , testRepo ),
1531
1644
envbuilderEnv ("PUSH_IMAGE" , "1" ),
1532
1645
envbuilderEnv ("DOCKERFILE_PATH" , "Dockerfile" ),
1533
1646
}})
1534
1647
require .NoError (t , err )
1535
- // Then: The file copied from stage a should be present
1536
- out := execContainer (t , ctrID , "cat /date.txt" )
1537
- require .NotEmpty (t , out )
1538
1648
1539
1649
// Then: the image should be pushed
1540
1650
_ , err = remote .Image (ref )
@@ -1548,6 +1658,33 @@ COPY --from=a /root/date.txt /date.txt`, testImageAlpine, testImageAlpine),
1548
1658
envbuilderEnv ("DOCKERFILE_PATH" , "Dockerfile" ),
1549
1659
}})
1550
1660
require .NoError (t , err )
1661
+
1662
+ // When: we change the Dockerfile
1663
+ srv .Close ()
1664
+ dockerfilePrebuildContents = strings .Replace (dockerfilePrebuildContents , "hello from the future" , "hello from the future, but different" , 1 )
1665
+ srv = newServer (dockerfilePrebuildContents )
1666
+
1667
+ // When: we rebuild the prebuild stage so that the cache is created
1668
+ _ , err = runEnvbuilder (t , runOpts {env : []string {
1669
+ envbuilderEnv ("GIT_URL" , srv .URL ),
1670
+ envbuilderEnv ("CACHE_REPO" , testRepo ),
1671
+ envbuilderEnv ("PUSH_IMAGE" , "1" ),
1672
+ envbuilderEnv ("DOCKERFILE_PATH" , "Dockerfile" ),
1673
+ }})
1674
+ require .NoError (t , err )
1675
+
1676
+ // Then: re-running envbuilder with GET_CACHED_IMAGE should still fail
1677
+ // on the second stage because the first stage file has changed.
1678
+ srv .Close ()
1679
+ srv = newServer (dockerfilePrebuildContents + dockerfileContents )
1680
+ _ , err = runEnvbuilder (t , runOpts {env : []string {
1681
+ envbuilderEnv ("GIT_URL" , srv .URL ),
1682
+ envbuilderEnv ("CACHE_REPO" , testRepo ),
1683
+ envbuilderEnv ("GET_CACHED_IMAGE" , "1" ),
1684
+ envbuilderEnv ("DOCKERFILE_PATH" , "Dockerfile" ),
1685
+ envbuilderEnv ("VERBOSE" , "1" ),
1686
+ }})
1687
+ require .ErrorContains (t , err , "error probing build cache: uncached COPY command" )
1551
1688
})
1552
1689
1553
1690
t .Run ("PushImageRequiresCache" , func (t * testing.T ) {
0 commit comments