From b0d27ee5d23866f2940e3973eb337c24257311c4 Mon Sep 17 00:00:00 2001 From: Ta-Ching Chen Date: Tue, 5 Nov 2019 10:30:16 +0800 Subject: [PATCH] Refactor record command (#1378) --- pkg/fission-cli/cli.go | 17 +- pkg/fission-cli/cmd/function/create.go | 1 - pkg/fission-cli/cmd/httptrigger/create.go | 3 +- pkg/fission-cli/cmd/httptrigger/delete.go | 1 - pkg/fission-cli/cmd/httptrigger/get.go | 1 - pkg/fission-cli/cmd/httptrigger/list.go | 1 - pkg/fission-cli/cmd/httptrigger/update.go | 1 - pkg/fission-cli/cmd/recorder/create.go | 120 +++++++++ pkg/fission-cli/cmd/recorder/delete.go | 66 +++++ pkg/fission-cli/cmd/recorder/get.go | 78 ++++++ pkg/fission-cli/cmd/recorder/list.go | 62 +++++ pkg/fission-cli/cmd/recorder/update.go | 141 ++++++++++ .../{records.go => cmd/records/view.go} | 93 ++++--- pkg/fission-cli/cmd/replay/replay.go | 67 +++++ pkg/fission-cli/recorder.go | 246 ------------------ pkg/fission-cli/replay.go | 51 ---- 16 files changed, 605 insertions(+), 344 deletions(-) create mode 100644 pkg/fission-cli/cmd/recorder/create.go create mode 100644 pkg/fission-cli/cmd/recorder/delete.go create mode 100644 pkg/fission-cli/cmd/recorder/get.go create mode 100644 pkg/fission-cli/cmd/recorder/list.go create mode 100644 pkg/fission-cli/cmd/recorder/update.go rename pkg/fission-cli/{records.go => cmd/records/view.go} (51%) create mode 100644 pkg/fission-cli/cmd/replay/replay.go delete mode 100644 pkg/fission-cli/recorder.go delete mode 100644 pkg/fission-cli/replay.go diff --git a/pkg/fission-cli/cli.go b/pkg/fission-cli/cli.go index c7a5eab4..66d9a8bf 100644 --- a/pkg/fission-cli/cli.go +++ b/pkg/fission-cli/cli.go @@ -38,6 +38,9 @@ import ( "github.com/fission/fission/pkg/fission-cli/cmd/mqtrigger" _package "github.com/fission/fission/pkg/fission-cli/cmd/package" plugincmd "github.com/fission/fission/pkg/fission-cli/cmd/plugin" + "github.com/fission/fission/pkg/fission-cli/cmd/recorder" + "github.com/fission/fission/pkg/fission-cli/cmd/records" + "github.com/fission/fission/pkg/fission-cli/cmd/replay" "github.com/fission/fission/pkg/fission-cli/cmd/spec" "github.com/fission/fission/pkg/fission-cli/cmd/support" "github.com/fission/fission/pkg/fission-cli/cmd/timetrigger" @@ -205,11 +208,11 @@ func NewCliApp() *cli.App { recEnabled := cli.BoolFlag{Name: "enable", Usage: "Enable recorder"} recDisabled := cli.BoolFlag{Name: "disable", Usage: "Disable recorder"} recSubcommands := []cli.Command{ - {Name: "create", Aliases: []string{"add"}, Usage: "Create recorder", Flags: []cli.Flag{recNameFlag, recFnFlag, recTriggersFlag, specSaveFlag}, Action: recorderCreate}, - {Name: "get", Usage: "Get recorder", Flags: []cli.Flag{recNameFlag}, Action: recorderGet}, - {Name: "update", Usage: "Update recorder", Flags: []cli.Flag{recNameFlag, recFnFlag, recTriggersFlag, recEnabled, recDisabled}, Action: recorderUpdate}, - {Name: "delete", Usage: "Delete recorder", Flags: []cli.Flag{recNameFlag, recorderNamespaceFlag}, Action: recorderDelete}, - {Name: "list", Usage: "List recorders", Flags: []cli.Flag{}, Action: recorderList}, + {Name: "create", Aliases: []string{"add"}, Usage: "Create recorder", Flags: []cli.Flag{recNameFlag, recFnFlag, recTriggersFlag, specSaveFlag}, Action: urfavecli.Wrapper(recorder.Create)}, + {Name: "get", Usage: "Get recorder", Flags: []cli.Flag{recNameFlag}, Action: urfavecli.Wrapper(recorder.Get)}, + {Name: "update", Usage: "Update recorder", Flags: []cli.Flag{recNameFlag, recFnFlag, recTriggersFlag, recEnabled, recDisabled}, Action: urfavecli.Wrapper(recorder.Update)}, + {Name: "delete", Usage: "Delete recorder", Flags: []cli.Flag{recNameFlag, recorderNamespaceFlag}, Action: urfavecli.Wrapper(recorder.Delete)}, + {Name: "list", Usage: "List recorders", Flags: []cli.Flag{}, Action: urfavecli.Wrapper(recorder.List)}, } // View records @@ -220,7 +223,7 @@ func NewCliApp() *cli.App { verbosityFlag := cli.BoolFlag{Name: "v", Usage: "Toggle verbosity -- view more detailed requests/responses"} vvFlag := cli.BoolFlag{Name: "vv", Usage: "Toggle verbosity -- view raw requests/responses"} recViewSubcommands := []cli.Command{ - {Name: "view", Usage: "View existing records", Flags: []cli.Flag{filterTimeTo, filterTimeFrom, filterFunction, filterTrigger, verbosityFlag, vvFlag}, Action: recordsView}, + {Name: "view", Usage: "View existing records", Flags: []cli.Flag{filterTimeTo, filterTimeFrom, filterFunction, filterTrigger, verbosityFlag, vvFlag}, Action: urfavecli.Wrapper(records.View)}, } // Replay records @@ -327,7 +330,7 @@ func NewCliApp() *cli.App { {Name: "mqtrigger", Aliases: []string{"mqt", "messagequeue"}, Usage: "Manage message queue triggers for functions", Subcommands: mqtSubcommands}, {Name: "recorder", Usage: "Manage recorders for functions", Subcommands: recSubcommands, Hidden: true}, {Name: "records", Usage: "View records with optional filters", Subcommands: recViewSubcommands, Hidden: true}, - {Name: "replay", Usage: "Replay records", Flags: []cli.Flag{reqIDFlag}, Action: replay}, + {Name: "replay", Usage: "Replay records", Flags: []cli.Flag{reqIDFlag}, Action: urfavecli.Wrapper(replay.Replay)}, {Name: "environment", Aliases: []string{"env"}, Usage: "Manage environments", Subcommands: envSubcommands}, {Name: "watch", Aliases: []string{"w"}, Usage: "Manage watches", Subcommands: wSubCommands}, {Name: "package", Aliases: []string{"pkg"}, Usage: "Manage packages", Subcommands: pkgSubCommands}, diff --git a/pkg/fission-cli/cmd/function/create.go b/pkg/fission-cli/cmd/function/create.go index d1e754f9..6759f68a 100644 --- a/pkg/fission-cli/cmd/function/create.go +++ b/pkg/fission-cli/cmd/function/create.go @@ -64,7 +64,6 @@ func (opts *CreateSubCommand) do(flags cli.Input) error { return opts.run(flags) } -// complete creates a environment objects and populates it with default value and CLI inputs. func (opts *CreateSubCommand) complete(flags cli.Input) error { fnNamespace := flags.String("fnNamespace") envNamespace := flags.String("envNamespace") diff --git a/pkg/fission-cli/cmd/httptrigger/create.go b/pkg/fission-cli/cmd/httptrigger/create.go index 550b8f77..75d726a4 100644 --- a/pkg/fission-cli/cmd/httptrigger/create.go +++ b/pkg/fission-cli/cmd/httptrigger/create.go @@ -18,10 +18,10 @@ package httptrigger import ( "fmt" - "github.com/pkg/errors" "net/http" "strings" + "github.com/pkg/errors" "github.com/satori/go.uuid" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -55,7 +55,6 @@ func (opts *CreateSubCommand) do(flags cli.Input) error { return opts.run(flags) } -// complete creates a environment objects and populates it with default value and CLI inputs. func (opts *CreateSubCommand) complete(flags cli.Input) error { functionList := flags.StringSlice("function") functionWeightsList := flags.IntSlice("weight") diff --git a/pkg/fission-cli/cmd/httptrigger/delete.go b/pkg/fission-cli/cmd/httptrigger/delete.go index 0a926b27..3aefeb22 100644 --- a/pkg/fission-cli/cmd/httptrigger/delete.go +++ b/pkg/fission-cli/cmd/httptrigger/delete.go @@ -50,7 +50,6 @@ func (opts *DeleteSubCommand) do(flags cli.Input) error { return opts.run(flags) } -// complete creates a environment objects and populates it with default value and CLI inputs. func (opts *DeleteSubCommand) complete(flags cli.Input) error { opts.triggerName = flags.String("name") opts.functionName = flags.String("function") diff --git a/pkg/fission-cli/cmd/httptrigger/get.go b/pkg/fission-cli/cmd/httptrigger/get.go index d6bb9684..6c41b801 100644 --- a/pkg/fission-cli/cmd/httptrigger/get.go +++ b/pkg/fission-cli/cmd/httptrigger/get.go @@ -52,7 +52,6 @@ func (opts *GetSubCommand) do(flags cli.Input) error { return opts.run(flags) } -// complete creates a environment objects and populates it with default value and CLI inputs. func (opts *GetSubCommand) complete(flags cli.Input) error { opts.trigger = flags.String("name") opts.namespace = flags.String("fnNamespace") diff --git a/pkg/fission-cli/cmd/httptrigger/list.go b/pkg/fission-cli/cmd/httptrigger/list.go index bbe1a85f..3996fcdd 100644 --- a/pkg/fission-cli/cmd/httptrigger/list.go +++ b/pkg/fission-cli/cmd/httptrigger/list.go @@ -46,7 +46,6 @@ func (opts *ListSubCommand) do(flags cli.Input) error { return opts.run(flags) } -// complete creates a environment objects and populates it with default value and CLI inputs. func (opts *ListSubCommand) complete(flags cli.Input) error { opts.triggerNamespace = flags.String("triggerNamespace") opts.filterFunctionName = flags.String("function") diff --git a/pkg/fission-cli/cmd/httptrigger/update.go b/pkg/fission-cli/cmd/httptrigger/update.go index af14b2d6..3396aa6e 100644 --- a/pkg/fission-cli/cmd/httptrigger/update.go +++ b/pkg/fission-cli/cmd/httptrigger/update.go @@ -50,7 +50,6 @@ func (opts *UpdateSubCommand) do(flags cli.Input) error { return opts.run(flags) } -// complete creates a environment objects and populates it with default value and CLI inputs. func (opts *UpdateSubCommand) complete(flags cli.Input) error { htName := flags.String("name") if len(htName) == 0 { diff --git a/pkg/fission-cli/cmd/recorder/create.go b/pkg/fission-cli/cmd/recorder/create.go new file mode 100644 index 00000000..d93fa78d --- /dev/null +++ b/pkg/fission-cli/cmd/recorder/create.go @@ -0,0 +1,120 @@ +/* +Copyright 2019 The Fission Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package recorder + +import ( + "fmt" + "strings" + + "github.com/pkg/errors" + "github.com/satori/go.uuid" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + fv1 "github.com/fission/fission/pkg/apis/fission.io/v1" + "github.com/fission/fission/pkg/controller/client" + "github.com/fission/fission/pkg/fission-cli/cliwrapper/cli" + "github.com/fission/fission/pkg/fission-cli/cmd" + "github.com/fission/fission/pkg/fission-cli/cmd/spec" +) + +type CreateSubCommand struct { + client *client.Client + recorder *fv1.Recorder +} + +func Create(flags cli.Input) error { + opts := CreateSubCommand{ + client: cmd.GetServer(flags), + } + return opts.do(flags) +} + +func (opts *CreateSubCommand) do(flags cli.Input) error { + err := opts.complete(flags) + if err != nil { + return err + } + return opts.run(flags) +} + +func (opts *CreateSubCommand) complete(flags cli.Input) error { + recName := flags.String("name") + if len(recName) == 0 { + recName = uuid.NewV4().String() + } + fnName := flags.String("function") + triggersOriginal := flags.StringSlice("trigger") + + // Function XOR triggers can be given + if len(fnName) == 0 && len(triggersOriginal) == 0 { + return errors.New("Need to specify at least one function or one trigger, use --function, --trigger") + } + if len(fnName) != 0 && len(triggersOriginal) != 0 { + return errors.New("Can specify either one function or one or more triggers, but not both") + } + + // TODO: Validate here or elsewhere that all triggers belong to the same namespace + + var triggers []string + if len(triggersOriginal) != 0 { + ts := strings.Split(triggersOriginal[0], ",") + for _, name := range ts { + if len(name) > 0 { + triggers = append(triggers, name) + } + } + } + // TODO: Define appropriate set of policies and defaults + //retPolicy := flags.String("retention") + //evictPolicy := flags.String("eviction") + + opts.recorder = &fv1.Recorder{ + Metadata: metav1.ObjectMeta{ + Name: recName, + Namespace: "default", + }, + Spec: fv1.RecorderSpec{ + Name: recName, + Function: fnName, + Triggers: triggers, + RetentionPolicy: "Permanent", // TODO: Implement customizable policies for expiration of records + EvictionPolicy: "None", + Enabled: true, + }, + } + + return nil +} + +func (opts *CreateSubCommand) run(flags cli.Input) error { + // If we're writing a spec, don't call the API + if flags.Bool("spec") { + specFile := fmt.Sprintf("recorder-%v.yaml", opts.recorder.Metadata.Name) + err := spec.SpecSave(*opts.recorder, specFile) + if err != nil { + return errors.Wrap(err, "error creating recorder spec") + } + return nil + } + _, err := opts.client.RecorderCreate(opts.recorder) + if err != nil { + return errors.Wrap(err, "error creating recorder") + } + + fmt.Printf("recorder '%s' created\n", opts.recorder.Metadata.Name) + return nil +} diff --git a/pkg/fission-cli/cmd/recorder/delete.go b/pkg/fission-cli/cmd/recorder/delete.go new file mode 100644 index 00000000..eb49d440 --- /dev/null +++ b/pkg/fission-cli/cmd/recorder/delete.go @@ -0,0 +1,66 @@ +/* +Copyright 2019 The Fission Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package recorder + +import ( + "fmt" + + "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/fission/fission/pkg/controller/client" + "github.com/fission/fission/pkg/fission-cli/cliwrapper/cli" + "github.com/fission/fission/pkg/fission-cli/cmd" +) + +type DeleteSubCommand struct { + client *client.Client + metadata *metav1.ObjectMeta +} + +func Delete(flags cli.Input) error { + opts := DeleteSubCommand{ + client: cmd.GetServer(flags), + } + return opts.do(flags) +} + +func (opts *DeleteSubCommand) do(flags cli.Input) error { + err := opts.complete(flags) + if err != nil { + return err + } + return opts.run(flags) +} + +func (opts *DeleteSubCommand) complete(flags cli.Input) error { + m, err := cmd.GetMetadata("name", "recorderns", flags) + if err != nil { + return err + } + opts.metadata = m + return nil +} + +func (opts *DeleteSubCommand) run(flags cli.Input) error { + err := opts.client.RecorderDelete(opts.metadata) + if err != nil { + return errors.Wrap(err, "error deleting recorder") + } + fmt.Printf("recorder '%v' deleted\n", opts.metadata.Name) + return nil +} diff --git a/pkg/fission-cli/cmd/recorder/get.go b/pkg/fission-cli/cmd/recorder/get.go new file mode 100644 index 00000000..4681a745 --- /dev/null +++ b/pkg/fission-cli/cmd/recorder/get.go @@ -0,0 +1,78 @@ +/* +Copyright 2019 The Fission Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package recorder + +import ( + "fmt" + "os" + "text/tabwriter" + + "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/fission/fission/pkg/controller/client" + "github.com/fission/fission/pkg/fission-cli/cliwrapper/cli" + "github.com/fission/fission/pkg/fission-cli/cmd" +) + +type GetSubCommand struct { + client *client.Client + name string +} + +func Get(flags cli.Input) error { + opts := GetSubCommand{ + client: cmd.GetServer(flags), + } + return opts.do(flags) +} + +func (opts *GetSubCommand) do(flags cli.Input) error { + err := opts.complete(flags) + if err != nil { + return err + } + return opts.run(flags) +} + +func (opts *GetSubCommand) complete(flags cli.Input) error { + opts.name = flags.String("name") + + if len(opts.name) <= 0 { + return errors.New("need a recorder name, use --name") + } + return nil +} + +func (opts *GetSubCommand) run(flags cli.Input) error { + recorder, err := opts.client.RecorderGet(&metav1.ObjectMeta{ + Name: opts.name, + Namespace: "default", + }) + if err != nil { + return errors.Wrap(err, "error getting recorder") + } + + w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0) + + fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\n", + "NAME", "ENABLED", "FUNCTION", "TRIGGERS", "RETENTION_POLICY", "EVICTION_POLICY") + fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\n", + recorder.Metadata.Name, recorder.Spec.Enabled, recorder.Spec.Function, recorder.Spec.Triggers, recorder.Spec.RetentionPolicy, recorder.Spec.EvictionPolicy) + w.Flush() + return nil +} diff --git a/pkg/fission-cli/cmd/recorder/list.go b/pkg/fission-cli/cmd/recorder/list.go new file mode 100644 index 00000000..5d28e2aa --- /dev/null +++ b/pkg/fission-cli/cmd/recorder/list.go @@ -0,0 +1,62 @@ +/* +Copyright 2019 The Fission Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package recorder + +import ( + "fmt" + "os" + "text/tabwriter" + + "github.com/pkg/errors" + + "github.com/fission/fission/pkg/controller/client" + "github.com/fission/fission/pkg/fission-cli/cliwrapper/cli" + "github.com/fission/fission/pkg/fission-cli/cmd" +) + +type ListSubCommand struct { + client *client.Client +} + +func List(flags cli.Input) error { + opts := ListSubCommand{ + client: cmd.GetServer(flags), + } + return opts.do(flags) +} + +func (opts *ListSubCommand) do(flags cli.Input) error { + return opts.run(flags) +} + +func (opts *ListSubCommand) run(flags cli.Input) error { + recorders, err := opts.client.RecorderList("default") + if err != nil { + return errors.Wrap(err, "error listing recorders") + } + + w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0) + + fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\n", + "NAME", "ENABLED", "FUNCTIONS", "TRIGGERS", "RETENTION_POLICY", "EVICTION_POLICY") + for _, r := range recorders { + fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\n", + r.Metadata.Name, r.Spec.Enabled, r.Spec.Function, r.Spec.Triggers, r.Spec.RetentionPolicy, r.Spec.EvictionPolicy) + } + w.Flush() + return nil +} diff --git a/pkg/fission-cli/cmd/recorder/update.go b/pkg/fission-cli/cmd/recorder/update.go new file mode 100644 index 00000000..ff551620 --- /dev/null +++ b/pkg/fission-cli/cmd/recorder/update.go @@ -0,0 +1,141 @@ +/* +Copyright 2019 The Fission Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package recorder + +import ( + "fmt" + "strings" + + "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + fv1 "github.com/fission/fission/pkg/apis/fission.io/v1" + "github.com/fission/fission/pkg/controller/client" + "github.com/fission/fission/pkg/fission-cli/cliwrapper/cli" + "github.com/fission/fission/pkg/fission-cli/cmd" +) + +type UpdateSubCommand struct { + client *client.Client + recorder *fv1.Recorder +} + +func Update(flags cli.Input) error { + opts := UpdateSubCommand{ + client: cmd.GetServer(flags), + } + return opts.do(flags) +} + +func (opts *UpdateSubCommand) do(flags cli.Input) error { + err := opts.complete(flags) + if err != nil { + return err + } + return opts.run(flags) +} + +func (opts *UpdateSubCommand) complete(flags cli.Input) error { + recName := flags.String("name") + enable := flags.Bool("enable") + disable := flags.Bool("disable") + //retPolicy := flags.String("retention") + //evictPolicy := flags.String("eviction") + triggers := flags.StringSlice("trigger") + function := flags.String("function") + + if enable && disable { + return errors.New("Cannot enable and disable a recorder simultaneously.") + } + + // Prevent enable or disable while trying to update other fields. These flags must be standalone. + if enable || disable { + if len(triggers) > 0 || len(function) > 0 { + return errors.New("Enabling or disabling a recorder with other (non-name) flags set is not supported.") + } + } else if len(triggers) == 0 && len(function) == 0 { + return errors.New("Need to specify either a function or trigger(s) for this recorder") + } + + if len(recName) == 0 { + return errors.New("Need name of recorder, use --name") + } + + recorder, err := opts.client.RecorderGet(&metav1.ObjectMeta{ + Name: recName, + Namespace: "default", + }) + if err != nil { + return errors.Wrap(err, "error getting recorder") + } + + updated := false + + // TODO: Additional validation on type of supported retention policy, eviction policy + + //if len(retPolicy) > 0 { + // recorder.Spec.RetentionPolicy = retPolicy + // updated = true + //} + //if len(evictPolicy) > 0 { + // recorder.Spec.EvictionPolicy = evictPolicy + // updated = true + //} + if enable { + recorder.Spec.Enabled = true + updated = true + } + + if disable { + recorder.Spec.Enabled = false + updated = true + } + + if len(triggers) > 0 { + var newTriggers []string + triggs := strings.Split(triggers[0], ",") + for _, name := range triggs { + if len(name) > 0 { + newTriggers = append(newTriggers, name) + } + } + recorder.Spec.Triggers = newTriggers + updated = true + } + + if len(function) > 0 { + recorder.Spec.Function = function + updated = true + } + + if !updated { + return errors.New("Nothing to update. Use --function, --triggers, --enable or --disable") + } + + opts.recorder = recorder + return nil +} + +func (opts *UpdateSubCommand) run(flags cli.Input) error { + _, err := opts.client.RecorderUpdate(opts.recorder) + if err != nil { + return errors.Wrap(err, "error updating recorder") + } + + fmt.Printf("recorder '%v' updated\n", opts.recorder.Metadata.Name) + return nil +} diff --git a/pkg/fission-cli/records.go b/pkg/fission-cli/cmd/records/view.go similarity index 51% rename from pkg/fission-cli/records.go rename to pkg/fission-cli/cmd/records/view.go index 879a31dc..bf4a0fb9 100644 --- a/pkg/fission-cli/records.go +++ b/pkg/fission-cli/cmd/records/view.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Fission Authors. +Copyright 2019 The Fission Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,72 +14,95 @@ See the License for the specific language governing permissions and limitations under the License. */ -package fission_cli +package records import ( "fmt" "os" "text/tabwriter" - "github.com/urfave/cli" + "github.com/pkg/errors" - "github.com/fission/fission/pkg/fission-cli/log" + "github.com/fission/fission/pkg/controller/client" + "github.com/fission/fission/pkg/fission-cli/cliwrapper/cli" + "github.com/fission/fission/pkg/fission-cli/cmd" "github.com/fission/fission/pkg/fission-cli/util" - "github.com/fission/fission/pkg/redis/build/gen" + redisCache "github.com/fission/fission/pkg/redis/build/gen" ) -func recordsView(c *cli.Context) error { - var verbosity int - if c.Bool("v") && c.Bool("vv") { - log.Fatal("conflicting verbosity levels, use either --v or --vv") +type ViewSubCommand struct { + client *client.Client +} + +func View(flags cli.Input) error { + opts := ViewSubCommand{ + client: cmd.GetServer(flags), } - if c.Bool("v") { + return opts.do(flags) +} + +func (opts *ViewSubCommand) do(flags cli.Input) error { + return opts.run(flags) +} + +func (opts *ViewSubCommand) run(flags cli.Input) error { + var verbosity int + if flags.Bool("v") && flags.Bool("vv") { + return errors.New("conflicting verbosity levels, use either --v or --vv") + } + if flags.Bool("v") { verbosity = 1 } - if c.Bool("vv") { + if flags.Bool("vv") { verbosity = 2 } - function := c.String("function") - trigger := c.String("trigger") - from := c.String("from") - to := c.String("to") + function := flags.String("function") + trigger := flags.String("trigger") + from := flags.String("from") + to := flags.String("to") //Refuse multiple filters for now if multipleFiltersSpecified(function, trigger, from+to) { - log.Fatal("maximum of one filter is currently supported, either --function, --trigger, or --from,--to") + return errors.New("maximum of one filter is currently supported, either --function, --trigger, or --from,--to") } if len(function) != 0 { - return recordsByFunction(function, verbosity, c) + return recordsByFunction(function, verbosity, flags) } if len(trigger) != 0 { - return recordsByTrigger(trigger, verbosity, c) + return recordsByTrigger(trigger, verbosity, flags) } if len(from) != 0 && len(to) != 0 { - return recordsByTime(from, to, verbosity, c) + return recordsByTime(from, to, verbosity, flags) + } + err := recordsAll(verbosity, flags) + if err != nil { + return errors.Wrap(err, "error viewing records") } - err := recordsAll(verbosity, c) - util.CheckErr(err, "view records") return nil } -func recordsAll(verbosity int, c *cli.Context) error { - fc := util.GetApiClient(c.GlobalString("server")) +func recordsAll(verbosity int, flags cli.Input) error { + fc := util.GetApiClient(flags.GlobalString("server")) records, err := fc.RecordsAll() - util.CheckErr(err, "view records") + if err != nil { + return errors.Wrap(err, "error viewing records") + } showRecords(records, verbosity) return nil } -func recordsByTrigger(trigger string, verbosity int, c *cli.Context) error { - fc := util.GetApiClient(c.GlobalString("server")) +func recordsByTrigger(trigger string, verbosity int, flags cli.Input) error { + fc := util.GetApiClient(flags.GlobalString("server")) records, err := fc.RecordsByTrigger(trigger) - util.CheckErr(err, "view records") + if err != nil { + return errors.Wrap(err, "error viewing records") + } showRecords(records, verbosity) @@ -87,22 +110,26 @@ func recordsByTrigger(trigger string, verbosity int, c *cli.Context) error { } // TODO: More accurate function name (function filter) -func recordsByFunction(function string, verbosity int, c *cli.Context) error { - fc := util.GetApiClient(c.GlobalString("server")) +func recordsByFunction(function string, verbosity int, flags cli.Input) error { + fc := util.GetApiClient(flags.GlobalString("server")) records, err := fc.RecordsByFunction(function) - util.CheckErr(err, "view records") + if err != nil { + return errors.Wrap(err, "error viewing records") + } showRecords(records, verbosity) return nil } -func recordsByTime(from string, to string, verbosity int, c *cli.Context) error { - fc := util.GetApiClient(c.GlobalString("server")) +func recordsByTime(from string, to string, verbosity int, flags cli.Input) error { + fc := util.GetApiClient(flags.GlobalString("server")) records, err := fc.RecordsByTime(from, to) - util.CheckErr(err, "view records") + if err != nil { + return errors.Wrap(err, "error viewing records") + } showRecords(records, verbosity) diff --git a/pkg/fission-cli/cmd/replay/replay.go b/pkg/fission-cli/cmd/replay/replay.go new file mode 100644 index 00000000..f616269c --- /dev/null +++ b/pkg/fission-cli/cmd/replay/replay.go @@ -0,0 +1,67 @@ +/* +Copyright 2019 The Fission Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package replay + +import ( + "fmt" + "os" + "text/tabwriter" + + "github.com/pkg/errors" + + "github.com/fission/fission/pkg/controller/client" + "github.com/fission/fission/pkg/fission-cli/cliwrapper/cli" + "github.com/fission/fission/pkg/fission-cli/cmd" +) + +type ReplaySubCommand struct { + client *client.Client +} + +func Replay(flags cli.Input) error { + opts := ReplaySubCommand{ + client: cmd.GetServer(flags), + } + return opts.do(flags) +} + +func (opts *ReplaySubCommand) do(flags cli.Input) error { + return opts.run(flags) +} + +func (opts *ReplaySubCommand) run(flags cli.Input) error { + reqUID := flags.String("reqUID") + if len(reqUID) == 0 { + return errors.New("Need a reqUID, use --reqUID flag to specify") + } + + responses, err := opts.client.ReplayByReqUID(reqUID) + if err != nil { + return errors.Wrap(err, "error replaying records") + } + + w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0) + + for _, resp := range responses { + fmt.Fprintf(w, "%v", + resp, + ) + } + + w.Flush() + return nil +} diff --git a/pkg/fission-cli/recorder.go b/pkg/fission-cli/recorder.go deleted file mode 100644 index 9cdb327c..00000000 --- a/pkg/fission-cli/recorder.go +++ /dev/null @@ -1,246 +0,0 @@ -/* -Copyright 2018 The Fission Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package fission_cli - -import ( - "fmt" - "os" - "strings" - "text/tabwriter" - - "github.com/satori/go.uuid" - "github.com/urfave/cli" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - fv1 "github.com/fission/fission/pkg/apis/fission.io/v1" - "github.com/fission/fission/pkg/fission-cli/cmd/spec" - "github.com/fission/fission/pkg/fission-cli/log" - "github.com/fission/fission/pkg/fission-cli/util" -) - -func recorderCreate(c *cli.Context) error { - client := util.GetApiClient(c.GlobalString("server")) - - recName := c.String("name") - if len(recName) == 0 { - recName = uuid.NewV4().String() - } - fnName := c.String("function") - triggersOriginal := c.StringSlice("trigger") - - // Function XOR triggers can be given - if len(fnName) == 0 && len(triggersOriginal) == 0 { - log.Fatal("Need to specify at least one function or one trigger, use --function, --trigger") - } - if len(fnName) != 0 && len(triggersOriginal) != 0 { - log.Fatal("Can specify either one function or one or more triggers, but not both") - } - - // TODO: Validate here or elsewhere that all triggers belong to the same namespace - - var triggers []string - if len(triggersOriginal) != 0 { - ts := strings.Split(triggersOriginal[0], ",") - for _, name := range ts { - if len(name) > 0 { - triggers = append(triggers, name) - } - } - } - // TODO: Define appropriate set of policies and defaults - //retPolicy := c.String("retention") - //evictPolicy := c.String("eviction") - - recorder := &fv1.Recorder{ - Metadata: metav1.ObjectMeta{ - Name: recName, - Namespace: "default", - }, - Spec: fv1.RecorderSpec{ - Name: recName, - Function: fnName, - Triggers: triggers, - RetentionPolicy: "Permanent", // TODO: Implement customizable policies for expiration of records - EvictionPolicy: "None", - Enabled: true, - }, - } - - // If we're writing a spec, don't call the API - if c.Bool("spec") { - specFile := fmt.Sprintf("recorder-%v.yaml", recName) - err := spec.SpecSave(*recorder, specFile) - util.CheckErr(err, "create recorder spec") - return nil - } - - _, err := client.RecorderCreate(recorder) - util.CheckErr(err, "create recorder") - - fmt.Printf("recorder '%s' created\n", recName) - return err -} - -func recorderGet(c *cli.Context) error { - client := util.GetApiClient(c.GlobalString("server")) - - recName := c.String("name") - - recorder, err := client.RecorderGet(&metav1.ObjectMeta{ - Name: recName, - Namespace: "default", - }) - - util.CheckErr(err, "get recorder") - - w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0) - - fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\n", - "NAME", "ENABLED", "FUNCTION", "TRIGGERS", "RETENTION_POLICY", "EVICTION_POLICY") - fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\n", - recorder.Metadata.Name, recorder.Spec.Enabled, recorder.Spec.Function, recorder.Spec.Triggers, recorder.Spec.RetentionPolicy, recorder.Spec.EvictionPolicy) - w.Flush() - - return nil -} - -func recorderUpdate(c *cli.Context) error { - client := util.GetApiClient(c.GlobalString("server")) - - recName := c.String("name") - enable := c.Bool("enable") - disable := c.Bool("disable") - //retPolicy := c.String("retention") - //evictPolicy := c.String("eviction") - triggers := c.StringSlice("trigger") - function := c.String("function") - - if enable && disable { - log.Fatal("Cannot enable and disable a recorder simultaneously.") - } - - // Prevent enable or disable while trying to update other fields. These flags must be standalone. - if enable || disable { - if len(triggers) > 0 || len(function) > 0 { - log.Fatal("Enabling or disabling a recorder with other (non-name) flags set is not supported.") - } - } else if len(triggers) == 0 && len(function) == 0 { - log.Fatal("Need to specify either a function or trigger(s) for this recorder") - } - - if len(recName) == 0 { - log.Fatal("Need name of recorder, use --name") - } - - recorder, err := client.RecorderGet(&metav1.ObjectMeta{ - Name: recName, - Namespace: "default", - }) - if err != nil { - util.CheckErr(err, "get recorder") - } - - updated := false - - // TODO: Additional validation on type of supported retention policy, eviction policy - - //if len(retPolicy) > 0 { - // recorder.Spec.RetentionPolicy = retPolicy - // updated = true - //} - //if len(evictPolicy) > 0 { - // recorder.Spec.EvictionPolicy = evictPolicy - // updated = true - //} - if enable { - recorder.Spec.Enabled = true - updated = true - } - - if disable { - recorder.Spec.Enabled = false - updated = true - } - - if len(triggers) > 0 { - var newTriggers []string - triggs := strings.Split(triggers[0], ",") - for _, name := range triggs { - if len(name) > 0 { - newTriggers = append(newTriggers, name) - } - } - recorder.Spec.Triggers = newTriggers - updated = true - } - - if len(function) > 0 { - recorder.Spec.Function = function - updated = true - } - - if !updated { - log.Fatal("Nothing to update. Use --function, --triggers, --enable or --disable") - } - - _, err = client.RecorderUpdate(recorder) - util.CheckErr(err, "update recorder") - - fmt.Printf("recorder '%v' updated\n", recName) - return nil -} - -func recorderDelete(c *cli.Context) error { - client := util.GetApiClient(c.GlobalString("server")) - - recName := c.String("name") - - if len(recName) == 0 { - log.Fatal("Need name of recorder to delete, use --name") - } - - recNs := c.String("recorderns") - - err := client.RecorderDelete(&metav1.ObjectMeta{ - Name: recName, - Namespace: recNs, - }) - - util.CheckErr(err, "delete recorder") - - fmt.Printf("recorder '%v' deleted\n", recName) - return nil -} - -func recorderList(c *cli.Context) error { - client := util.GetApiClient(c.GlobalString("server")) - - recorders, err := client.RecorderList("default") - util.CheckErr(err, "list recorders") - - w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0) - - fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\n", - "NAME", "ENABLED", "FUNCTIONS", "TRIGGERS", "RETENTION_POLICY", "EVICTION_POLICY") - for _, r := range recorders { - fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\n", - r.Metadata.Name, r.Spec.Enabled, r.Spec.Function, r.Spec.Triggers, r.Spec.RetentionPolicy, r.Spec.EvictionPolicy) - } - w.Flush() - - return nil -} diff --git a/pkg/fission-cli/replay.go b/pkg/fission-cli/replay.go deleted file mode 100644 index 68e71d0c..00000000 --- a/pkg/fission-cli/replay.go +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright 2018 The Fission Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package fission_cli - -import ( - "fmt" - "os" - "text/tabwriter" - - "github.com/fission/fission/pkg/fission-cli/log" - "github.com/fission/fission/pkg/fission-cli/util" - "github.com/urfave/cli" -) - -func replay(c *cli.Context) error { - fc := util.GetApiClient(c.GlobalString("server")) - - reqUID := c.String("reqUID") - if len(reqUID) == 0 { - log.Fatal("Need a reqUID, use --reqUID flag to specify") - } - - responses, err := fc.ReplayByReqUID(reqUID) - util.CheckErr(err, "replay records") - - w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0) - - for _, resp := range responses { - fmt.Fprintf(w, "%v", - resp, - ) - } - - w.Flush() - - return nil -}