@@ -1279,7 +1279,11 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
1279
1279
// No base image means there's nothing to put in a
1280
1280
// layer, so don't create one.
1281
1281
emptyLayer := (s .builder .FromImageID == "" )
1282
- if imgID , ref , err = s .commit (ctx , s .getCreatedBy (nil , "" ), emptyLayer , s .output , s .executor .squash || s .executor .confidentialWorkload .Convert , lastStage ); err != nil {
1282
+ createdBy , err := s .getCreatedBy (nil , "" )
1283
+ if err != nil {
1284
+ return "" , nil , false , fmt .Errorf ("unable to get createdBy for the node: %w" , err )
1285
+ }
1286
+ if imgID , ref , err = s .commit (ctx , createdBy , emptyLayer , s .output , s .executor .squash || s .executor .confidentialWorkload .Convert , lastStage ); err != nil {
1283
1287
return "" , nil , false , fmt .Errorf ("committing base container: %w" , err )
1284
1288
}
1285
1289
} else {
@@ -1426,7 +1430,11 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
1426
1430
if s .executor .timestamp != nil {
1427
1431
timestamp = * s .executor .timestamp
1428
1432
}
1429
- s .builder .AddPrependedEmptyLayer (& timestamp , s .getCreatedBy (node , addedContentSummary ), "" , "" )
1433
+ createdBy , err := s .getCreatedBy (node , addedContentSummary )
1434
+ if err != nil {
1435
+ return "" , nil , false , fmt .Errorf ("unable to get createdBy for the node: %w" , err )
1436
+ }
1437
+ s .builder .AddPrependedEmptyLayer (& timestamp , createdBy , "" , "" )
1430
1438
continue
1431
1439
}
1432
1440
// This is the last instruction for this stage,
@@ -1436,7 +1444,11 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
1436
1444
// stage.
1437
1445
if lastStage || imageIsUsedLater {
1438
1446
logCommit (s .output , i )
1439
- imgID , ref , err = s .commit (ctx , s .getCreatedBy (node , addedContentSummary ), false , s .output , s .executor .squash , lastStage && lastInstruction )
1447
+ createdBy , err := s .getCreatedBy (node , addedContentSummary )
1448
+ if err != nil {
1449
+ return "" , nil , false , fmt .Errorf ("unable to get createdBy for the node: %w" , err )
1450
+ }
1451
+ imgID , ref , err = s .commit (ctx , createdBy , false , s .output , s .executor .squash , lastStage && lastInstruction )
1440
1452
if err != nil {
1441
1453
return "" , nil , false , fmt .Errorf ("committing container for step %+v: %w" , * step , err )
1442
1454
}
@@ -1657,14 +1669,18 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
1657
1669
// We're not going to find any more cache hits, so we
1658
1670
// can stop looking for them.
1659
1671
checkForLayers = false
1672
+ createdBy , err := s .getCreatedBy (node , addedContentSummary )
1673
+ if err != nil {
1674
+ return "" , nil , false , fmt .Errorf ("unable to get createdBy for the node: %w" , err )
1675
+ }
1660
1676
// Create a new image, maybe with a new layer, with the
1661
1677
// name for this stage if it's the last instruction.
1662
1678
logCommit (s .output , i )
1663
1679
// While committing we always set squash to false here
1664
1680
// because at this point we want to save history for
1665
1681
// layers even if its a squashed build so that they
1666
1682
// can be part of the build cache.
1667
- imgID , ref , err = s .commit (ctx , s . getCreatedBy ( node , addedContentSummary ) , ! s .stepRequiresLayer (step ), commitName , false , lastStage && lastInstruction )
1683
+ imgID , ref , err = s .commit (ctx , createdBy , ! s .stepRequiresLayer (step ), commitName , false , lastStage && lastInstruction )
1668
1684
if err != nil {
1669
1685
return "" , nil , false , fmt .Errorf ("committing container for step %+v: %w" , * step , err )
1670
1686
}
@@ -1695,12 +1711,16 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
1695
1711
1696
1712
if lastInstruction && lastStage {
1697
1713
if s .executor .squash || s .executor .confidentialWorkload .Convert || len (s .executor .sbomScanOptions ) != 0 {
1714
+ createdBy , err := s .getCreatedBy (node , addedContentSummary )
1715
+ if err != nil {
1716
+ return "" , nil , false , fmt .Errorf ("unable to get createdBy for the node: %w" , err )
1717
+ }
1698
1718
// If this is the last instruction of the last stage,
1699
1719
// create a squashed or confidential workload
1700
1720
// version of the image if that's what we're after,
1701
1721
// or a normal one if we need to scan the image while
1702
1722
// committing it.
1703
- imgID , ref , err = s .commit (ctx , s . getCreatedBy ( node , addedContentSummary ) , ! s .stepRequiresLayer (step ), commitName , s .executor .squash || s .executor .confidentialWorkload .Convert , lastStage && lastInstruction )
1723
+ imgID , ref , err = s .commit (ctx , createdBy , ! s .stepRequiresLayer (step ), commitName , s .executor .squash || s .executor .confidentialWorkload .Convert , lastStage && lastInstruction )
1704
1724
if err != nil {
1705
1725
return "" , nil , false , fmt .Errorf ("committing final squash step %+v: %w" , * step , err )
1706
1726
}
@@ -1792,54 +1812,58 @@ func historyEntriesEqual(base, derived v1.History) bool {
1792
1812
// that we're comparing.
1793
1813
// Used to verify whether a cache of the intermediate image exists and whether
1794
1814
// to run the build again.
1795
- func (s * StageExecutor ) historyAndDiffIDsMatch (baseHistory []v1.History , baseDiffIDs []digest.Digest , child * parser.Node , history []v1.History , diffIDs []digest.Digest , addedContentSummary string , buildAddsLayer bool ) bool {
1815
+ func (s * StageExecutor ) historyAndDiffIDsMatch (baseHistory []v1.History , baseDiffIDs []digest.Digest , child * parser.Node , history []v1.History , diffIDs []digest.Digest , addedContentSummary string , buildAddsLayer bool ) ( bool , error ) {
1796
1816
// our history should be as long as the base's, plus one entry for what
1797
1817
// we're doing
1798
1818
if len (history ) != len (baseHistory )+ 1 {
1799
- return false
1819
+ return false , nil
1800
1820
}
1801
1821
// check that each entry in the base history corresponds to an entry in
1802
1822
// our history, and count how many of them add a layer diff
1803
1823
expectedDiffIDs := 0
1804
1824
for i := range baseHistory {
1805
1825
if ! historyEntriesEqual (baseHistory [i ], history [i ]) {
1806
- return false
1826
+ return false , nil
1807
1827
}
1808
1828
if ! baseHistory [i ].EmptyLayer {
1809
1829
expectedDiffIDs ++
1810
1830
}
1811
1831
}
1812
1832
if len (baseDiffIDs ) != expectedDiffIDs {
1813
- return false
1833
+ return false , nil
1814
1834
}
1815
1835
if buildAddsLayer {
1816
1836
// we're adding a layer, so we should have exactly one more
1817
1837
// layer than the base image
1818
1838
if len (diffIDs ) != expectedDiffIDs + 1 {
1819
- return false
1839
+ return false , nil
1820
1840
}
1821
1841
} else {
1822
1842
// we're not adding a layer, so we should have exactly the same
1823
1843
// layers as the base image
1824
1844
if len (diffIDs ) != expectedDiffIDs {
1825
- return false
1845
+ return false , nil
1826
1846
}
1827
1847
}
1828
1848
// compare the diffs for the layers that we should have in common
1829
1849
for i := range baseDiffIDs {
1830
1850
if diffIDs [i ] != baseDiffIDs [i ] {
1831
- return false
1851
+ return false , nil
1832
1852
}
1833
1853
}
1834
- return history [len (baseHistory )].CreatedBy == s .getCreatedBy (child , addedContentSummary )
1854
+ createdBy , err := s .getCreatedBy (child , addedContentSummary )
1855
+ if err != nil {
1856
+ return false , fmt .Errorf ("unable to get createdBy for the node: %w" , err )
1857
+ }
1858
+ return history [len (baseHistory )].CreatedBy == createdBy , nil
1835
1859
}
1836
1860
1837
1861
// getCreatedBy returns the command the image at node will be created by. If
1838
1862
// the passed-in CompositeDigester is not nil, it is assumed to have the digest
1839
1863
// information for the content if the node is ADD or COPY.
1840
- func (s * StageExecutor ) getCreatedBy (node * parser.Node , addedContentSummary string ) string {
1864
+ func (s * StageExecutor ) getCreatedBy (node * parser.Node , addedContentSummary string ) ( string , error ) {
1841
1865
if node == nil {
1842
- return "/bin/sh"
1866
+ return "/bin/sh" , nil
1843
1867
}
1844
1868
switch strings .ToUpper (node .Value ) {
1845
1869
case "ARG" :
@@ -1849,15 +1873,72 @@ func (s *StageExecutor) getCreatedBy(node *parser.Node, addedContentSummary stri
1849
1873
}
1850
1874
}
1851
1875
buildArgs := s .getBuildArgsKey ()
1852
- return "/bin/sh -c #(nop) ARG " + buildArgs
1876
+ return "/bin/sh -c #(nop) ARG " + buildArgs , nil
1853
1877
case "RUN" :
1854
1878
shArg := ""
1855
1879
buildArgs := s .getBuildArgsResolvedForRun ()
1880
+ appendCheckSum := ""
1881
+ for _ , flag := range node .Flags {
1882
+ var err error
1883
+ mountOptionSource := ""
1884
+ mountOptionFrom := ""
1885
+ mountCheckSum := ""
1886
+ if strings .HasPrefix (flag , "--mount=" ) {
1887
+ mountInfo := getFromAndSourceKeysFromMountFlag (flag )
1888
+ if mountInfo .Type != "bind" {
1889
+ continue
1890
+ }
1891
+ mountOptionSource = mountInfo .Source
1892
+ mountOptionFrom = mountInfo .From
1893
+ // If source is not specified then default is '.'
1894
+ if mountOptionSource == "" {
1895
+ mountOptionSource = "."
1896
+ }
1897
+ }
1898
+ // Source specificed is part of stage, image or additional-build-context.
1899
+ if mountOptionFrom != "" {
1900
+ // If this is not a stage then get digest of image or additional build context
1901
+ if _ , ok := s .executor .stages [mountOptionFrom ]; ! ok {
1902
+ if builder , ok := s .executor .containerMap [mountOptionFrom ]; ok {
1903
+ // Found valid image, get image digest.
1904
+ mountCheckSum = builder .FromImageDigest
1905
+ } else {
1906
+ if s .executor .additionalBuildContexts [mountOptionFrom ].IsImage {
1907
+ if builder , ok := s .executor .containerMap [s .executor .additionalBuildContexts [mountOptionFrom ].Value ]; ok {
1908
+ // Found valid image, get image digest.
1909
+ mountCheckSum = builder .FromImageDigest
1910
+ }
1911
+ } else {
1912
+ // Found additional build context, get directory sha.
1913
+ basePath := s .executor .additionalBuildContexts [mountOptionFrom ].Value
1914
+ if s .executor .additionalBuildContexts [mountOptionFrom ].IsURL {
1915
+ basePath = s .executor .additionalBuildContexts [mountOptionFrom ].DownloadedCache
1916
+ }
1917
+ mountCheckSum , err = generatePathChecksum (filepath .Join (basePath , mountOptionSource ))
1918
+ if err != nil {
1919
+ return "" , fmt .Errorf ("generating checksum for directory %q in %q: %w" , mountOptionSource , basePath , err )
1920
+ }
1921
+ }
1922
+ }
1923
+ }
1924
+ } else {
1925
+ if mountOptionSource != "" {
1926
+ mountCheckSum , err = generatePathChecksum (filepath .Join (s .executor .contextDir , mountOptionSource ))
1927
+ if err != nil {
1928
+ return "" , fmt .Errorf ("generating checksum for directory %q in %q: %w" , mountOptionSource , s .executor .contextDir , err )
1929
+ }
1930
+ }
1931
+ }
1932
+ if mountCheckSum != "" {
1933
+ // add a separator to appendCheckSum
1934
+ appendCheckSum += ":" + mountCheckSum
1935
+ }
1936
+ }
1856
1937
if len (node .Original ) > 4 {
1857
1938
shArg = node .Original [4 :]
1858
1939
}
1859
1940
if buildArgs != "" {
1860
- return "|" + strconv .Itoa (len (strings .Split (buildArgs , " " ))) + " " + buildArgs + " /bin/sh -c " + shArg
1941
+ return "|" + strconv .Itoa (len (strings .Split (buildArgs , " " ))) + " " + buildArgs + " /bin/sh -c " + shArg + appendCheckSum , nil
1861
1942
}
1862
1943
result := "/bin/sh -c " + shArg
1863
1944
if len (node .Heredocs ) > 0 {
@@ -1866,15 +1947,15 @@ func (s *StageExecutor) getCreatedBy(node *parser.Node, addedContentSummary stri
1866
1947
result = result + "\n " + heredocContent
1867
1948
}
1868
1949
}
1869
- return result
1950
+ return result + appendCheckSum , nil
1870
1951
case "ADD" , "COPY" :
1871
1952
destination := node
1872
1953
for destination .Next != nil {
1873
1954
destination = destination .Next
1874
1955
}
1875
- return "/bin/sh -c #(nop) " + strings .ToUpper (node .Value ) + " " + addedContentSummary + " in " + destination .Value + " "
1956
+ return "/bin/sh -c #(nop) " + strings .ToUpper (node .Value ) + " " + addedContentSummary + " in " + destination .Value + " " , nil
1876
1957
default :
1877
- return "/bin/sh -c #(nop) " + node .Original
1958
+ return "/bin/sh -c #(nop) " + node .Original , nil
1878
1959
}
1879
1960
}
1880
1961
@@ -2023,7 +2104,10 @@ func (s *StageExecutor) generateCacheKey(ctx context.Context, currNode *parser.N
2023
2104
fmt .Fprintln (hash , diffIDs [i ].String ())
2024
2105
}
2025
2106
}
2026
- createdBy := s .getCreatedBy (currNode , addedContentDigest )
2107
+ createdBy , err := s .getCreatedBy (currNode , addedContentDigest )
2108
+ if err != nil {
2109
+ return "" , err
2110
+ }
2027
2111
fmt .Fprintf (hash , "%t" , buildAddsLayer )
2028
2112
fmt .Fprintln (hash , createdBy )
2029
2113
fmt .Fprintln (hash , manifestType )
@@ -2203,7 +2287,11 @@ func (s *StageExecutor) intermediateImageExists(ctx context.Context, currNode *p
2203
2287
continue
2204
2288
}
2205
2289
// children + currNode is the point of the Dockerfile we are currently at.
2206
- if s .historyAndDiffIDsMatch (baseHistory , baseDiffIDs , currNode , history , diffIDs , addedContentDigest , buildAddsLayer ) {
2290
+ foundMatch , err := s .historyAndDiffIDsMatch (baseHistory , baseDiffIDs , currNode , history , diffIDs , addedContentDigest , buildAddsLayer )
2291
+ if err != nil {
2292
+ return "" , err
2293
+ }
2294
+ if foundMatch {
2207
2295
return image .ID , nil
2208
2296
}
2209
2297
}
0 commit comments