Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#

# build stage
FROM golang:1.23-alpine AS build-env
FROM golang:1.24-alpine AS build-env
RUN apk add --no-cache gcc musl-dev
ARG D=/go/src/github.com/fnproject/cli
ARG GO111MODULE=on
Expand Down
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,29 @@ curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh
## CLI Development
* Refer to the [Fn CLI Wiki](https://github.com/fnproject/cli/wiki) for development details.

## Watch (local auto-deploy)
To watch a directory and automatically redeploy to a local Fn server when files change:

```sh
fn watch --app <app>
```

This watches the current directory recursively and triggers:

```sh
fn deploy --app <app> --local --no-bump
```

### Ignoring paths
`fn watch` ignores these directories by default:

- `.git`, `.fn`, `node_modules`, `target`, `dist`, `vendor`, `Dockerfile-fn-tmp*`

You can add more ignore rules by creating a `.fnignore` file in the watched directory (one pattern per line; `#` comments supported), and/or by passing `--ignore` flags.

### Build from source
See [CONTRIBUTING](https://github.com/fnproject/cli/blob/master/CONTRIBUTING.md) for instructions to build the CLI from source.





5 changes: 5 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@

# Releases
## Unreleased

* Enabling support for node24
* Enabling support for java21

## v 0.6.47

* Update default version for python to 3.12
Expand Down
4 changes: 4 additions & 0 deletions client/invoke.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type InvokeRequest struct {
Content io.Reader
Env []string
ContentType string
FnInvokeType string
// TODO headers should be their real type?
}

Expand Down Expand Up @@ -82,6 +83,9 @@ func Invoke(provider provider.Provider, ireq InvokeRequest) (*http.Response, err
} else {
req.Header.Set("Content-Type", "text/plain")
}
if ireq.FnInvokeType != "" {
req.Header.Set("fn-invoke-type", ireq.FnInvokeType)
}

if len(env) > 0 {
EnvAsHeader(req, env)
Expand Down
48 changes: 48 additions & 0 deletions client/invoke_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package client

import (
"io"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"

"github.com/fnproject/fn_go/client/version"
"github.com/fnproject/fn_go/clientv2"
"github.com/fnproject/fn_go/provider"
)

type invokeTestProvider struct{}

func (p *invokeTestProvider) APIClientv2() *clientv2.Fn { return nil }
func (p *invokeTestProvider) APIURL() *url.URL { return nil }
func (p *invokeTestProvider) UnavailableResources() []provider.FnResourceType {
return nil
}
func (p *invokeTestProvider) VersionClient() *version.Client { return nil }
func (p *invokeTestProvider) WrapCallTransport(rt http.RoundTripper) http.RoundTripper {
return rt
}

func TestInvokeSetsFnInvokeTypeHeader(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if got := r.Header.Get("fn-invoke-type"); got != "detached" {
t.Fatalf("expected fn-invoke-type detached, got %q", got)
}
w.WriteHeader(http.StatusAccepted)
_, _ = w.Write([]byte("ok"))
}))
defer server.Close()

resp, err := Invoke(&invokeTestProvider{}, InvokeRequest{
URL: server.URL,
FnInvokeType: "detached",
Content: strings.NewReader("{}"),
})
if err != nil {
t.Fatalf("Invoke() error = %v", err)
}
defer resp.Body.Close()
_, _ = io.ReadAll(resp.Body)
}
3 changes: 2 additions & 1 deletion commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@ import (
"github.com/urfave/cli"
)

//Cmd is a mapping from a commands name to its corresponding structure
// Cmd is a mapping from a commands name to its corresponding structure
type Cmd map[string]cli.Command

// Commands map of all top-level commands
var Commands = Cmd{
"build": BuildCommand(),
"build-server": BuildServerCommand(),
"bump": common.BumpCommand(),
"watch": WatchCommand(),
"invoke": InvokeCommand(),
"configure": ConfigureCommand(),
"create": CreateCommand(),
Expand Down
18 changes: 18 additions & 0 deletions commands/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
trigger "github.com/fnproject/cli/objects/trigger"
v2Client "github.com/fnproject/fn_go/clientv2"
models "github.com/fnproject/fn_go/modelsv2"
fnprovider "github.com/fnproject/fn_go/provider"
"github.com/oracle/oci-go-sdk/v65/artifacts"
ociCommon "github.com/oracle/oci-go-sdk/v65/common"
"github.com/oracle/oci-go-sdk/v65/keymanagement"
Expand Down Expand Up @@ -96,6 +97,7 @@ func DeployCommand() cli.Command {
if err != nil {
return err
}
cmd.provider = provider
cmd.clientV2 = provider.APIClientv2()
return nil
},
Expand All @@ -109,6 +111,7 @@ func DeployCommand() cli.Command {

type deploycmd struct {
clientV2 *v2Client.Fn
provider fnprovider.Provider

appName string
createApp bool
Expand Down Expand Up @@ -349,6 +352,7 @@ func (p *deploycmd) deployFuncV20180708(c *cli.Context, app *models.App, funcfil
if funcfile.Name == "" {
funcfile.Name = filepath.Base(filepath.Dir(funcfilePath)) // todo: should probably make a copy of ff before changing it
}
common.WarnIfOCIManagedFunctionSettingsUnsupported(os.Stderr, p.provider, funcfile.Name, funcfile)

oracleProvider, _ := getOracleProvider()
if oracleProvider != nil && oracleProvider.ImageCompartmentID != "" {
Expand Down Expand Up @@ -419,11 +423,25 @@ func (p *deploycmd) deployFuncV20180708(c *cli.Context, app *models.App, funcfil

func (p *deploycmd) updateFunction(c *cli.Context, appID string, ff *common.FuncFileV20180708) error {
fmt.Printf("Updating function %s using image %s...\n", ff.Name, ff.ImageNameV20180708())
var detachedSeconds int
if ff.Deploy != nil && ff.Deploy.OCI != nil && ff.Deploy.OCI.DetachedMode != nil && ff.Deploy.OCI.DetachedMode.Timeout != "" {
_, seconds, err := common.ParseDetachedTimeoutSpec(ff.Deploy.OCI.DetachedMode.Timeout)
if err != nil {
return err
}
detachedSeconds = seconds
}

fn := &models.Fn{}
if err := function.WithFuncFileV20180708(ff, fn); err != nil {
return fmt.Errorf("Error getting function with funcfile: %s", err)
}
if detachedSeconds > 0 && common.IsOracleProvider(p.provider) {
function.SetDetachedTimeoutAnnotation(fn, detachedSeconds)
}
if common.IsOracleProvider(p.provider) && ff.Deploy != nil && ff.Deploy.OCI != nil && ff.Deploy.OCI.DetachedMode != nil {
function.SetDestinationAnnotations(fn, ff.Deploy.OCI.DetachedMode.OnSuccess, ff.Deploy.OCI.DetachedMode.OnFailure)
}

fnRes, err := function.GetFnByName(p.clientV2, appID, ff.Name)
if _, ok := err.(function.NameNotFoundError); ok {
Expand Down
27 changes: 27 additions & 0 deletions commands/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,18 @@ func initFlags(a *initFnCmd) []cli.Flag {
Name: "annotation",
Usage: "Function annotation (can be specified multiple times)",
},
cli.StringFlag{
Name: "detached-timeout",
Usage: "Set OCI detached mode timeout using a duration like 20m or 1h",
},
cli.StringFlag{
Name: "on-success",
Usage: "Set OCI detached success destination using <stream|queue|notifications>:<ocid>",
},
cli.StringFlag{
Name: "on-failure",
Usage: "Set OCI detached failure destination using <stream|queue|notifications>:<ocid>",
},
}

return fgs
Expand Down Expand Up @@ -176,6 +188,21 @@ func (a *initFnCmd) init(c *cli.Context) error {

function.WithFlags(c, &fn)
a.bindFn(&fn)
if timeoutSpec, _, err := common.ParseDetachedTimeoutSpec(c.String("detached-timeout")); err != nil {
return err
} else {
common.SetDetachedTimeout(a.ff, timeoutSpec)
}
if dest, err := common.ParseOCIDestinationSpec("--on-success", c.String("on-success")); err != nil {
return err
} else {
common.SetOnSuccessDestination(a.ff, dest)
}
if dest, err := common.ParseOCIDestinationSpec("--on-failure", c.String("on-failure")); err != nil {
return err
} else {
common.SetOnFailureDestination(a.ff, dest)
}

runtime := c.String("runtime")
initImage := c.String("init-image")
Expand Down
13 changes: 13 additions & 0 deletions commands/invoke.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ var InvokeFnFlags = []cli.Flag{
Name: "output",
Usage: "Output format (json)",
},
cli.StringFlag{
Name: "fn-invoke-type",
Usage: "Invoke type for Oracle Functions: sync or detached",
},
}

// InvokeCommand returns call cli.command
Expand Down Expand Up @@ -129,6 +133,14 @@ func (cl *invokeCmd) Invoke(c *cli.Context) error {
}
content := stdin()
wd := common.GetWd()
invokeType := strings.ToLower(strings.TrimSpace(c.String("fn-invoke-type")))
if invokeType != "" && invokeType != "sync" && invokeType != "detached" {
return fmt.Errorf("invalid value for --fn-invoke-type: %q", invokeType)
}
if invokeType == "detached" && !common.IsOracleProvider(cl.provider) {
fmt.Fprintln(os.Stderr, "Warning: --fn-invoke-type=detached is only supported with an oracle provider and will be ignored.")
invokeType = ""
}

if c.String("content-type") != "" {
contentType = c.String("content-type")
Expand All @@ -145,6 +157,7 @@ func (cl *invokeCmd) Invoke(c *cli.Context) error {
Content: content,
Env: c.StringSlice("e"),
ContentType: contentType,
FnInvokeType: invokeType,
},
)
if err != nil {
Expand Down
Loading