Skip to content

Commit

Permalink
feat: enable scaffolding for host builds
Browse files Browse the repository at this point in the history
- Refactors out scaffolding to its own package
- Adds additional scaffolding tests
- Replaces the mock scaffolded functoin with actual code
- Fixes a bug where local jobs were not receiving context cancellation
- Fixes a bug where signatures of instanced HTTP was erroring
- Adds a sleep/wait for tcp listener when starting host jobs
  • Loading branch information
lkingland committed May 23, 2023
1 parent fa317ba commit 1ee740a
Show file tree
Hide file tree
Showing 14 changed files with 416 additions and 288 deletions.
56 changes: 13 additions & 43 deletions pkg/functions/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import (
"time"

"gopkg.in/yaml.v2"

"knative.dev/func/pkg/scaffolding"
"knative.dev/func/pkg/utils"
)

Expand Down Expand Up @@ -414,10 +416,10 @@ func (c *Client) Runtimes() ([]string, error) {
// create a running function whose source code and metadata match that provided
// by the passed function instance, returning the final route and any errors.
func (c *Client) Apply(ctx context.Context, f Function) (string, Function, error) {
if !f.Initialized() {
return c.New(ctx, f)
} else {
if f.Initialized() {
return c.Update(ctx, f)
} else {
return c.New(ctx, f)
}
}

Expand All @@ -426,7 +428,8 @@ func (c *Client) Apply(ctx context.Context, f Function) (string, Function, error
// Updates a function which has already been initialized to run the latest
// source code.
//
// Use Init, Build, Push and Deploy independently for lower level control.
// Use Apply for higher level control. Use Init, Build, Push and Deploy
// independently for lower level control.
// Returns final primary route to the Function and any errors.
func (c *Client) Update(ctx context.Context, f Function) (string, Function, error) {
if !f.Initialized() {
Expand All @@ -451,7 +454,8 @@ func (c *Client) Update(ctx context.Context, f Function) (string, Function, erro
// Function. Used by Apply when the path is not yet an initialized function.
// Errors if the path is alrady an initialized function.
//
// Use Init, Build, Push, Deploy etc. independently for lower level control.
// Use Apply for higher level control. Use Init, Build, Push, Deploy
// independently for lower level control.
// Returns the primary route to the function or error.
func (c *Client) New(ctx context.Context, cfg Function) (string, Function, error) {
c.progressListener.SetTotal(3)
Expand Down Expand Up @@ -627,49 +631,15 @@ func (c *Client) Build(ctx context.Context, f Function) (Function, error) {
// It also updates the included symlink to function source 'f' to point to
// the current function's source.
func (c *Client) Scaffold(ctx context.Context, f Function, dest string) (err error) {
// First get a reference to the repository containing the scaffolding to use
//
// TODO: In order to support extensible scaffolding from external repositories,
// Retain the repository reference from which a Function was initialized
// in order to re-read out its scaffolding later. This can be the locally-
// installed repository name or the remote reference URL. There are benefits
// and detriments either way. A third option would be to store the
// scaffolding locally, but this also has downsides.
//
// If function creatd from a local repository named:
// repo = repoFromURL(f.RepoURL)
// If function created from a remote reference:
// c.Repositories().Get(f.RepoName)
// If function not created from an external repository:
repo, err := c.Repositories().Get(DefaultRepositoryName)
if err != nil {
return
}

// Detect the method signature
s, err := functionSignature(f)
repo, err := NewRepository("", "") // default (embedded) repository
if err != nil {
return
}

// Write Scaffolding from the Repository into the destination
if err = repo.WriteScaffolding(ctx, f, s, dest); err != nil {
return
}

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

// printBuildActivity is a helper for ensuring the user gets feedback from
// the long task of containerized builds.
func (c *Client) printBuildActivity(ctx context.Context) {
m := []string{
"Still building",
Expand Down
3 changes: 3 additions & 0 deletions pkg/functions/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ func NewJob(f Function, host, port string, errs chan error, onStop func() error,
if j.Errors == nil {
j.Errors = make(chan error, 1)
}
if j.onStop == nil {
j.onStop = func() error { return nil }
}
if err = cleanupJobDirs(j); err != nil {
return
}
Expand Down
30 changes: 5 additions & 25 deletions pkg/functions/repository.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package functions

import (
"context"
"errors"
"fmt"
"net/url"
Expand Down Expand Up @@ -156,6 +155,11 @@ func NewRepository(name, uri string) (r Repository, err error) {
return
}

// FS returns the underlying filesystem of this repository.
func (r Repository) FS() filesystem.Filesystem {
return r.fs
}

// filesystemFromURI returns a filesystem from the data located at the
// given URI. If URI is not provided, indicates the embedded repo should
// be loaded. URI can be a remote git repository (http:// https:// etc.),
Expand Down Expand Up @@ -525,30 +529,6 @@ func (r *Repository) Write(dest string) (err error) {
return filesystem.CopyFromFS(".", dest, fs)
}

// WriteScaffolding code to the given path.
//
// Scaffolding is a language-level operation which first detects the method
// signature used by the function's source code and then writes the
// appropriate scaffolding.
//
// NOTE: Scaffoding is not per-template, because a template is merely an
// example starting point for a Function implementation and should have no
// bearing on the shape that function can eventually take. The language,
// and optionally invocation hint (For cloudevents) are used for this. For
// example, there can be multiple templates which exemplify a given method
// signature, and the implementation can be switched at any time by the author.
// Language, by contrast, is fixed at time of initialization.
func (r *Repository) WriteScaffolding(ctx context.Context, f Function, s Signature, dest string) error {
if r.fs == nil {
return errors.New("repository has no filesystem")
}
path := fmt.Sprintf("%v/scaffolding/%v", f.Runtime, s.String()) // fs uses / on all OSs
if _, err := r.fs.Stat(path); err != nil {
return fmt.Errorf("no scaffolding found for '%v' signature '%v'. %v.", f.Runtime, s, err)
}
return filesystem.CopyFromFS(path, dest, r.fs)
}

// URL attempts to read the remote git origin URL of the repository. Best
// effort; returns empty string if the repository is not a git repo or the repo
// has been mutated beyond recognition on disk (ex: removing the origin remote)
Expand Down
3 changes: 2 additions & 1 deletion pkg/functions/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ func runGo(ctx context.Context, job *Job) (err error) {
// cmd.Cancel = stop // TODO: use when we upgrade to go 1.20
if job.Host != "127.0.0.1" {
// TODO: Update the functions go runtime to accept LISTEN_ADDRESS rather
// than just port
// than just port in able to allow listening on other interfaces
// (keeping the default localhost only)
fmt.Fprintf(os.Stderr, "Warning: the Go functions runtime currently only supports localhost '127.0.0.1'. Requested listen interface '%v' will be ignored.", job.Host)
}
// See the 1.19 [release notes](https://tip.golang.org/doc/go1.19) which state:
Expand Down
5 changes: 5 additions & 0 deletions pkg/functions/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ func TestRunner(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer func() {
if err := job.Stop(); err != nil {
t.Fatalf("error on job stop: %v", err)
}
}()

// Invoke
resp, err := http.Get(fmt.Sprintf("http://%s:%s", job.Host, job.Port))
Expand Down
147 changes: 0 additions & 147 deletions pkg/functions/signatures.go

This file was deleted.

26 changes: 10 additions & 16 deletions pkg/oci/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
v1 "github.com/google/go-containerregistry/pkg/v1"

fn "knative.dev/func/pkg/functions"
"knative.dev/func/pkg/scaffolding"
)

var path = filepath.Join
Expand Down Expand Up @@ -73,26 +74,19 @@ func (b *Builder) Build(ctx context.Context, f fn.Function) (err error) {
}
defer teardown(cfg)

//TODO: Use scaffold package when merged:
/*
if err = scaffolding.Scaffold(ctx, f, cfg.buildDir()); err != nil {
return
}
*/
// IN the meantime, use an airball mainfile
data := `
package main
import "fmt"
// Load the embedded repository
repo, err := fn.NewRepository("", "")
if err != nil {
return
}

func main () {
fmt.Println("Hello, world!")
}
`
if err = os.WriteFile(path(cfg.buildDir(), "main.go"), []byte(data), 0664); err != nil {
// Write out the scaffolding
err = scaffolding.Write(cfg.buildDir(), f.Root, f.Runtime, f.Invoke, repo.FS())
if err != nil {
return
}

// Create an OCI container from the scaffolded function
if err = containerize(cfg); err != nil {
return
}
Expand Down
Loading

0 comments on commit 1ee740a

Please sign in to comment.