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

Rm f symlink #2548

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18,714 changes: 9,348 additions & 9,366 deletions generate/zz_filesystem_generated.go

Large diffs are not rendered by default.

137 changes: 137 additions & 0 deletions pkg/filesystem/filesystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ package filesystem

import (
"archive/zip"
"bytes"
"errors"
"fmt"
"io"
"io/fs"
"os"
"path"
"path/filepath"
"strings"
"text/template"

billy "github.com/go-git/go-billy/v5"
)
Expand Down Expand Up @@ -253,6 +256,140 @@ func (m maskingFS) Readlink(link string) (string, error) {
return m.fs.Readlink(link)
}

// NewTemplatingFS creates a new filesystem that evaluates/expands Go templates from *.ftmpl files in the baseFS.
// For example if the baseFS contains file go.mod.ftmpl then the new resultant FS will contain go.mod file instead,
// and the file would be result of template execution with templateData as the template data.
func NewTemplatingFS(baseFS Filesystem, templateData any) templatingFS {
return templatingFS{
fs: baseFS,
templateData: templateData,
}
}

type templatingFS struct {
fs Filesystem
templateData any
}

type templatingFI struct {
fs.FileInfo
size int64
}

func (t templatingFI) Size() int64 {
return t.size
}

func (t templatingFI) Name() string {
return strings.TrimSuffix(t.FileInfo.Name(), ".ftmpl")
}

type templatingFile struct {
fi templatingFI
buff bytes.Buffer
}

func (t *templatingFile) Stat() (fs.FileInfo, error) {
return t.fi, nil
}

func (t *templatingFile) Read(i []byte) (int, error) {
return t.buff.Read(i)
}

func (t *templatingFile) Close() error {
return nil
}

func (t templatingFS) Open(name string) (fs.File, error) {
n := name + ".ftmpl"

if _, err := t.fs.Stat(n); errors.Is(err, fs.ErrNotExist) {
return t.fs.Open(name)
}

tmpl, err := template.ParseFS(t.fs, n)
if err != nil {
return nil, err
}

var f templatingFile
err = tmpl.Execute(&f.buff, t.templateData)
if err != nil {
return nil, err
}

fi, err := t.fs.Stat(n)
if err != nil {
return nil, err
}
f.fi = templatingFI{FileInfo: fi, size: int64(f.buff.Len())}

return &f, nil
}

func (t templatingFS) ReadDir(name string) ([]fs.DirEntry, error) {
des, err := t.fs.ReadDir(name)
if err != nil {
return nil, err
}

result := make([]fs.DirEntry, len(des))
for i, de := range des {
var fi fs.FileInfo
if !strings.HasSuffix(de.Name(), ".ftmpl") {
result[i] = de
continue
}

fi, err = t.Stat(path.Join(name, strings.TrimSuffix(de.Name(), ".ftmpl")))
if err != nil {
return nil, err
}
result[i] = dirEntry{fi}

}
return result, nil
}

func (t templatingFS) Stat(name string) (fs.FileInfo, error) {
n := name + ".ftmpl"

fi, err := t.fs.Stat(n)
if errors.Is(err, fs.ErrNotExist) {
return t.fs.Stat(name)
}

var tmpl *template.Template
tmpl, err = template.ParseFS(t.fs, n)
if err != nil {
return nil, err
}

var w wc
err = tmpl.Execute(&w, t.templateData)
if err != nil {
return nil, err
}
return templatingFI{
FileInfo: fi,
size: w.written,
}, nil
}

type wc struct {
written int64
}

func (w *wc) Write(p []byte) (n int, err error) {
w.written += int64(len(p))
return len(p), nil
}

func (t templatingFS) Readlink(link string) (string, error) {
return t.fs.Readlink(link)
}

// CopyFromFS copies files from the `src` dir on the accessor Filesystem to local filesystem into `dest` dir.
// The src path uses slashes as their separator.
// The dest path uses OS specific separator.
Expand Down
68 changes: 68 additions & 0 deletions pkg/filesystem/filesystem_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,74 @@ func TestCopy(t *testing.T) {
}
}

func TestTemplatingFS(t *testing.T) {
tfs := filesystem.NewTemplatingFS(
mockFS{files: []FileInfo{
{
Path: "src",
Typ: fs.ModeDir,
},
{
Path: "src/hello.txt.ftmpl",
Content: []byte("Hi {{.Name}}!"),
},
}},
struct{ Name string }{Name: "John"},
)

expectedContent := "Hi John!"

fi, err := tfs.Stat("src/hello.txt")
if err != nil {
t.Fatal(err)
}
if fi.Size() != int64(len(expectedContent)) {
t.Errorf("size missmatch")
}

des, err := tfs.ReadDir("src")
if err != nil {
t.Fatal(err)
}
if len(des) != 1 {
t.Fatal("expected exactly one item in directory")
}
if des[0].Name() != "hello.txt" {
t.Fatalf("unexpected file: %q", des[0].Name())
}
fi, err = des[0].Info()
if err != nil {
t.Fatal(err)
}
if fi.Size() != int64(len(expectedContent)) {
t.Error("size missmatch")
}

expectedFiles := []FileInfo{
{
Path: ".",
Typ: fs.ModeDir,
},
{
Path: "src",
Typ: fs.ModeDir,
},
{
Path: "src/hello.txt",
Content: []byte(expectedContent),
},
}

actualFiles, err := loadFS(tfs)
if err != nil {
t.Fatal(err)
}

if diff := cmp.Diff(expectedFiles, actualFiles); diff != "" {
t.Error("filesystem content missmatch (-want, +got):", diff)
}
}

// mock for testing symlink functionality
type mockFS struct {
files []FileInfo
Expand Down
25 changes: 13 additions & 12 deletions pkg/scaffolding/scaffold.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package scaffolding

import (
"fmt"
"os"
"path/filepath"

"knative.dev/func/pkg/filesystem"
Expand Down Expand Up @@ -43,6 +42,19 @@ func Write(out, src, runtime, invoke string, fs filesystem.Filesystem) (err erro
return ErrScaffoldingNotFound
}

rel, err := filepath.Rel(out, src)
if err != nil {
return ScaffoldingError{"error determining relative path to function source", err}
}

fs = filesystem.NewTemplatingFS(fs, struct {
ModuleName string
FuncPath string
}{
ModuleName: "function",
FuncPath: rel,
})

// Copy from d -> out from the filesystem
if err := filesystem.CopyFromFS(d, out, fs); err != nil {
return ScaffoldingError{"filesystem copy failed", err}
Expand All @@ -56,17 +68,6 @@ func Write(out, src, runtime, invoke string, fs filesystem.Filesystem) (err erro
return ScaffoldingError{"certs copy failed", err}
}

// Replace the 'f' link of the scaffolding (which is now incorrect) to
// link to the function's root.
rel, err := filepath.Rel(out, src)
if err != nil {
return ScaffoldingError{"error determining relative path to function source", err}
}
link := filepath.Join(out, "f")
_ = os.Remove(link)
if err = os.Symlink(rel, link); err != nil {
return fmt.Errorf("error linking scaffolding to source %w", err)
}
return
}

Expand Down
1 change: 0 additions & 1 deletion templates/go/scaffolding/instanced-cloudevents/f

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module s

replace function => ./f
replace {{.ModuleName}} => {{.FuncPath}}

go 1.21

Expand Down
1 change: 0 additions & 1 deletion templates/go/scaffolding/instanced-http/f

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module s

replace function => ./f
replace {{.ModuleName}} => {{.FuncPath}}

go 1.21

Expand Down
1 change: 0 additions & 1 deletion templates/go/scaffolding/static-cloudevents/f

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module s

replace function => ./f
replace {{.ModuleName}} => {{.FuncPath}}

go 1.21

Expand Down
1 change: 0 additions & 1 deletion templates/go/scaffolding/static-http/f

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module s

replace function => ./f
replace {{.ModuleName}} => {{.FuncPath}}

go 1.21

Expand Down
Loading