func/cmd/emit.go

143 lines
4.6 KiB
Go

package cmd
import (
"fmt"
"io/ioutil"
fn "github.com/boson-project/func"
"github.com/boson-project/func/cloudevents"
"github.com/boson-project/func/knative"
"github.com/google/uuid"
"github.com/ory/viper"
"github.com/spf13/cobra"
)
func init() {
e := cloudevents.NewEmitter()
root.AddCommand(emitCmd)
// TODO: do these env vars make sense?
emitCmd.Flags().StringP("sink", "k", "", "Send the CloudEvent to the function running at [sink]. The special value \"local\" can be used to send the event to a function running on the local host. When provided, the --path flag is ignored (Env: $FUNC_SINK)")
emitCmd.Flags().StringP("source", "s", e.Source, "CloudEvent source (Env: $FUNC_SOURCE)")
emitCmd.Flags().StringP("type", "t", e.Type, "CloudEvent type (Env: $FUNC_TYPE)")
emitCmd.Flags().StringP("id", "i", uuid.NewString(), "CloudEvent ID (Env: $FUNC_ID)")
emitCmd.Flags().StringP("data", "d", "", "Any arbitrary string to be sent as the CloudEvent data. Ignored if --file is provided (Env: $FUNC_DATA)")
emitCmd.Flags().StringP("file", "f", "", "Path to a local file containing CloudEvent data to be sent (Env: $FUNC_FILE)")
emitCmd.Flags().StringP("content-type", "c", "application/json", "The MIME Content-Type for the CloudEvent data (Env: $FUNC_CONTENT_TYPE)")
emitCmd.Flags().StringP("path", "p", cwd(), "Path to the project directory. Ignored when --sink is provided (Env: $FUNC_PATH)")
}
var emitCmd = &cobra.Command{
Use: "emit",
Short: "Emit a CloudEvent to a function endpoint",
Long: `Emit event
Emits a CloudEvent, sending it to the deployed function.
`,
Example: `
# Send a CloudEvent to the deployed function with no data and default values
# for source, type and ID
kn func emit
# Send a CloudEvent to the deployed function with the data found in ./test.json
kn func emit --file ./test.json
# Send a CloudEvent to the function running locally with a CloudEvent containing
# "Hello World!" as the data field, with a content type of "text/plain"
kn func emit --data "Hello World!" --content-type "text/plain" -s local
# Send a CloudEvent to the function running locally with an event type of "my.event"
kn func emit --type my.event --sink local
# Send a CloudEvent to the deployed function found at /path/to/fn with an id of "fn.test"
kn func emit --path /path/to/fn -i fn.test
# Send a CloudEvent to an arbitrary endpoint
kn func emit --sink "http://my.event.broker.com"
`,
SuggestFor: []string{"meit", "emti", "send"},
PreRunE: bindEnv("source", "type", "id", "data", "file", "path", "sink", "content-type"),
RunE: runEmit,
}
func runEmit(cmd *cobra.Command, args []string) (err error) {
config := newEmitConfig()
var endpoint string
if config.Sink != "" {
if config.Sink == "local" {
endpoint = "http://localhost:8080"
} else {
endpoint = config.Sink
}
} else {
var f fn.Function
f, err = fn.NewFunction(config.Path)
if err != nil {
return
}
// What happens if the function hasn't been deployed but they don't run with --local=true
// Maybe we should be thinking about saving the endpoint URL in func.yaml after each deploy
var d *knative.Describer
d, err = knative.NewDescriber("")
if err != nil {
return
}
var desc fn.Description
desc, err = d.Describe(f.Name)
if err != nil {
return
}
// Use the first available route
endpoint = desc.Routes[0]
}
emitter := cloudevents.NewEmitter()
emitter.Source = config.Source
emitter.Type = config.Type
emitter.Id = config.Id
emitter.ContentType = config.ContentType
emitter.Data = config.Data
if config.File != "" {
var buf []byte
if emitter.Data != "" && config.Verbose {
// TODO: This made me wonder whether we should switch to a real logging library
fmt.Printf("WARN: Found both --data and --file. Using file: %v\n", config.File)
}
buf, err = ioutil.ReadFile(config.File)
if err != nil {
return
}
emitter.Data = string(buf)
}
client := fn.New(
fn.WithEmitter(emitter),
)
return client.Emit(cmd.Context(), endpoint)
}
type emitConfig struct {
Path string
Source string
Type string
Id string
Data string
File string
ContentType string
Sink string
Verbose bool
}
func newEmitConfig() emitConfig {
return emitConfig{
Path: viper.GetString("path"),
Source: viper.GetString("source"),
Type: viper.GetString("type"),
Id: viper.GetString("id"),
Data: viper.GetString("data"),
File: viper.GetString("file"),
ContentType: viper.GetString("content-type"),
Sink: viper.GetString("sink"),
Verbose: viper.GetBool("verbose"),
}
}