-
Notifications
You must be signed in to change notification settings - Fork 36
AD CLI #196
AD CLI #196
Conversation
Merged esad from private repo to anomaly detection plugin.
Do we plan to use same repo for AD CLI tool? |
Yeah. Recently, SQL CLI is also merged with sql repo. |
Added build and publish artifacts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: why is the go programming language used for cli? if the programming language needs to be different from the rest of the project code, why not python for the equivalent implementation could be simpler?
cli/README.md
Outdated
@@ -0,0 +1,97 @@ | |||
![Test Workflow](https://github.com/VijayanB/esad/workflows/Build%20and%20Test%20Anomaly%20detection%20commandline%20tool/badge.svg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor. is this expected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing it. I removed it for now.
cli/README.md
Outdated
# Open Distro for Elasticsearch AD CLI | ||
|
||
The AD CLI component in Open Distro for Elasticsearch (ODFE) is a command line interface for ODFE AD plugin. | ||
his CLI provides greater flexibility of use. User can use CLI to easily do things that are difficult or sometimes impossible to do with kibana UI. This doesn’t use any additional system resources to load any of graphical part, thus making it simpler and faster than UI. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor. his should be This
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing it. I updated it.
cli/README.md
Outdated
|
||
## Basic Commands | ||
|
||
AN ESAD CLI has following structure |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor. AN should be An
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing it. I updated it in next commit.
cli/cmd/create.go
Outdated
} | ||
|
||
func createDetectors(fileNames []string, status bool) error { | ||
h, err := getCommandHandler() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion. for readability, refrain from using single letters for variable names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i updated it. Nevertheless, accordingly to go style, (https://github.com/golang/go/wiki/CodeReviewComments#variable-names) it is preferred to use short variable name if it is not used below a page down .
cli/cmd/profile.go
Outdated
When you specify a profile to run a command, the settings and credentials are used to run that command. | ||
You can specify a profile in an environment variable (ESAD_PROFILE) which essentially acts as the default profile for commands if default doesn't exists. | ||
The ESAD CLI supports using any of multiple named profiles that are stored in the config and credentials files.`, | ||
//Run: func(cmd *cobra.Command, args []string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor. unused code can be deleted in multiple places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I deleted the commented code. Thanks for pointing out.
cli/cmd/profile.go
Outdated
} | ||
} | ||
|
||
func validProfileName(name string) bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor. the name should be existingprofilename as existing name is not valid for creation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i updated to isProfileExists since it returns bool. But i renamed variable names like you suggested in other place. Can you review it?
cli/cmd/profile.go
Outdated
} | ||
|
||
var safeProfiles []entity.Profile | ||
profiles := getProfiles() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor. retrieving profiles is repeated and can use refactoring.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic to get profiles (load file, deserialization ) is a single method, just that it is being used in multiple use cases. In this particular case, i had split logic to delete actual profile and verify whether the profile is valid or not . This will make logic easy to maintain than having both use cases entangled.
cli/cmd/profile.go
Outdated
profiles := getProfiles() | ||
for _, p := range profiles { | ||
safe := true | ||
for _, name := range validProfiles { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question. does go have libraries for data structures other than array?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unfortunately no. It has map and slice(array). Usually if there is need for processing lot of data, go recommends concurrent programming by spanning multiple threads. My guess is since go is not meant for data processing and keep language simple and opinionated, it doesn't have data structures like java.
Fixed Code review comments
Update publish only tags are created.
I believe, there is a plan to have one cli in long run and at the same time we saw sql cli/ sql odbc individual repos are being integrated into their plugin repo to have single release and simplify maintenance and engage community in the mean time. |
thanks for the reasonable answers. I am neither for nor against the decision. Just for the record, aws cli is based on python. Also, for an open source project, a more popular/familiar programming language might invite more contributions. |
const mockDetectorName = "detector" | ||
|
||
func helperLoadBytes(t *testing.T, name string) []byte { | ||
path := filepath.Join("testdata", name) // relative path |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question. is it also a go convention to not have a separate directory/module for test code & data?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you mean test file and implementation? Yes, go uses package oriented design, hence code/test/testdata should always be close to each other. There is no guideline but i observed this being used in many of application tool
like gofmt / https://golang.org/src/cmd/gofmt/
Codecov Report
@@ Coverage Diff @@
## master #196 +/- ##
=========================================
Coverage ? 79.74%
=========================================
Files ? 6
Lines ? 622
Branches ? 0
=========================================
Hits ? 496
Misses ? 77
Partials ? 49
Flags with carried forward coverage won't be shown. Click here to find out more. Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion. making pr smaller helps reviewers.
cli/README.md
Outdated
|
||
It only supports [Open Distro for Elasticsearch (ODFE) AD Plugin](https://opendistro.github.io/for-elasticsearch-docs/docs/ad/) | ||
You must have the ODFE AD plugin installed to your Elasticsearch instance to connect. | ||
Users can run this CLI from MacOS and Linux, and connect to any valid Elasticsearch end-point such as Amazon Elasticsearch Service (AES).The ESAD CLI implements AD APIs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor. MacOS and Linux and Windows
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack.
suggestion. making pr smaller helps reviewers.
Definitely, i started as proof of concept and later i added features. I will definitely create small commits and multiple PR to make sure that code is easy to review .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forgot to add "Windows" or this CLI can't support windows?
cli/cmd/stop.go
Outdated
if idStatus { | ||
action = ad.StopAnomalyDetectorByID | ||
} | ||
err := execute(action, args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question. why execute is not needed here but needed in start?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Go's package scope allows us to refer to the method outside file as long as it is inside same package. I created one file per command but start and stop shares lot. Now i merged both the command in single file to keep code together.
cli/internal/controller/ad/ad.go
Outdated
if bar != nil { | ||
bar.Incr() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor. should increment be done after an operation is finished?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. I updated it. Thanks for pointing it.
cli/internal/controller/ad/ad.go
Outdated
res, err := c.gateway.StopDetector(ctx, id) // ignore error | ||
if err != nil { | ||
return err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue. the error looks like not ignored.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the comment. Initially i was ignoring but later decided to respect and return the same
cli/internal/controller/ad/ad.go
Outdated
if err != nil { | ||
log.Fatal(err) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue. is it safe to continue with a fatal error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. It is not. I updated it.
cli/internal/controller/ad/ad.go
Outdated
if len(detectors) < 1 { | ||
return | ||
} | ||
var deleted []entity.Detector |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor. this name should be undeleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. I fixed it now.
- endpoint: https://odfe-node1:9200 | ||
username: admin | ||
password: foobar | ||
name: dev |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What will happen if I use same name for two profiles. Will throw error or override the prior one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no validation if file is specified by user. We only have validation if user is creating a profile. Let's say if user has two profile with same name and this config is passed as parameter to the command, it will not override instead, it will select the first profile which matches the name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, so we have error handling for this case. How about add this in readme doc and the help doc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added it in readme.
``` | ||
For example to start detector: | ||
``` | ||
$ esad start [detector-name-pattern] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems the commands not follow same name convention. esad profile create
looks like esad <resource> <action>
, but here esad start [detector-name-pattern]
looks like esad <action> <resource>
. Is it possible to follow same name convention? Maybe consult Carlos to confirm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, esad, profile, create, start are commands. You can think like tree structure.
esad -> profile -> create
-> delete
-> Create
-> Start
Anyway, i asked carlos to provide feedback. If i didn't receive anything related to that, i will bring this point.
Thanks for feedback.
``` | ||
To get the version of the ESAD CLI: | ||
``` | ||
$ esad --version |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about adding version compatibility table in the help doc and read-me doc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. I will add it.
cli/internal/gateway/es/es.go
Outdated
return endpoint, nil | ||
} | ||
|
||
func (g *gateway) Search(ctx context.Context, index string, field string) ([]byte, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is to get distinct value of one field. How about we rename this method as "SearchDistinctValue"? So I don't need to read the method implementation to learn this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack
* express or implied. See the License for the specific language governing | ||
* permissions and limitations under the License. | ||
*/ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the name convention for the file name? I see this ES controller named as "controller.go", but AD controller named as "ad.go". And I see five "ad.go" files in these folders: entity, mapper, gateway, handler, controller. Is it common practice in Go to use same file name and differentiate them with package path?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I renamed to es.go. In Go, file name is not as vital as package name. I have seen in most of the popular go repositories, they use file name as same as package name. Yes, package is considered as module than file.
cli/internal/controller/ad/ad.go
Outdated
} | ||
} | ||
|
||
func (c controller) SearchDetectorByName(ctx context.Context, name string) ([]entity.Detector, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add comments for public methods ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACk
cli/internal/controller/ad/ad.go
Outdated
return nil | ||
} | ||
|
||
func (c controller) StopDetectorByName(ctx context.Context, pattern string, display bool) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor: how about move StartDetectorByName
method before StopDetectorByName
? This can improve a little bit code readability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACk.
cli/internal/controller/ad/ad.go
Outdated
return c.processDetectorByAction(ctx, pattern, "stop", c.StopDetector, display) | ||
} | ||
|
||
//DeleteDetector deletes detector |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor: this simple comment looks like some duplicate of the method name "DeleteDetector". How about add more details like what we will do for force is true/false? Suggest to check other places too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack
//go:generate mockgen -destination=mocks/mock_ad.go -package=mocks . Controller | ||
|
||
//Controller is an interface for ES Cluster to get distinct values | ||
type Controller interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename it as EsController
considering we have AnomalyDetectorController
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From Go Wiki:
All references to names in your package will be done using the package name, so you can omit that name from the identifiers. For example, if you are in package chubby, you don't need type ChubbyFile, which clients will write as chubby.ChubbyFile. Instead, name the type File, which clients will write as chubby.File.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I renamed adController to Controller to keep consistent.
) | ||
|
||
const ( | ||
baseURL = "_opendistro/_anomaly_detection/detectors" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should write const in uppercase letters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately in GO, it is recommended to write like this. If const will be exported, then we can capitalize the first character.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, interesting way to use naming convention to control access.
cli/README.md
Outdated
1. Install from source: | ||
|
||
``` | ||
$ go get github.com/VijayanB/esad/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we change the repo ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing it out.
cli/internal/handler/ad/ad.go
Outdated
return h.CreateAnomalyDetector(fileName, interactive) | ||
} | ||
|
||
//GenerateAnomalyDetector creates detector based on file configurations |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrong comments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACk
1) Move methods close to help readability 2) Add comments to exported methods 3) Fix typo and copied comments 4) Rename file name
if !isProfileExists(name) { | ||
break | ||
} | ||
fmt.Println("profile", name, "already exists.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about print all existing profile names as well? So user don't need to guess which name is not used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They can use list command to get profile names. Do you still think that providing all names will be good for UX?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool. Let's keep it as is.
cli/cmd/profile.go
Outdated
} else { | ||
response = getUserInputAsText() | ||
} | ||
if len(response) < 1 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about we abstract this as validate func and pass it into getUserInput
? So it's flexible to support more parameter's validation logic in future. As currently we have only one simple validation, don't need to make it too complicated currently. Add some todo is fine. It's up to you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
func deleteProfiles(names []string) { | ||
|
||
var existingProfileNames []string | ||
for _, name := range names { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not so efficient to run isProfileExists
for every profile name, which will getProfile
and do iteration to check if name exists. How about read all profiles, construct map (key: profile name, value: profile), then remove deleted profiles ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK.
cli/cmd/start_stop.go
Outdated
commandStop = "stop" | ||
) | ||
|
||
// createCmd represents the create command |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrong comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. I removed it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. I removed it now.
}, | ||
} | ||
|
||
var stopCmd = &cobra.Command{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing comment ? How about add comments for every command?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typically in go, comment is required for exported variables or functions. Here we are not exporting stopCmd. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments can help others include ourselves to understand code quickly. I think we should add some basic description for these commands like what the command does, what's the input/output, potential exceptions. There are already some description for this command for line 47-48. If you don't like too much comment, how about make line48 more accurate as "Stops detectors based on detector id or detector name pattern, ...."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it. I added comments and updated help as well.
return features, nil | ||
} | ||
|
||
func getUnit(request string) (*string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We only support minutes on AD Kibana. Should we support more unit options in AD CLI? If user use "Hours" to create index with CLI, then AD Kibana may not show it correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now, create command fails with response "Hours is not supported". In my opinion this is more as restriction in backend than frontend. What do you think? Do we plan to support this in future? if so then having this will have old clients to use this format.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently we have no plan to support other unit options. How about we support only minutes here to keep consistent with AD Kibana (support minutes only) and backend (supports minutes/seconds, code).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. Will keep only minutes and will raise error for other cases
cli/internal/mapper/ad/ad.go
Outdated
featureCount += len(f.AggregationType) * len(f.Field) | ||
} | ||
if featureCount == 0 || featureCount > featureCountLimit { | ||
return fmt.Errorf("trying to create %d feautes, only upto %d features are allowed", featureCount, featureCountLimit) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor: typo, feautes
-> features
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
cli/cmd/start_stop.go
Outdated
return | ||
} | ||
idStatus, _ := cmd.Flags().GetBool("id") | ||
action := ad.StopAnomalyDetector |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about rename as "StopAnomalyDetectorByNamePattern" considering this method "StopAnomalyDetectorByID"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Rename Method name to include byNamePattern Support only minutes for interval Added extra comments
ESAD Version 0.1 to interact with AD Plugin through cli.
Merged esad from private repo to anomaly detection plugin.
Issue #, if available:
Description of changes:
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.