src: better ctx propagation (#283)

Signed-off-by: Matej Vasek <mvasek@redhat.com>
This commit is contained in:
Matej Vasek 2021-03-24 16:05:52 +01:00 committed by GitHub
parent 76717ee7d9
commit 923c788f4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 93 additions and 77 deletions

View File

@ -36,7 +36,7 @@ var RuntimeToBuildpack = map[string]string{
}
// Build the Function at path.
func (builder *Builder) Build(f bosonFunc.Function) (err error) {
func (builder *Builder) Build(ctx context.Context, f bosonFunc.Function) (err error) {
// Use the builder found in the Function configuration file
// If one isn't found, use the defaults
@ -86,7 +86,7 @@ func (builder *Builder) Build(f bosonFunc.Function) (err error) {
}
// Build based using the given builder.
if err = packClient.Build(context.Background(), packOpts); err != nil {
if err = packClient.Build(ctx, packOpts); err != nil {
// If the builder was not showing logs, embed the full logs in the error.
if !builder.Verbose {
err = fmt.Errorf("%v\noutput: %s\n", err, logWriter.(*bytes.Buffer).String())

View File

@ -40,7 +40,7 @@ var ErrNotBuilt = errors.New("not built")
// Builder of Function source to runnable image.
type Builder interface {
// Build a Function project with source located at path.
Build(Function) error
Build(context.Context, Function) error
}
// Pusher of Function image to a registry.
@ -53,7 +53,7 @@ type Pusher interface {
// Deployer of Function source to running status.
type Deployer interface {
// Deploy a Function of given name, using given backing image.
Deploy(Function) error
Deploy(context.Context, Function) error
}
// Runner runs the Function locally.
@ -65,13 +65,13 @@ type Runner interface {
// Remover of deployed services.
type Remover interface {
// Remove the Function from remote.
Remove(name string) error
Remove(ctx context.Context, name string) error
}
// Lister of deployed services.
type Lister interface {
// List the Functions currently deployed.
List() ([]ListItem, error)
List(ctx context.Context) ([]ListItem, error)
}
type ListItem struct {
@ -245,7 +245,7 @@ func WithRegistry(registry string) Option {
// New Function.
// Use Create, Build and Deploy independently for lower level control.
func (c *Client) New(cfg Function) (err error) {
func (c *Client) New(ctx context.Context, cfg Function) (err error) {
c.progressListener.SetTotal(3)
defer c.progressListener.Done()
@ -263,14 +263,14 @@ func (c *Client) New(cfg Function) (err error) {
// Build the now-initialized Function
c.progressListener.Increment("Building container image")
if err = c.Build(f.Root); err != nil {
if err = c.Build(ctx, f.Root); err != nil {
return
}
// Deploy the initialized Function, returning its publicly
// addressible name for possible registration.
c.progressListener.Increment("Deploying Function to cluster")
if err = c.Deploy(context.TODO(), f.Root); err != nil {
if err = c.Deploy(ctx, f.Root); err != nil {
return
}
@ -371,7 +371,7 @@ func (c *Client) Create(cfg Function) (err error) {
// Build the Function at path. Errors if the Function is either unloadable or does
// not contain a populated Image.
func (c *Client) Build(path string) (err error) {
func (c *Client) Build(ctx context.Context, path string) (err error) {
fmt.Println("Building function image")
@ -385,7 +385,7 @@ func (c *Client) Build(path string) (err error) {
return
}
if err = c.builder.Build(f); err != nil {
if err = c.builder.Build(ctx, f); err != nil {
return
}
@ -431,7 +431,7 @@ func (c *Client) Deploy(ctx context.Context, path string) (err error) {
// Deploy a new or Update the previously-deployed Function
fmt.Println("Deploying function to the cluster")
return c.deployer.Deploy(f)
return c.deployer.Deploy(ctx, f)
}
func (c *Client) Route(path string) (err error) {
@ -468,9 +468,9 @@ func (c *Client) Run(ctx context.Context, root string) error {
}
// List currently deployed Functions.
func (c *Client) List() ([]ListItem, error) {
func (c *Client) List(ctx context.Context) ([]ListItem, error) {
// delegate to concrete implementation of lister entirely.
return c.lister.List()
return c.lister.List(ctx)
}
// Describe a Function. Name takes precidence. If no name is provided,
@ -494,11 +494,11 @@ func (c *Client) Describe(name, root string) (d Description, err error) {
// Remove a Function. Name takes precidence. If no name is provided,
// the Function defined at root is used if it exists.
func (c *Client) Remove(cfg Function) error {
func (c *Client) Remove(ctx context.Context, cfg Function) error {
// If name is provided, it takes precidence.
// Otherwise load the Function deined at root.
if cfg.Name != "" {
return c.remover.Remove(cfg.Name)
return c.remover.Remove(ctx, cfg.Name)
}
f, err := NewFunction(cfg.Root)
@ -508,7 +508,7 @@ func (c *Client) Remove(cfg Function) error {
if !f.Initialized() {
return fmt.Errorf("Function at %v can not be removed unless initialized. Try removing by name.", f.Root)
}
return c.remover.Remove(f.Name)
return c.remover.Remove(ctx, f.Name)
}
// Manual implementations (noops) of required interfaces.
@ -521,7 +521,7 @@ func (c *Client) Remove(cfg Function) error {
type noopBuilder struct{ output io.Writer }
func (n *noopBuilder) Build(_ Function) error { return nil }
func (n *noopBuilder) Build(ctx context.Context, _ Function) error { return nil }
type noopPusher struct{ output io.Writer }
@ -529,7 +529,7 @@ func (n *noopPusher) Push(ctx context.Context, f Function) (string, error) { ret
type noopDeployer struct{ output io.Writer }
func (n *noopDeployer) Deploy(_ Function) error { return nil }
func (n *noopDeployer) Deploy(ctx context.Context, _ Function) error { return nil }
type noopRunner struct{ output io.Writer }
@ -537,11 +537,11 @@ func (n *noopRunner) Run(_ context.Context, _ Function) error { return nil }
type noopRemover struct{ output io.Writer }
func (n *noopRemover) Remove(string) error { return nil }
func (n *noopRemover) Remove(context.Context, string) error { return nil }
type noopLister struct{ output io.Writer }
func (n *noopLister) List() ([]ListItem, error) { return []ListItem{}, nil }
func (n *noopLister) List(context.Context) ([]ListItem, error) { return []ListItem{}, nil }
type noopDNSProvider struct{ output io.Writer }

View File

@ -66,7 +66,7 @@ func TestList(t *testing.T) {
boson.WithVerbose(verbose))
// Act
names, err := client.List()
names, err := client.List(context.Background())
if err != nil {
t.Fatal(err)
}
@ -85,13 +85,13 @@ func TestNew(t *testing.T) {
client := newClient(verbose)
// Act
if err := client.New(boson.Function{Name: "testnew", Root: ".", Runtime: "go"}); err != nil {
if err := client.New(context.Background(), boson.Function{Name: "testnew", Root: ".", Runtime: "go"}); err != nil {
t.Fatal(err)
}
defer del(t, client, "testnew")
// Assert
items, err := client.List()
items, err := client.List(context.Background())
names := []string{}
for _, item := range items {
names = append(names, item.Name)
@ -111,12 +111,12 @@ func TestDeploy(t *testing.T) {
client := newClient(verbose)
if err := client.New(boson.Function{Name: "deploy", Root: ".", Runtime: "go"}); err != nil {
if err := client.New(context.Background(), boson.Function{Name: "deploy", Root: ".", Runtime: "go"}); err != nil {
t.Fatal(err)
}
defer del(t, client, "deploy")
if err := client.Deploy(context.TODO(), "."); err != nil {
if err := client.Deploy(context.Background(), "."); err != nil {
t.Fatal(err)
}
}
@ -128,16 +128,16 @@ func TestRemove(t *testing.T) {
client := newClient(verbose)
if err := client.New(boson.Function{Name: "remove", Root: ".", Runtime: "go"}); err != nil {
if err := client.New(context.Background(), boson.Function{Name: "remove", Root: ".", Runtime: "go"}); err != nil {
t.Fatal(err)
}
waitFor(t, client, "remove")
if err := client.Remove(boson.Function{Name: "remove"}); err != nil {
if err := client.Remove(context.Background(), boson.Function{Name: "remove"}); err != nil {
t.Fatal(err)
}
names, err := client.List()
names, err := client.List(context.Background())
if err != nil {
t.Fatal(err)
}
@ -204,7 +204,7 @@ func newClient(verbose bool) *boson.Client {
func del(t *testing.T, c *boson.Client, name string) {
t.Helper()
waitFor(t, c, name)
if err := c.Remove(boson.Function{Name: name}); err != nil {
if err := c.Remove(context.Background(), boson.Function{Name: name}); err != nil {
t.Fatal(err)
}
}
@ -218,7 +218,7 @@ func waitFor(t *testing.T, c *boson.Client, name string) {
var pollInterval = 2 * time.Second
for { // ever (i.e. defer to global test timeout)
nn, err := c.List()
nn, err := c.List(context.Background())
if err != nil {
t.Fatal(err)
}

View File

@ -35,7 +35,7 @@ func TestNew(t *testing.T) {
client := bosonFunc.New(bosonFunc.WithRegistry(TestRegistry))
// New Function using Client
if err := client.New(bosonFunc.Function{Root: root}); err != nil {
if err := client.New(context.Background(), bosonFunc.Function{Root: root}); err != nil {
t.Fatal(err)
}
}
@ -70,12 +70,12 @@ func TestExtantAborts(t *testing.T) {
// New once
client := bosonFunc.New(bosonFunc.WithRegistry(TestRegistry))
if err := client.New(bosonFunc.Function{Root: root}); err != nil {
if err := client.New(context.Background(), bosonFunc.Function{Root: root}); err != nil {
t.Fatal(err)
}
// New again should fail as already initialized
if err := client.New(bosonFunc.Function{Root: root}); err == nil {
if err := client.New(context.Background(), bosonFunc.Function{Root: root}); err == nil {
t.Fatal("error expected initilizing a path already containing an initialized Function")
}
}
@ -96,7 +96,7 @@ func TestNonemptyDirectoryAborts(t *testing.T) {
}
client := bosonFunc.New(bosonFunc.WithRegistry(TestRegistry))
if err := client.New(bosonFunc.Function{Root: root}); err == nil {
if err := client.New(context.Background(), bosonFunc.Function{Root: root}); err == nil {
t.Fatal("error expected initilizing a Function in a nonempty directory")
}
}
@ -121,7 +121,7 @@ func TestHiddenFilesIgnored(t *testing.T) {
}
client := bosonFunc.New(bosonFunc.WithRegistry(TestRegistry))
if err := client.New(bosonFunc.Function{Root: root}); err != nil {
if err := client.New(context.Background(), bosonFunc.Function{Root: root}); err != nil {
t.Fatal(err)
}
}
@ -138,7 +138,7 @@ func TestDefaultRuntime(t *testing.T) {
// Create a new function at root with all defaults.
client := bosonFunc.New(bosonFunc.WithRegistry(TestRegistry))
if err := client.New(bosonFunc.Function{Root: root}); err != nil {
if err := client.New(context.Background(), bosonFunc.Function{Root: root}); err != nil {
t.Fatal(err)
}
@ -184,7 +184,7 @@ func TestExtensibleTemplates(t *testing.T) {
bosonFunc.WithRegistry(TestRegistry))
// Create a Function specifying a template, 'json' that only exists in the extensible set
if err := client.New(bosonFunc.Function{Root: root, Trigger: "boson-experimental/json"}); err != nil {
if err := client.New(context.Background(), bosonFunc.Function{Root: root, Trigger: "boson-experimental/json"}); err != nil {
t.Fatal(err)
}
@ -209,7 +209,7 @@ func TestUnsupportedRuntime(t *testing.T) {
// create a Function call witn an unsupported runtime should bubble
// the error generated by the underlying initializer.
if err := client.New(bosonFunc.Function{Root: root, Runtime: "invalid"}); err == nil {
if err := client.New(context.Background(), bosonFunc.Function{Root: root, Runtime: "invalid"}); err == nil {
t.Fatal("unsupported runtime did not generate error")
}
}
@ -232,7 +232,7 @@ func TestNamed(t *testing.T) {
client := bosonFunc.New(bosonFunc.WithRegistry(TestRegistry))
if err := client.New(bosonFunc.Function{Root: root, Name: name}); err != nil {
if err := client.New(context.Background(), bosonFunc.Function{Root: root, Name: name}); err != nil {
t.Fatal(err)
}
@ -266,7 +266,7 @@ func TestRegistryRequired(t *testing.T) {
client := bosonFunc.New()
var err error
if err = client.New(bosonFunc.Function{Root: root}); err == nil {
if err = client.New(context.Background(), bosonFunc.Function{Root: root}); err == nil {
t.Fatal("did not receive expected error creating a Function without specifying Registry")
}
fmt.Println(err)
@ -285,7 +285,7 @@ func TestDeriveImage(t *testing.T) {
// Create the function which calculates fields such as name and image.
client := bosonFunc.New(bosonFunc.WithRegistry(TestRegistry))
if err := client.New(bosonFunc.Function{Root: root}); err != nil {
if err := client.New(context.Background(), bosonFunc.Function{Root: root}); err != nil {
t.Fatal(err)
}
@ -317,7 +317,7 @@ func TestDeriveImageDefaultRegistry(t *testing.T) {
// Rather than use TestRegistry, use a single-token name and expect
// the DefaultRegistry to be prepended.
client := bosonFunc.New(bosonFunc.WithRegistry("alice"))
if err := client.New(bosonFunc.Function{Root: root}); err != nil {
if err := client.New(context.Background(), bosonFunc.Function{Root: root}); err != nil {
t.Fatal(err)
}
@ -398,7 +398,7 @@ func TestNewDelegates(t *testing.T) {
// Invoke the creation, triggering the Function delegates, and
// perform follow-up assertions that the Functions were indeed invoked.
if err := client.New(bosonFunc.Function{Root: root}); err != nil {
if err := client.New(context.Background(), bosonFunc.Function{Root: root}); err != nil {
t.Fatal(err)
}
@ -426,7 +426,7 @@ func TestRun(t *testing.T) {
// Create a client with the mock runner and the new test Function
runner := mock.NewRunner()
client := bosonFunc.New(bosonFunc.WithRegistry(TestRegistry), bosonFunc.WithRunner(runner))
if err := client.New(bosonFunc.Function{Root: root}); err != nil {
if err := client.New(context.Background(), bosonFunc.Function{Root: root}); err != nil {
t.Fatal(err)
}
@ -474,7 +474,7 @@ func TestUpdate(t *testing.T) {
bosonFunc.WithDeployer(deployer))
// create the new Function which will be updated
if err := client.New(bosonFunc.Function{Root: root}); err != nil {
if err := client.New(context.Background(), bosonFunc.Function{Root: root}); err != nil {
t.Fatal(err)
}
@ -512,7 +512,7 @@ func TestUpdate(t *testing.T) {
// Invoke the creation, triggering the Function delegates, and
// perform follow-up assertions that the Functions were indeed invoked.
if err := client.Deploy(context.TODO(), root); err != nil {
if err := client.Deploy(context.Background(), root); err != nil {
t.Fatal(err)
}
@ -545,7 +545,7 @@ func TestRemoveByPath(t *testing.T) {
bosonFunc.WithRegistry(TestRegistry),
bosonFunc.WithRemover(remover))
if err := client.New(bosonFunc.Function{Root: root}); err != nil {
if err := client.New(context.Background(), bosonFunc.Function{Root: root}); err != nil {
t.Fatal(err)
}
@ -556,7 +556,7 @@ func TestRemoveByPath(t *testing.T) {
return nil
}
if err := client.Remove(bosonFunc.Function{Root: root}); err != nil {
if err := client.Remove(context.Background(), bosonFunc.Function{Root: root}); err != nil {
t.Fatal(err)
}
@ -596,12 +596,12 @@ func TestRemoveByName(t *testing.T) {
}
// Run remove with only a name
if err := client.Remove(bosonFunc.Function{Name: expectedName}); err != nil {
if err := client.Remove(context.Background(), bosonFunc.Function{Name: expectedName}); err != nil {
t.Fatal(err)
}
// Run remove with a name and a root, which should be ignored in favor of the name.
if err := client.Remove(bosonFunc.Function{Name: expectedName, Root: root}); err != nil {
if err := client.Remove(context.Background(), bosonFunc.Function{Name: expectedName, Root: root}); err != nil {
t.Fatal(err)
}
@ -636,7 +636,7 @@ func TestRemoveUninitializedFails(t *testing.T) {
bosonFunc.WithRemover(remover))
// Attempt to remove by path (uninitialized), expecting an error.
if err := client.Remove(bosonFunc.Function{Root: root}); err == nil {
if err := client.Remove(context.Background(), bosonFunc.Function{Root: root}); err == nil {
t.Fatalf("did not received expeced error removing an uninitialized func")
}
}
@ -647,7 +647,7 @@ func TestList(t *testing.T) {
client := bosonFunc.New(bosonFunc.WithLister(lister)) // lists deployed Functions.
if _, err := client.List(); err != nil {
if _, err := client.List(context.Background()); err != nil {
t.Fatal(err)
}
@ -666,7 +666,7 @@ func TestListOutsideRoot(t *testing.T) {
// Instantiate in the current working directory, with no name.
client := bosonFunc.New(bosonFunc.WithLister(lister))
if _, err := client.List(); err != nil {
if _, err := client.List(context.Background()); err != nil {
t.Fatal(err)
}
@ -694,7 +694,7 @@ func TestDeployUnbuilt(t *testing.T) {
}
// Now try to deploy it. Ie. without having run the necessary build step.
err := client.Deploy(context.TODO(), root)
err := client.Deploy(context.Background(), root)
if err == nil {
t.Fatal("did not receive an error attempting to deploy an unbuilt Function")
}

View File

@ -6,8 +6,8 @@ import (
"github.com/ory/viper"
"github.com/spf13/cobra"
"github.com/boson-project/func/buildpacks"
bosonFunc "github.com/boson-project/func"
"github.com/boson-project/func/buildpacks"
"github.com/boson-project/func/prompt"
)
@ -100,7 +100,7 @@ func runBuild(cmd *cobra.Command, _ []string) (err error) {
bosonFunc.WithRegistry(config.Registry), // for deriving image name when --image not provided explicitly.
bosonFunc.WithBuilder(builder))
return client.Build(config.Path)
return client.Build(cmd.Context(), config.Path)
}
type buildConfig struct {

View File

@ -19,7 +19,7 @@ func CompleteFunctionList(cmd *cobra.Command, args []string, toComplete string)
directive = cobra.ShellCompDirectiveError
return
}
list, err := lister.List()
list, err := lister.List(cmd.Context())
if err != nil {
directive = cobra.ShellCompDirectiveError
return

View File

@ -71,7 +71,7 @@ func runDelete(cmd *cobra.Command, args []string) (err error) {
bosonFunc.WithVerbose(config.Verbose),
bosonFunc.WithRemover(remover))
return client.Remove(function)
return client.Remove(cmd.Context(), function)
}
type deleteConfig struct {

View File

@ -133,7 +133,7 @@ func runDeploy(cmd *cobra.Command, _ []string) (err error) {
bosonFunc.WithProgressListener(listener))
if config.Build {
if err := client.Build(config.Path); err != nil {
if err := client.Build(cmd.Context(), config.Path); err != nil {
return err
}
}

View File

@ -22,6 +22,9 @@ func main() {
go func() {
<-sigs
cancel()
// second sigint/sigterm is treated as sigkill
<-sigs
os.Exit(137)
}()
cmd.SetMeta(date, vers, hash)

View File

@ -70,7 +70,7 @@ func runList(cmd *cobra.Command, args []string) (err error) {
bosonFunc.WithVerbose(config.Verbose),
bosonFunc.WithLister(lister))
items, err := client.List()
items, err := client.List(cmd.Context())
if err != nil {
return
}

View File

@ -3,6 +3,7 @@ package cmd
import (
"context"
"fmt"
"github.com/pkg/errors"
"os"
"path/filepath"
"strings"
@ -77,6 +78,10 @@ func Execute(ctx context.Context) {
root.Version = version.String()
// Execute the root of the command tree.
if err := root.ExecuteContext(ctx); err != nil {
if errors.Cause(err) == context.Canceled {
os.Exit(130)
return
}
// Errors are printed to STDERR output and the process exits with code of 1.
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)

View File

@ -1,10 +1,7 @@
package cmd
import (
"context"
"fmt"
"github.com/pkg/errors"
"github.com/ory/viper"
"github.com/spf13/cobra"
@ -67,9 +64,6 @@ func runRun(cmd *cobra.Command, args []string) (err error) {
bosonFunc.WithVerbose(config.Verbose))
err = client.Run(cmd.Context(), config.Path)
if errors.Cause(err) == context.Canceled {
err = nil
}
return
}

View File

@ -1,6 +1,7 @@
package knative
import (
"context"
"fmt"
"strings"
"time"
@ -35,7 +36,7 @@ func NewDeployer(namespaceOverride string) (deployer *Deployer, err error) {
return
}
func (d *Deployer) Deploy(f bosonFunc.Function) (err error) {
func (d *Deployer) Deploy(ctx context.Context, f bosonFunc.Function) (err error) {
// k8s does not support service names with dots. so encode it such that
// www.my-domain,com -> www-my--domain-com

View File

@ -1,6 +1,7 @@
package knative
import (
"context"
corev1 "k8s.io/api/core/v1"
clientservingv1 "knative.dev/client/pkg/serving/v1"
"knative.dev/pkg/apis"
@ -31,7 +32,7 @@ func NewLister(namespaceOverride string) (l *Lister, err error) {
return
}
func (l *Lister) List() (items []bosonFunc.ListItem, err error) {
func (l *Lister) List(context.Context) (items []bosonFunc.ListItem, err error) {
client, err := NewServingClient(l.Namespace)
if err != nil {

View File

@ -1,6 +1,7 @@
package knative
import (
"context"
"fmt"
"time"
@ -25,7 +26,7 @@ type Remover struct {
Verbose bool
}
func (remover *Remover) Remove(name string) (err error) {
func (remover *Remover) Remove(ctx context.Context, name string) (err error) {
serviceName, err := k8s.ToK8sAllowedName(name)
if err != nil {

View File

@ -1,6 +1,9 @@
package mock
import bosonFunc "github.com/boson-project/func"
import (
"context"
bosonFunc "github.com/boson-project/func"
)
type Builder struct {
BuildInvoked bool
@ -13,7 +16,7 @@ func NewBuilder() *Builder {
}
}
func (i *Builder) Build(f bosonFunc.Function) error {
func (i *Builder) Build(ctx context.Context, f bosonFunc.Function) error {
i.BuildInvoked = true
return i.BuildFn(f)
}

View File

@ -1,6 +1,9 @@
package mock
import bosonFunc "github.com/boson-project/func"
import (
"context"
bosonFunc "github.com/boson-project/func"
)
type Deployer struct {
DeployInvoked bool
@ -13,7 +16,7 @@ func NewDeployer() *Deployer {
}
}
func (i *Deployer) Deploy(f bosonFunc.Function) error {
func (i *Deployer) Deploy(ctx context.Context, f bosonFunc.Function) error {
i.DeployInvoked = true
return i.DeployFn(f)
}

View File

@ -1,6 +1,9 @@
package mock
import bosonFunc "github.com/boson-project/func"
import (
"context"
bosonFunc "github.com/boson-project/func"
)
type Lister struct {
ListInvoked bool
@ -13,7 +16,7 @@ func NewLister() *Lister {
}
}
func (l *Lister) List() ([]bosonFunc.ListItem, error) {
func (l *Lister) List(context.Context) ([]bosonFunc.ListItem, error) {
l.ListInvoked = true
return l.ListFn()
}

View File

@ -1,5 +1,7 @@
package mock
import "context"
type Remover struct {
RemoveInvoked bool
RemoveFn func(string) error
@ -9,7 +11,7 @@ func NewRemover() *Remover {
return &Remover{}
}
func (r *Remover) Remove(name string) error {
func (r *Remover) Remove(ctx context.Context, name string) error {
r.RemoveInvoked = true
return r.RemoveFn(name)
}