Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
CLI: Download Detector Configuration as File (#229)
Browse files Browse the repository at this point in the history
* Refactor cat command

Go provides an abstraction to support writing on file
and stdout as same interface, refactor cat to accept
func as input. Later, download will pass writeInfile as parameter

* CLI: Refactor command to display error

Printing error on console is added everywhere.
Create a function to check error and print.

* Add Download command

Download is similar to cat, excepts it download the configuration and
save it as file with detector name as file name on current folder

* CLI: Add interactive to prompt for overwriting files

Similar to cp from linux, add interactive to allow user to decide
whether to overwrite file or not

* CLI: Allow user to specify output folder path

Added flag to allow user to specify target location where
detectors will be downloaded.

* CLI: Make download prompt as default

Download detector will always prompt if there is file
exists in output directory.
  • Loading branch information
VijayanB authored Sep 17, 2020
1 parent 0fbfeac commit 7d780a8
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 55 deletions.
76 changes: 48 additions & 28 deletions cli/cmd/cat.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import (
entity "esad/internal/entity/ad"
"esad/internal/handler/ad"
"fmt"
"io"
"os"

"github.com/spf13/cobra"
)
Expand All @@ -33,30 +35,35 @@ var catCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
//If no args, display usage
if len(args) < 1 {
if err := cmd.Usage(); err != nil {
fmt.Println(err)
}
displayError(cmd.Usage(), commandCat)
return
}
idStatus, _ := cmd.Flags().GetBool("id")
commandHandler, err := getCommandHandler()
if err != nil {
fmt.Println(err)
}
// default is name
action := ad.GetAnomalyDetectorsByNamePattern
if idStatus {
action = getDetectorsByID
}
results, err := getDetectors(commandHandler, args, action)
if err != nil {
fmt.Println(err)
return
}
printDetectors(results)
err := printDetectors(Println, cmd, args)
displayError(err, commandCat)
},
}

type Display func(*cobra.Command, *entity.DetectorOutput) error

//printDetectors print detectors
func printDetectors(display Display, cmd *cobra.Command, detectors []string) error {
idStatus, _ := cmd.Flags().GetBool("id")
commandHandler, err := getCommandHandler()
if err != nil {
return err
}
// default is name
action := ad.GetAnomalyDetectorsByNamePattern
if idStatus {
action = getDetectorsByID
}
results, err := getDetectors(commandHandler, detectors, action)
if err != nil {
return err
}
return print(cmd, display, results)
}

func getDetectors(
commandHandler *ad.Handler, args []string, get func(*ad.Handler, string) (
[]*entity.DetectorOutput, error)) ([]*entity.DetectorOutput, error) {
Expand All @@ -81,20 +88,33 @@ func getDetectorsByID(commandHandler *ad.Handler, ID string) ([]*entity.Detector
return []*entity.DetectorOutput{output}, nil
}

//printDetectors displays the list of output. Since this is json format, use indent function to
// pretty print before printing on console
func printDetectors(results []*entity.DetectorOutput) {
//print displays the list of output.
func print(cmd *cobra.Command, display Display, results []*entity.DetectorOutput) error {
if results == nil {
return
return nil
}
for _, d := range results {
formattedOutput, err := json.MarshalIndent(d, "", " ")
if err != nil {
fmt.Println(err)
return
if err := display(cmd, d); err != nil {
return err
}
fmt.Println(string(formattedOutput))
}
return nil
}

//FPrint prints detector configuration on writer
//Since this is json format, use indent function to pretty print before printing on writer
func FPrint(writer io.Writer, d *entity.DetectorOutput) error {
formattedOutput, err := json.MarshalIndent(d, "", " ")
if err != nil {
return err
}
_, err = fmt.Fprintln(writer, string(formattedOutput))
return err
}

//Println prints detector configuration on stdout
func Println(cmd *cobra.Command, d *entity.DetectorOutput) error {
return FPrint(os.Stdout, d)
}

func init() {
Expand Down
9 changes: 2 additions & 7 deletions cli/cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,12 @@ var createCmd = &cobra.Command{
}
//If no args, display usage
if len(args) < 1 {
if err := cmd.Usage(); err != nil {
fmt.Println(err)
}
displayError(cmd.Usage(), commandCreate)
return
}
status, _ := cmd.Flags().GetBool(interactive)
err := createDetectors(args, status)
if err != nil {
fmt.Println(commandCreate, "command failed")
fmt.Println("Reason:", err)
}
displayError(err, commandCreate)
},
}

Expand Down
10 changes: 2 additions & 8 deletions cli/cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ package cmd

import (
handler "esad/internal/handler/ad"
"fmt"

"github.com/spf13/cobra"
)
Expand All @@ -30,9 +29,7 @@ var deleteCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
//If no args, display usage
if len(args) < 1 {
if err := cmd.Usage(); err != nil {
fmt.Println(err)
}
displayError(cmd.Usage(), commandDelete)
return
}
force, _ := cmd.Flags().GetBool("force")
Expand All @@ -42,10 +39,7 @@ var deleteCmd = &cobra.Command{
action = handler.DeleteAnomalyDetectorByID
}
err := deleteDetectors(args, force, action)
if err != nil {
fmt.Println(commandDelete, "command failed")
fmt.Println("Reason:", err)
}
displayError(err, commandDelete)
},
}

Expand Down
107 changes: 107 additions & 0 deletions cli/cmd/download.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
* http://www.apache.org/licenses/LICENSE-2.0
* or in the "license" file accompanying this file. This file 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 cmd

import (
entity "esad/internal/entity/ad"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/spf13/cobra"
)

const (
commandDownload = "download"
flagOutput = "output"
fileExtensionJSON = "json"
)

//downloadCmd downloads detectors configuration on current working directory
//based on detector id or name patter
var downloadCmd = &cobra.Command{
Use: commandDownload + " [flags] [list of detectors]",
Short: "Downloads detectors configurations based on id or name pattern",
Long: fmt.Sprintf("Description:\n " +
`Downloads detectors configurations based on id or name pattern, use "" to make sure the name is not matched with pwd lists'`),
Run: func(cmd *cobra.Command, args []string) {
//If no args, display usage
if len(args) < 1 {
displayError(cmd.Usage(), commandDownload)
return
}
err := printDetectors(WriteInFile, cmd, args)
displayError(err, commandDownload)
},
}

//WriteInFile writes detector's configuration on file
//file will be created inside current working directory,
//with detector name as file name
func WriteInFile(cmd *cobra.Command, d *entity.DetectorOutput) error {
output, _ := cmd.Flags().GetString(flagOutput)
if _, err := os.Stat(output); os.IsNotExist(err) {
return fmt.Errorf("output directory [%s] does not exists", output)
}
filePath := filepath.Join(output, fmt.Sprintf("%s.%s", d.Name, fileExtensionJSON))
if ok := isCreateFileAllowed(filePath); !ok {
return nil
}
f, err := os.Create(filePath)
defer func() {
f.Close()
}()
if err != nil {
return err
}
return FPrint(f, d)
}

func isCreateFileAllowed(path string) bool {
if _, err := os.Stat(path); os.IsNotExist(err) {
return true
}
return askForConfirmation(path)
}

func askForConfirmation(path string) bool {

fmt.Printf("overwrite %s? (y/n [n])", filepath.Base(path))
var response string
_, err := fmt.Fscanln(os.Stdin, &response)
if err != nil {
//Exit if for some reason, we are not able to accept user input
fmt.Printf("failed to accept value from user due to %s", err)
return false
}
switch strings.ToLower(response) {
case "y", "yes":
return true
case "n", "no":
return false
default:
return false
}
}

func init() {
esadCmd.AddCommand(downloadCmd)
downloadCmd.Flags().BoolP("name", "", true, "input is name or pattern")
downloadCmd.Flags().BoolP("id", "", false, "input is id")
cwd, err := os.Getwd()
if err != nil {
fmt.Println("failed to find current working directory due to ", err)
}
downloadCmd.Flags().StringP(flagOutput, "o", cwd, "downloads detectors inside this folder path")
}
7 changes: 7 additions & 0 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,10 @@ func GetHandler(c *client.Client, u *client.UserConfig) *handler.Handler {
ctr := controller.New(os.Stdin, esc, g)
return handler.New(ctr)
}

func displayError(err error, cmdName string) {
if err != nil {
fmt.Println(cmdName, "command failed")
fmt.Println("Reason: ", err)
}
}
19 changes: 7 additions & 12 deletions cli/cmd/start_stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ package cmd
import (
"esad/internal/client"
"esad/internal/handler/ad"
"fmt"

"github.com/spf13/cobra"
)
Expand All @@ -32,16 +31,17 @@ var startCmd = &cobra.Command{
Short: "Start detectors based on an ID or name pattern",
Long: `Start detectors based on a pattern. Use "" to make sure the name does not match with pwd lists'`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 1 {
displayError(cmd.Usage(), commandStop)
return
}
idStatus, _ := cmd.Flags().GetBool("id")
action := ad.StartAnomalyDetectorByNamePattern
if idStatus {
action = ad.StartAnomalyDetectorByID
}
err := execute(action, args)
if err != nil {
fmt.Println(commandStart, "command failed")
fmt.Println("Reason:", err)
}
displayError(err, commandStart)
},
}

Expand All @@ -54,9 +54,7 @@ var stopCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
//If no args, display usage
if len(args) < 1 {
if err := cmd.Usage(); err != nil {
fmt.Println(err)
}
displayError(cmd.Usage(), commandStop)
return
}
idStatus, _ := cmd.Flags().GetBool("id")
Expand All @@ -65,10 +63,7 @@ var stopCmd = &cobra.Command{
action = ad.StopAnomalyDetectorByID
}
err := execute(action, args)
if err != nil {
fmt.Println(commandStop, "command failed")
fmt.Println("Reason:", err)
}
displayError(err, commandStop)
},
}

Expand Down

0 comments on commit 7d780a8

Please sign in to comment.