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 174f47a commit 0480b8e
Show file tree
Hide file tree
Showing 16 changed files with 420 additions and 292 deletions.
5 changes: 2 additions & 3 deletions cmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@ func runBuild(cmd *cobra.Command, _ []string, newClient ClientFactory) (err erro
}

// Client
// Concrete implementations (ex builder) vary based on final effective config
oo, err := cfg.options()
if err != nil {
return
Expand Down Expand Up @@ -340,8 +339,8 @@ func (c buildConfig) Validate() (err error) {
return
}

// options returns options suitable for instantiating a client based on the
// current state of the build config object.
// options returns client invocation options based on the values in config.
//
// This will be unnecessary and refactored away when the host-based OCI
// builder and pusher are the default implementations and the Pack and S2I
// constructors simplified.
Expand Down
3 changes: 2 additions & 1 deletion cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ func runDeploy(cmd *cobra.Command, newClient ClientFactory) (err error) {
// Informative non-error messages regarding the final deployment request
printDeployMessages(cmd.OutOrStdout(), cfg)

// Client
oo, err := cfg.options()
if err != nil {
return
Expand Down Expand Up @@ -304,7 +305,7 @@ func build(cmd *cobra.Command, flag string, f fn.Function, client *fn.Client) (f
return f, err
}
}
} else if build, _ := strconv.ParseBool(flag); build == true {
} else if build, _ := strconv.ParseBool(flag); build {
if f, err = client.Build(cmd.Context(), f); err != nil {
return f, err
}
Expand Down
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.

Loading

0 comments on commit 0480b8e

Please sign in to comment.