Skip to content
This repository was archived by the owner on Jun 3, 2025. It is now read-only.

Commit 216b14f

Browse files
authored
Merge pull request #289 from bobcatfish/copy_copy_copy
Always snapshot files in COPY and RUN commands
2 parents 3603900 + 7f64037 commit 216b14f

13 files changed

Lines changed: 274 additions & 96 deletions

File tree

README.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ We do **not** recommend running the kaniko executor binary in another image, as
2929
- [Security](#security)
3030
- [Comparison with Other Tools](#comparison-with-other-tools)
3131
- [Community](#community)
32+
- [Limitations](#limitations)
3233

3334
_If you are interested in contributing to kaniko, see [DEVELOPMENT.md](DEVELOPMENT.md) and [CONTRIBUTING.md](CONTRIBUTING.md)._
3435

@@ -256,7 +257,8 @@ To configure credentials, you will need to do the following:
256257
#### --snapshotMode
257258

258259
You can set the `--snapshotMode=<full (default), time>` flag to set how kaniko will snapshot the filesystem.
259-
If `--snapshotMode=time` is set, only file mtime will be considered when snapshotting.
260+
If `--snapshotMode=time` is set, only file mtime will be considered when snapshotting (see
261+
[limitations related to mtime](#mtime-and-snapshotting)).
260262

261263
#### --build-arg
262264

@@ -356,3 +358,23 @@ provides.
356358
[kaniko-users](https://groups.google.com/forum/#!forum/kaniko-users) Google group
357359

358360
To Contribute to kaniko, see [DEVELOPMENT.md](DEVELOPMENT.md) and [CONTRIBUTING.md](CONTRIBUTING.md).
361+
362+
## Limitations
363+
364+
### mtime and snapshotting
365+
366+
When taking a snapshot, kaniko's hashing algorithms include (or in the case of
367+
[`--snapshotMode=time`](#--snapshotmode), only use) a file's
368+
[`mtime`](https://en.wikipedia.org/wiki/Inode#POSIX_inode_description) to determine
369+
if the file has changed. Unfortunately there is a delay between when changes to a
370+
file are made and when the `mtime` is updated. This means:
371+
372+
* With the time-only snapshot mode (`--snapshotMode=time`), kaniko may miss changes
373+
introduced by `RUN` commands entirely.
374+
* With the default snapshot mode (`--snapshotMode=full`), whether or not kaniko will
375+
add a layer in the case where a `RUN` command modifies a file **but the contents do
376+
not** change is theoretically non-deterministic. This _does not affect the contents_
377+
which will still be correct, but it does affect the number of layers.
378+
379+
_Note that these issues are currently theoretical only. If you see this issue occur, please
380+
[open an issue](https://github.com/GoogleContainerTools/kaniko/issues)._
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
FROM alpine@sha256:5ce5f501c457015c4b91f91a15ac69157d9b06f1a75cf9107bf2b62e0843983a
2+
COPY context/foo /foo
3+
COPY context/foo /foo
4+
COPY context/foo /foo
5+
COPY context/foo /foo
6+
COPY context/foo /foo
7+
COPY context/foo /foo
8+
COPY context/foo /foo
9+
COPY context/foo /foo
10+
COPY context/foo /foo
11+
COPY context/foo /foo
12+
COPY context/foo /foo
13+
COPY context/foo /foo
14+
COPY context/foo /foo
15+
COPY context/foo /foo
16+
COPY context/foo /foo
17+
COPY context/foo /foo
18+
COPY context/foo /foo
19+
COPY context/foo /foo
20+
COPY context/foo /foo
21+
COPY context/foo /foo
22+
COPY context/foo /foo
23+
COPY context/foo /foo
24+
COPY context/foo /foo
25+
COPY context/foo /foo
26+
COPY context/foo /foo
27+
COPY context/foo /foo
28+
COPY context/foo /foo
29+
COPY context/foo /foo
30+
COPY context/foo /foo
31+
COPY context/foo /foo
32+
COPY context/foo /foo
33+
COPY context/foo /foo
34+
COPY context/foo /foo
35+
COPY context/foo /foo
36+
COPY context/foo /foo
37+
COPY context/foo /foo
38+
COPY context/foo /foo
39+
COPY context/foo /foo
40+
COPY context/foo /foo
41+
COPY context/foo /foo
42+
COPY context/foo /foo
43+
COPY context/foo /foo
44+
COPY context/foo /foo
45+
COPY context/foo /foo
46+
COPY context/foo /foo
47+
COPY context/foo /foo
48+
COPY context/foo /foo
49+
COPY context/foo /foo

integration/integration_test.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,9 @@ func TestMain(m *testing.M) {
135135
defer DeleteFromBucket(fileInBucket)
136136

137137
fmt.Println("Building kaniko image")
138-
buildKaniko := exec.Command("docker", "build", "-t", ExecutorImage, "-f", "../deploy/Dockerfile", "..")
139-
err = buildKaniko.Run()
140-
if err != nil {
141-
fmt.Print(err)
142-
fmt.Print("Building kaniko failed.")
138+
cmd := exec.Command("docker", "build", "-t", ExecutorImage, "-f", "../deploy/Dockerfile", "..")
139+
if _, err = RunCommandWithoutTest(cmd); err != nil {
140+
fmt.Printf("Building kaniko failed: %s", err)
143141
os.Exit(1)
144142
}
145143

pkg/commands/volume.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package commands
1818

1919
import (
20+
"fmt"
2021
"os"
2122
"strings"
2223

@@ -53,13 +54,14 @@ func (v *VolumeCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.
5354
return err
5455
}
5556

56-
logrus.Infof("Creating directory %s", volume)
57-
if err := os.MkdirAll(volume, 0755); err != nil {
58-
return err
57+
// Only create and snapshot the dir if it didn't exist already
58+
if _, err := os.Stat(volume); os.IsNotExist(err) {
59+
logrus.Infof("Creating directory %s", volume)
60+
v.snapshotFiles = []string{volume}
61+
if err := os.MkdirAll(volume, 0755); err != nil {
62+
return fmt.Errorf("Could not create directory for volume %s: %s", volume, err)
63+
}
5964
}
60-
61-
//Check if directory already exists?
62-
v.snapshotFiles = append(v.snapshotFiles, volume)
6365
}
6466
config.Volumes = existingVolumes
6567

pkg/commands/workdir.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,14 @@ func (w *WorkdirCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile
4747
config.WorkingDir = filepath.Join(config.WorkingDir, resolvedWorkingDir)
4848
}
4949
logrus.Infof("Changed working directory to %s", config.WorkingDir)
50-
w.snapshotFiles = []string{config.WorkingDir}
51-
return os.MkdirAll(config.WorkingDir, 0755)
50+
51+
// Only create and snapshot the dir if it didn't exist already
52+
if _, err := os.Stat(config.WorkingDir); os.IsNotExist(err) {
53+
logrus.Infof("Creating directory %s", config.WorkingDir)
54+
w.snapshotFiles = []string{config.WorkingDir}
55+
return os.MkdirAll(config.WorkingDir, 0755)
56+
}
57+
return nil
5258
}
5359

5460
// FilesToSnapshot returns the workingdir, which should have been created if it didn't already exist

pkg/executor/build.go

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -87,23 +87,41 @@ func DoBuild(opts *options.KanikoOptions) (v1.Image, error) {
8787
if err := dockerCommand.ExecuteCommand(&imageConfig.Config, buildArgs); err != nil {
8888
return nil, err
8989
}
90-
// Don't snapshot if it's not the final stage and not the final command
91-
// Also don't snapshot if it's the final stage, not the final command, and single snapshot is set
92-
if (!finalStage && !finalCmd) || (finalStage && !finalCmd && opts.SingleSnapshot) {
93-
continue
94-
}
95-
// Now, we get the files to snapshot from this command and take the snapshot
9690
snapshotFiles := dockerCommand.FilesToSnapshot()
97-
if finalCmd {
98-
snapshotFiles = nil
91+
var contents []byte
92+
93+
// If this is an intermediate stage, we only snapshot for the last command and we
94+
// want to snapshot the entire filesystem since we aren't tracking what was changed
95+
// by previous commands.
96+
if !finalStage {
97+
if finalCmd {
98+
contents, err = snapshotter.TakeSnapshotFS()
99+
}
100+
} else {
101+
// If we are in single snapshot mode, we only take a snapshot once, after all
102+
// commands have completed.
103+
if opts.SingleSnapshot {
104+
if finalCmd {
105+
contents, err = snapshotter.TakeSnapshotFS()
106+
}
107+
} else {
108+
// Otherwise, in the final stage we take a snapshot at each command. If we know
109+
// the files that were changed, we'll snapshot those explicitly, otherwise we'll
110+
// check if anything in the filesystem changed.
111+
if snapshotFiles != nil {
112+
contents, err = snapshotter.TakeSnapshot(snapshotFiles)
113+
} else {
114+
contents, err = snapshotter.TakeSnapshotFS()
115+
}
116+
}
99117
}
100-
contents, err := snapshotter.TakeSnapshot(snapshotFiles)
101118
if err != nil {
102-
return nil, err
119+
return nil, fmt.Errorf("Error taking snapshot of files for command %s: %s", dockerCommand, err)
103120
}
121+
104122
util.MoveVolumeWhitelistToWhitelist()
105123
if contents == nil {
106-
logrus.Info("No files were changed, appending empty layer to config.")
124+
logrus.Info("No files were changed, appending empty layer to config. No layer added to image.")
107125
continue
108126
}
109127
// Append the layer to the image

pkg/snapshot/layered_map.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package snapshot
1818

1919
import (
20+
"fmt"
2021
"path/filepath"
2122
"strings"
2223
)
@@ -82,6 +83,20 @@ func (l *LayeredMap) MaybeAddWhiteout(s string) (bool, error) {
8283
return true, nil
8384
}
8485

86+
// Add will add the specified file s to the layered map.
87+
func (l *LayeredMap) Add(s string) error {
88+
newV, err := l.hasher(s)
89+
if err != nil {
90+
return fmt.Errorf("Error creating hash for %s: %s", s, err)
91+
}
92+
l.layers[len(l.layers)-1][s] = newV
93+
return nil
94+
}
95+
96+
// MaybeAdd will add the specified file s to the layered map if
97+
// the layered map's hashing function determines it has changed. If
98+
// it has not changed, it will not be added. Returns true if the file
99+
// was added.
85100
func (l *LayeredMap) MaybeAdd(s string) (bool, error) {
86101
oldV, ok := l.Get(s)
87102
newV, err := l.hasher(s)

0 commit comments

Comments
 (0)