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:
parent
2d12e28795
commit
a3656d1441
1
cmd/ko/test/kodata/HEAD
Symbolic link
1
cmd/ko/test/kodata/HEAD
Symbolic link
@ -0,0 +1 @@
|
||||
../../../../.git/HEAD
|
1
cmd/ko/test/kodata/refs
Symbolic link
1
cmd/ko/test/kodata/refs
Symbolic link
@ -0,0 +1 @@
|
||||
../../../../.git/refs
|
@ -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))
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user