Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: host builder nonregular file support #2156

Merged
merged 12 commits into from
Feb 20, 2024
106 changes: 83 additions & 23 deletions pkg/oci/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,64 @@ func TestBuilder_Build(t *testing.T) {

last := path(f.Root, fn.RunDataDir, "builds", "last", "oci")

validateOCI(last, t)
validateOCIStructure(last, t) // validate it adheres to the basics of the OCI spec
}

// TestBuilder_Files ensures that static files are added to the container
// image as expected. This includes template files, regular files and links.
func TestBuilder_Files(t *testing.T) {
root, done := Mktemp(t)
defer done()

// Create a function with the default template
f, err := fn.New().Init(fn.Function{Root: root, Runtime: "go"})
if err != nil {
t.Fatal(err)
}

// Add a regular file
if err := os.WriteFile("a.txt", []byte("file a"), 0644); err != nil {
t.Fatal(err)
}

// Links
var linkMode fs.FileMode
var linkExecutable bool
if runtime.GOOS != "windows" {
// Default: create a symlink
linkMode = fs.ModeSymlink
linkExecutable = true
if err := os.Symlink("a.txt", "a.lnk"); err != nil {
t.Fatal(err)
}
} else {
// Windows: create a copy
if err := os.WriteFile("a.lnk", []byte("file a"), 0644); err != nil {
t.Fatal(err)
}
}

if err := NewBuilder("", true).Build(context.Background(), f, TestPlatforms); err != nil {
t.Fatal(err)
}

expected := []fileInfo{
{Path: "/etc/pki/tls/certs/ca-certificates.crt"},
{Path: "/etc/ssl/certs/ca-certificates.crt"},
{Path: "/func", Type: fs.ModeDir},
{Path: "/func/README.md"},
{Path: "/func/a.lnk", Executable: linkExecutable, Type: linkMode},
{Path: "/func/a.txt"},
{Path: "/func/f", Executable: true},
{Path: "/func/func.yaml"},
{Path: "/func/go.mod"},
{Path: "/func/handle.go"},
{Path: "/func/handle_test.go"},
}

last := path(f.Root, fn.RunDataDir, "builds", "last", "oci")

validateOCIFiles(last, expected, t)
}

// TestBuilder_Concurrency
Expand Down Expand Up @@ -145,9 +202,9 @@ type ImageIndex struct {
} `json:"manifests"`
}

// validateOCI performs a cursory check that the given path exists and
// validateOCIStructue performs a cursory check that the given path exists and
// has the basics of an OCI compliant structure.
func validateOCI(path string, t *testing.T) {
func validateOCIStructure(path string, t *testing.T) {
if _, err := os.Stat(path); err != nil {
t.Fatalf("unable to stat output path. %v", path)
return
Expand Down Expand Up @@ -185,9 +242,24 @@ func validateOCI(path string, t *testing.T) {
}

if len(imageIndex.Manifests) < 1 {
t.Fatal("fewer manifests")
t.Fatal("no manifests")
}
}

// validateOCIFiles ensures that the OCI image at path contains files with
// the given attributes.
func validateOCIFiles(path string, expected []fileInfo, t *testing.T) {
// Load the Image Index
bb, err := os.ReadFile(filepath.Join(path, "index.json"))
if err != nil {
t.Fatalf("failed to read index.json: %v", err)
}
var imageIndex ImageIndex
if err = json.Unmarshal(bb, &imageIndex); err != nil {
t.Fatalf("failed to parse index.json: %v", err)
}

// Load the first manifest
digest := strings.TrimPrefix(imageIndex.Manifests[0].Digest, "sha256:")
manifestFile := filepath.Join(path, "blobs", "sha256", digest)
manifestFileData, err := os.ReadFile(manifestFile)
Expand All @@ -203,12 +275,6 @@ func validateOCI(path string, t *testing.T) {
if err != nil {
t.Fatal(err)
}

type fileInfo struct {
Path string
Type fs.FileMode
Executable bool
}
var files []fileInfo

for _, layer := range mf.Layers {
Expand Down Expand Up @@ -247,19 +313,13 @@ func validateOCI(path string, t *testing.T) {
return files[i].Path < files[j].Path
})

expectedFiles := []fileInfo{
{Path: "/etc/pki/tls/certs/ca-certificates.crt"},
{Path: "/etc/ssl/certs/ca-certificates.crt"},
{Path: "/func", Type: fs.ModeDir},
{Path: "/func/README.md"},
{Path: "/func/f", Executable: true},
{Path: "/func/func.yaml"},
{Path: "/func/go.mod"},
{Path: "/func/handle.go"},
{Path: "/func/handle_test.go"},
}

if diff := cmp.Diff(expectedFiles, files); diff != "" {
if diff := cmp.Diff(expected, files); diff != "" {
t.Error("files in oci differ from expectation (-want, +got):", diff)
}
}

type fileInfo struct {
Path string
Type fs.FileMode
Executable bool
lkingland marked this conversation as resolved.
Show resolved Hide resolved
}
Loading