Skip to content

Commit fd39e0f

Browse files
committed
Build embedded functions relative to a single project filesystem
The function build path threaded two filesystems through its call chain: the project root, and a separate afero.BasePathFs rooted at the project's functions directory. Both pointed at the same underlying tree, so most function operations were addressed relative to the functions directory while runtime tarballs and the language builder's ProjectFS were addressed relative to the project root. Carrying both meant every function in the chain took two afero.Fs arguments, and the per-function builder filesystem was a BasePathFs wrapped over another BasePathFs. This change drops the functions-rooted filesystem and addresses everything relative to the project root, joining spec.paths.functions inline where the functions directory was previously the root. The build chain now takes a single afero.Fs, and the per-function builder filesystem wraps the project filesystem once instead of twice. Signed-off-by: Nic Cope <nicc@rk0n.org>
1 parent f8863ca commit fd39e0f

2 files changed

Lines changed: 19 additions & 23 deletions

File tree

internal/project/build.go

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -183,12 +183,10 @@ func (b *realBuilder) Build(ctx context.Context, project *devv1alpha1.Project, p
183183
}
184184
}
185185

186-
functionsSource := afero.NewBasePathFs(projectFS, project.Spec.Paths.Functions)
187-
188186
// Determine the set of functions to build. If the project explicitly
189187
// declares a Functions list we use it verbatim. Otherwise we auto-discover
190188
// by listing subdirectories of the functions path.
191-
fns, err := resolveFunctions(project, functionsSource)
189+
fns, err := resolveFunctions(project, projectFS)
192190
if err != nil {
193191
return nil, errors.Wrap(err, "failed to resolve functions")
194192
}
@@ -262,7 +260,7 @@ func (b *realBuilder) Build(ctx context.Context, project *devv1alpha1.Project, p
262260

263261
// Build the resolved functions.
264262
o.log.Debug("Building functions")
265-
imgMap, deps, err := b.buildFunctions(ctx, projectFS, functionsSource, project, fns, o.projectBasePath, o.eventCh)
263+
imgMap, deps, err := b.buildFunctions(ctx, projectFS, project, fns, o.projectBasePath, o.eventCh)
266264
if err != nil {
267265
return nil, err
268266
}
@@ -317,12 +315,12 @@ func (b *realBuilder) Build(ctx context.Context, project *devv1alpha1.Project, p
317315
// the project explicitly declares functions, that list is returned verbatim.
318316
// Otherwise it auto-discovers Directory-source functions by listing
319317
// subdirectories of the project's functions path.
320-
func resolveFunctions(project *devv1alpha1.Project, functionsSource afero.Fs) ([]devv1alpha1.Function, error) {
318+
func resolveFunctions(project *devv1alpha1.Project, projectFS afero.Fs) ([]devv1alpha1.Function, error) {
321319
if len(project.Spec.Functions) > 0 {
322320
return project.Spec.Functions, nil
323321
}
324322

325-
infos, err := afero.ReadDir(functionsSource, "/")
323+
infos, err := afero.ReadDir(projectFS, project.Spec.Paths.Functions)
326324
switch {
327325
case os.IsNotExist(err):
328326
return nil, nil
@@ -344,7 +342,7 @@ func resolveFunctions(project *devv1alpha1.Project, functionsSource afero.Fs) ([
344342
}
345343

346344
// buildFunctions builds the given list of embedded functions.
347-
func (b *realBuilder) buildFunctions(ctx context.Context, projectFS, fromFS afero.Fs, project *devv1alpha1.Project, fns []devv1alpha1.Function, basePath string, eventCh async.EventChannel) (ImageTagMap, []xpmetav1.Dependency, error) {
345+
func (b *realBuilder) buildFunctions(ctx context.Context, projectFS afero.Fs, project *devv1alpha1.Project, fns []devv1alpha1.Function, basePath string, eventCh async.EventChannel) (ImageTagMap, []xpmetav1.Dependency, error) {
348346
var (
349347
imgMap = make(map[name.Tag]v1.Image)
350348
imgMu sync.Mutex
@@ -366,7 +364,7 @@ func (b *realBuilder) buildFunctions(ctx context.Context, projectFS, fromFS afer
366364
eventCh.SendEvent(eventText, async.EventStatusStarted)
367365

368366
fnRepo := fmt.Sprintf("%s_%s", project.Spec.Repository, fnName)
369-
imgs, err := b.buildFunction(ctx, projectFS, fromFS, project, fn, basePath)
367+
imgs, err := b.buildFunction(ctx, projectFS, project, fn, basePath)
370368
if err != nil {
371369
eventCh.SendEvent(eventText, async.EventStatusFailure)
372370
return errors.Wrapf(err, "failed to build function %q", fnName)
@@ -419,7 +417,7 @@ func (b *realBuilder) buildFunctions(ctx context.Context, projectFS, fromFS afer
419417
// buildFunction builds the package images for a single function. It resolves
420418
// the function's runtime images (either by building from source or by loading
421419
// a pre-built tarball) and then wraps each one with the package metadata.
422-
func (b *realBuilder) buildFunction(ctx context.Context, projectFS, functionsFS afero.Fs, project *devv1alpha1.Project, fn devv1alpha1.Function, basePath string) ([]v1.Image, error) {
420+
func (b *realBuilder) buildFunction(ctx context.Context, projectFS afero.Fs, project *devv1alpha1.Project, fn devv1alpha1.Function, basePath string) ([]v1.Image, error) {
423421
fnName := fn.Name()
424422
meta := &xpmetav1.Function{
425423
TypeMeta: metav1.TypeMeta{
@@ -451,19 +449,18 @@ func (b *realBuilder) buildFunction(ctx context.Context, projectFS, functionsFS
451449
// directory under functions/, so they have no examples to ship.
452450
examplesParser := parser.NewEchoBackend("")
453451
if fn.Source == devv1alpha1.FunctionSourceDirectory {
454-
// Resolve the examples directory relative to functionsFS rather than
455-
// wrapping it in another BasePathFs - it joins the path once here
456-
// instead of on every file operation, and keeps the path visible to
457-
// readers.
458-
examplesDir := filepath.Join(fn.Directory.Name, "examples")
459-
examplesExist, err := afero.IsDir(functionsFS, examplesDir)
452+
// Resolve the examples directory relative to the project root, so we
453+
// address it through the same filesystem as everything else rather
454+
// than wrapping a separate functions-rooted BasePathFs.
455+
examplesDir := filepath.Join(project.Spec.Paths.Functions, fn.Directory.Name, "examples")
456+
examplesExist, err := afero.IsDir(projectFS, examplesDir)
460457
switch {
461458
case err == nil, os.IsNotExist(err):
462459
default:
463460
return nil, errors.Wrap(err, "failed to check for examples")
464461
}
465462
if examplesExist {
466-
examplesParser = parser.NewFsBackend(functionsFS,
463+
examplesParser = parser.NewFsBackend(projectFS,
467464
parser.FsDir(examplesDir),
468465
parser.FsFilters(parser.SkipNotYAML()),
469466
)
@@ -481,7 +478,7 @@ func (b *realBuilder) buildFunction(ctx context.Context, projectFS, functionsFS
481478
examples.New(),
482479
)
483480

484-
runtimeImages, err := b.runtimeImages(ctx, projectFS, functionsFS, project, fn, basePath)
481+
runtimeImages, err := b.runtimeImages(ctx, projectFS, project, fn, basePath)
485482
if err != nil {
486483
return nil, err
487484
}
@@ -501,10 +498,10 @@ func (b *realBuilder) buildFunction(ctx context.Context, projectFS, functionsFS
501498
// runtimeImages returns the per-architecture runtime images for a function. For
502499
// Directory-source functions this dispatches to the appropriate builder. For
503500
// Tarball-source functions it loads the supplied OCI tarball.
504-
func (b *realBuilder) runtimeImages(ctx context.Context, projectFS, functionsFS afero.Fs, project *devv1alpha1.Project, fn devv1alpha1.Function, basePath string) ([]v1.Image, error) {
501+
func (b *realBuilder) runtimeImages(ctx context.Context, projectFS afero.Fs, project *devv1alpha1.Project, fn devv1alpha1.Function, basePath string) ([]v1.Image, error) {
505502
switch fn.Source {
506503
case devv1alpha1.FunctionSourceDirectory:
507-
return b.buildDirectoryRuntime(ctx, projectFS, functionsFS, project, fn.Directory, basePath)
504+
return b.buildDirectoryRuntime(ctx, projectFS, project, fn.Directory, basePath)
508505
case devv1alpha1.FunctionSourceTarball:
509506
return loadTarballRuntime(projectFS, fn.Tarball, project.Spec.Architectures)
510507
default:
@@ -515,8 +512,8 @@ func (b *realBuilder) runtimeImages(ctx context.Context, projectFS, functionsFS
515512

516513
// buildDirectoryRuntime invokes the appropriate language builder to produce
517514
// runtime images from a function's source directory.
518-
func (b *realBuilder) buildDirectoryRuntime(ctx context.Context, projectFS, functionsFS afero.Fs, project *devv1alpha1.Project, dir *devv1alpha1.FunctionDirectory, basePath string) ([]v1.Image, error) {
519-
fnFS := afero.NewBasePathFs(functionsFS, dir.Name)
515+
func (b *realBuilder) buildDirectoryRuntime(ctx context.Context, projectFS afero.Fs, project *devv1alpha1.Project, dir *devv1alpha1.FunctionDirectory, basePath string) ([]v1.Image, error) {
516+
fnFS := afero.NewBasePathFs(projectFS, filepath.Join(project.Spec.Paths.Functions, dir.Name))
520517

521518
fnBasePath := ""
522519
if basePath != "" {

internal/project/build_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,7 @@ func TestResolveFunctions(t *testing.T) {
377377
proj.Spec.Repository = "xpkg.crossplane.io/example/test"
378378
proj.Default()
379379

380-
fnsSource := afero.NewBasePathFs(projFS, proj.Spec.Paths.Functions)
381-
got, err := resolveFunctions(proj, fnsSource)
380+
got, err := resolveFunctions(proj, projFS)
382381
if err != nil {
383382
t.Fatalf("resolveFunctions: %v", err)
384383
}

0 commit comments

Comments
 (0)