1
0
mirror of https://github.com/ko-build/ko.git synced 2025-03-03 15:32:20 +02:00

Add support for recursively resolving directory symlinks. (#73)

* Add support for recursively resolving directory symlinks.

This adds support for properly resolving directory symlinks within kodata.
I verified that with this I can symlink `.git/refs` into `kodata/` and (with
changes) resolve the `.git/HEAD` symlink to read the appropriate
`ref: refs/heads/...` files with the commit SHA.

* Incorporate code review feedback
This commit is contained in:
Matt Moore 2019-08-15 17:59:15 -07:00 committed by GitHub
parent 2d12e28795
commit a3656d1441
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 80 additions and 64 deletions

1
cmd/ko/test/kodata/HEAD Symbolic link
View File

@ -0,0 +1 @@
../../../../.git/HEAD

1
cmd/ko/test/kodata/refs Symbolic link
View File

@ -0,0 +1 @@
../../../../.git/refs

View File

@ -29,4 +29,11 @@ func main() {
log.Fatalf("Error reading %q: %v", file, err)
}
log.Printf(string(bytes))
file = filepath.Join(dp, "refs/heads/master")
bytes, err = ioutil.ReadFile(file)
if err != nil {
log.Fatalf("Error reading %q: %v", file, err)
}
log.Printf(string(bytes))
}

View File

@ -20,5 +20,5 @@ metadata:
spec:
containers:
- name: obiwan
image: github.com/google/ko/cmd/test
image: github.com/google/ko/cmd/ko/test
restartPolicy: Never

View File

@ -276,6 +276,70 @@ func (g *gobuild) kodataPath(s string) (string, error) {
// Where kodata lives in the image.
const kodataRoot = "/var/run/ko"
// walkRecursive performs a filepath.Walk of the given root directory adding it
// to the provided tar.Writer with root -> chroot. All symlinks are dereferenced,
// which is what leads to recursion when we encounter a directory symlink.
func walkRecursive(tw *tar.Writer, root, chroot string) error {
return filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if path == root {
// Add an entry for the root directory of our walk.
return tw.WriteHeader(&tar.Header{
Name: chroot,
Typeflag: tar.TypeDir,
// Use a fixed Mode, so that this isn't sensitive to the directory and umask
// under which it was created. Additionally, windows can only set 0222,
// 0444, or 0666, none of which are executable.
Mode: 0555,
})
}
if err != nil {
return err
}
// Skip other directories.
if info.Mode().IsDir() {
return nil
}
newPath := filepath.Join(chroot, path[len(root):])
path, err = filepath.EvalSymlinks(path)
if err != nil {
return err
}
// Chase symlinks.
info, err = os.Stat(path)
if err != nil {
return err
}
// Skip other directories.
if info.Mode().IsDir() {
return walkRecursive(tw, path, newPath)
}
// Open the file to copy it into the tarball.
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
// Copy the file into the image tarball.
if err := tw.WriteHeader(&tar.Header{
Name: newPath,
Size: info.Size(),
Typeflag: tar.TypeReg,
// Use a fixed Mode, so that this isn't sensitive to the directory and umask
// under which it was created. Additionally, windows can only set 0222,
// 0444, or 0666, none of which are executable.
Mode: 0555,
}); err != nil {
return err
}
_, err = io.Copy(tw, file)
return err
})
}
func (g *gobuild) tarKoData(importpath string) (*bytes.Buffer, error) {
buf := bytes.NewBuffer(nil)
// Compress this before calling tarball.LayerFromOpener, since it eagerly
@ -293,60 +357,7 @@ func (g *gobuild) tarKoData(importpath string) (*bytes.Buffer, error) {
return nil, err
}
err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if path == root {
// Add an entry for /var/run/ko
return tw.WriteHeader(&tar.Header{
Name: kodataRoot,
Typeflag: tar.TypeDir,
// Use a fixed Mode, so that this isn't sensitive to the directory and umask
// under which it was created. Additionally, windows can only set 0222,
// 0444, or 0666, none of which are executable.
Mode: 0555,
})
}
if err != nil {
return err
}
// Skip other directories.
if info.Mode().IsDir() {
return nil
}
// Chase symlinks.
info, err = os.Stat(path)
if err != nil {
return err
}
// Open the file to copy it into the tarball.
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
// Copy the file into the image tarball.
newPath := filepath.Join(kodataRoot, path[len(root):])
if err := tw.WriteHeader(&tar.Header{
Name: newPath,
Size: info.Size(),
Typeflag: tar.TypeReg,
// Use a fixed Mode, so that this isn't sensitive to the directory and umask
// under which it was created. Additionally, windows can only set 0222,
// 0444, or 0666, none of which are executable.
Mode: 0555,
}); err != nil {
return err
}
_, err = io.Copy(tw, file)
return err
})
if err != nil {
return nil, err
}
return buf, nil
return buf, walkRecursive(tw, root, kodataRoot)
}
// Build implements build.Interface

View File

@ -253,19 +253,15 @@ func TestGoBuild(t *testing.T) {
})
t.Run("check app layer contents", func(t *testing.T) {
expectedHash := v1.Hash{
Algorithm: "sha256",
Hex: "4379f30a6c6f66221c3c54dddd378fcfa5a7304a6655ff783b102069c0f943ab",
}
appLayer := ls[baseLayers]
dataLayer := ls[baseLayers]
if got, err := appLayer.Digest(); err != nil {
if _, err := dataLayer.Digest(); err != nil {
t.Errorf("Digest() = %v", err)
} else if got != expectedHash {
t.Errorf("Digest() = %v, want %v", got, expectedHash)
}
// We don't check the data layer here because it includes a symlink of refs and
// will produce a distinct hash each time we commit something.
r, err := appLayer.Uncompressed()
r, err := dataLayer.Uncompressed()
if err != nil {
t.Errorf("Uncompressed() = %v", err)
}