Skip to content

Commit 941c719

Browse files
authored
Add support for ES REST API as generic commands (opendistro-for-elasticsearch#43)
* Support curl command to support REST API Added curl, get, put, post, delete commands to execute appropriate REST Action. Added integration tests. Since elasticsearch can support output format as json/yaml, allow user to add it as argument. if no value is passed, default by elasticsearch will be returned. curl command accept a filter_path parameter that can be used to filter the response returned by Elasticsearch.
1 parent 3419c3c commit 941c719

32 files changed

+1601
-33
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# we will put our integration testing in this path
2-
INTEGRATION_TEST_PATH=./it
2+
INTEGRATION_TEST_PATH=./it/...
33

44
# set of env variables that you need for testing
55
ENV_LOCAL_TEST=\

commands/curl.go

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package commands
17+
18+
import (
19+
"fmt"
20+
"odfe-cli/client"
21+
ctrl "odfe-cli/controller/es"
22+
entity "odfe-cli/entity/es"
23+
gateway "odfe-cli/gateway/es"
24+
handler "odfe-cli/handler/es"
25+
26+
"github.com/spf13/cobra"
27+
)
28+
29+
const (
30+
curlCommandName = "curl"
31+
curlPrettyFlagName = "pretty"
32+
curlPathFlagName = "path"
33+
curlQueryParamsFlagName = "query-params"
34+
curlDataFlagName = "data"
35+
curlHeadersFlagName = "headers"
36+
curlOutputFormatFlagName = "output-format"
37+
curlOutputFilterPathFlagName = "filter-path"
38+
)
39+
40+
//curlCommand is base command for Elasticsearch REST APIs.
41+
var curlCommand = &cobra.Command{
42+
Use: curlCommandName,
43+
Short: "Manage Elasticsearch core features",
44+
Long: "Use the curl command to execute any REST API calls against the cluster.",
45+
}
46+
47+
func init() {
48+
curlCommand.Flags().BoolP("help", "h", false, "Help for curl command")
49+
curlCommand.PersistentFlags().Bool(curlPrettyFlagName, false, "Response will be formatted")
50+
curlCommand.PersistentFlags().StringP(curlOutputFormatFlagName, "o", "",
51+
"Output format if supported by cluster, else, default format by Elasticsearch. Example json, yaml")
52+
curlCommand.PersistentFlags().StringP(curlOutputFilterPathFlagName, "f", "",
53+
"Filter output fields returned by Elasticsearch. Use comma ',' to separate list of filters")
54+
GetRoot().AddCommand(curlCommand)
55+
}
56+
57+
//GetCurlCommand returns Curl base command, since this will be needed for subcommands
58+
//to add as parent later
59+
func GetCurlCommand() *cobra.Command {
60+
return curlCommand
61+
}
62+
63+
//getCurlHandler returns handler by wiring the dependency manually
64+
func getCurlHandler() (*handler.Handler, error) {
65+
c, err := client.New(nil)
66+
if err != nil {
67+
return nil, err
68+
}
69+
profile, err := GetProfile()
70+
if err != nil {
71+
return nil, err
72+
}
73+
g := gateway.New(c, profile)
74+
facade := ctrl.New(g)
75+
return handler.New(facade), nil
76+
}
77+
78+
//CurlActionExecute executes API based on user request
79+
func CurlActionExecute(input entity.CurlCommandRequest) error {
80+
81+
commandHandler, err := getCurlHandler()
82+
if err != nil {
83+
return err
84+
}
85+
response, err := handler.Curl(commandHandler, input)
86+
if err == nil {
87+
fmt.Println(string(response))
88+
return nil
89+
}
90+
if requestError, ok := err.(*entity.RequestError); ok {
91+
fmt.Println(requestError.GetResponse())
92+
return nil
93+
}
94+
return err
95+
}
96+
97+
func FormatOutput() bool {
98+
isPretty, _ := curlCommand.PersistentFlags().GetBool(curlPrettyFlagName)
99+
return isPretty
100+
}
101+
102+
func GetUserInputAsStringForFlag(flagName string) string {
103+
format, _ := curlCommand.PersistentFlags().GetString(flagName)
104+
return format
105+
}
106+
107+
func Run(cmd cobra.Command, cmdName string) {
108+
input := entity.CurlCommandRequest{
109+
Action: cmdName,
110+
Pretty: FormatOutput(),
111+
OutputFormat: GetUserInputAsStringForFlag(curlOutputFormatFlagName),
112+
OutputFilterPath: GetUserInputAsStringForFlag(curlOutputFilterPathFlagName),
113+
}
114+
input.Path, _ = cmd.Flags().GetString(curlPathFlagName)
115+
input.QueryParams, _ = cmd.Flags().GetString(curlQueryParamsFlagName)
116+
input.Data, _ = cmd.Flags().GetString(curlDataFlagName)
117+
input.Headers, _ = cmd.Flags().GetString(curlHeadersFlagName)
118+
err := CurlActionExecute(input)
119+
DisplayError(err, cmdName)
120+
}

commands/curl_delete.go

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package commands
17+
18+
import (
19+
"github.com/spf13/cobra"
20+
)
21+
22+
const curlDeleteCommandName = "delete"
23+
24+
var curlDeleteExample = `
25+
# Delete a document from an index.
26+
odfe-cli curl delete --path "my-index/_doc/1" \
27+
--query-params "routing=odfe-node1"
28+
`
29+
30+
var curlDeleteCmd = &cobra.Command{
31+
Use: curlDeleteCommandName + " [flags] ",
32+
Short: "Delete command to execute requests against cluster",
33+
Long: "Delete command enables you to run any DELETE API against cluster",
34+
Example: curlDeleteExample,
35+
Run: func(cmd *cobra.Command, args []string) {
36+
Run(*cmd, curlDeleteCommandName)
37+
},
38+
}
39+
40+
func init() {
41+
GetCurlCommand().AddCommand(curlDeleteCmd)
42+
curlDeleteCmd.Flags().StringP(curlPathFlagName, "P", "", "URL path for the REST API")
43+
_ = curlDeleteCmd.MarkFlagRequired(curlPathFlagName)
44+
curlDeleteCmd.Flags().StringP(curlQueryParamsFlagName, "q", "",
45+
"URL query parameters (key & value) for the REST API. Use ‘&’ to separate multiple parameters. Ex: -q \"v=true&s=order:desc,index_patterns\"")
46+
curlDeleteCmd.Flags().StringP(
47+
curlHeadersFlagName, "H", "",
48+
"Headers for the REST API. Consists of case-insensitive name followed by a colon (`:`), then by its value. Use ';' to separate multiple parameters. Ex: -H \"content-type:json;accept-encoding:gzip\"")
49+
curlDeleteCmd.Flags().BoolP("help", "h", false, "Help for curl "+curlDeleteCommandName)
50+
}

commands/curl_get.go

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package commands
17+
18+
import (
19+
"github.com/spf13/cobra"
20+
)
21+
22+
const curlGetCommandName = "get"
23+
24+
var curlGetExample = `
25+
# get document count for an index
26+
odfe-cli curl get --path "_cat/count/my-index-01" --query-params "v=true" --pretty
27+
28+
# get health status of a cluster.
29+
odfe-cli curl get --path "_cluster/health" --pretty --filter-path "status"
30+
31+
# get explanation for cluster allocation for a given index and shard number
32+
odfe-cli curl get --path "_cluster/allocation/explain" \
33+
--data '{
34+
"index": "my-index-01",
35+
"shard": 0,
36+
"primary": false,
37+
"current_node": "nodeA"
38+
}'
39+
`
40+
41+
var curlGetCmd = &cobra.Command{
42+
Use: curlGetCommandName + " [flags] ",
43+
Short: "Get command to execute requests against cluster",
44+
Long: "Get command enables you to run any GET API against cluster",
45+
Example: curlGetExample,
46+
Run: func(cmd *cobra.Command, args []string) {
47+
Run(*cmd, curlGetCommandName)
48+
},
49+
}
50+
51+
func init() {
52+
GetCurlCommand().AddCommand(curlGetCmd)
53+
curlGetCmd.Flags().StringP(curlPathFlagName, "P", "", "URL path for the REST API")
54+
_ = curlGetCmd.MarkFlagRequired(curlPathFlagName)
55+
curlGetCmd.Flags().StringP(curlQueryParamsFlagName, "q", "",
56+
"URL query parameters (key & value) for the REST API. Use ‘&’ to separate multiple parameters. Ex: -q \"v=true&s=order:desc,index_patterns\"")
57+
curlGetCmd.Flags().StringP(
58+
curlDataFlagName, "d", "",
59+
"Data for the REST API. If value starts with '@', the rest should be a file name to read the data from.")
60+
curlGetCmd.Flags().StringP(
61+
curlHeadersFlagName, "H", "",
62+
"Headers for the REST API. Consists of case-insensitive name followed by a colon (`:`), then by its value. Use ';' to separate multiple parameters. Ex: -H \"content-type:json;accept-encoding:gzip\"")
63+
curlGetCmd.Flags().BoolP("help", "h", false, "Help for curl "+curlGetCommandName)
64+
}

commands/curl_post.go

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package commands
17+
18+
import (
19+
"github.com/spf13/cobra"
20+
)
21+
22+
const curlPostCommandName = "post"
23+
24+
var postExample = `
25+
# change the allocation of shards in a cluster.
26+
odfe-cli curl post --path "_cluster/reroute" \
27+
--data '
28+
{
29+
"commands": [
30+
{
31+
"move": {
32+
"index": "odfe-cli", "shard": 0,
33+
"from_node": "odfe-node1", "to_node": "odfe-node2"
34+
}
35+
},
36+
{
37+
"allocate_replica": {
38+
"index": "test", "shard": 1,
39+
"node": "odfe-node3"
40+
}
41+
}
42+
]}' \
43+
--pretty
44+
45+
# insert a document to an index
46+
odfe-cli curl post --path "my-index-01/_doc" \
47+
--data '
48+
{
49+
"message": "insert document",
50+
"ip": {
51+
"address": "127.0.0.1"
52+
}
53+
}'
54+
55+
`
56+
var curlPostCmd = &cobra.Command{
57+
Use: curlPostCommandName + " [flags] ",
58+
Short: "Post command to execute requests against cluster",
59+
Long: "Post command enables you to run any POST API against cluster",
60+
Example: postExample,
61+
Run: func(cmd *cobra.Command, args []string) {
62+
Run(*cmd, curlPostCommandName)
63+
},
64+
}
65+
66+
func init() {
67+
GetCurlCommand().AddCommand(curlPostCmd)
68+
curlPostCmd.Flags().StringP(curlPathFlagName, "P", "", "URL path for the REST API")
69+
_ = curlPostCmd.MarkFlagRequired(curlPathFlagName)
70+
curlPostCmd.Flags().StringP(curlQueryParamsFlagName, "q", "",
71+
"URL query parameters (key & value) for the REST API. Use ‘&’ to separate multiple parameters. Ex: -q \"v=true&s=order:desc,index_patterns\"")
72+
curlPostCmd.Flags().StringP(
73+
curlDataFlagName, "d", "",
74+
"Data for the REST API. If value starts with '@', the rest should be a file name to read the data from.")
75+
curlPostCmd.Flags().StringP(
76+
curlHeadersFlagName, "H", "",
77+
"Headers for the REST API. Consists of case-insensitive name followed by a colon (`:`), then by its value. Use ';' to separate multiple parameters. Ex: -H \"content-type:json;accept-encoding:gzip\"")
78+
curlPostCmd.Flags().BoolP("help", "h", false, "Help for curl "+curlPostCommandName)
79+
}

commands/curl_put.go

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package commands
17+
18+
import (
19+
"github.com/spf13/cobra"
20+
)
21+
22+
const curlPutCommandName = "put"
23+
24+
var curlPutExample = `
25+
# Create a knn index from mapping setting saved in file "knn-mapping.json"
26+
odfe-cli curl put --path "my-knn-index" \
27+
--data "@some-location/knn-mapping.json" \
28+
--pretty
29+
30+
# Update cluster settings transiently
31+
odfe-cli curl put --path "_cluster/settings" \
32+
--query-params "flat_settings=true" \
33+
--data '
34+
{
35+
"transient" : {
36+
"indices.recovery.max_bytes_per_sec" : "20mb"
37+
}
38+
}' \
39+
--pretty
40+
41+
`
42+
43+
var curlPutCmd = &cobra.Command{
44+
Use: curlPutCommandName + " [flags] ",
45+
Short: "Put command to execute requests against cluster",
46+
Long: "Put command enables you to run any PUT API against cluster",
47+
Example: curlPutExample,
48+
Run: func(cmd *cobra.Command, args []string) {
49+
Run(*cmd, curlPutCommandName)
50+
},
51+
}
52+
53+
func init() {
54+
GetCurlCommand().AddCommand(curlPutCmd)
55+
curlPutCmd.Flags().StringP(curlPathFlagName, "P", "", "URL path for the REST API")
56+
_ = curlPutCmd.MarkFlagRequired(curlPathFlagName)
57+
curlPutCmd.Flags().StringP(curlQueryParamsFlagName, "q", "",
58+
"URL query parameters (key & value) for the REST API. Use ‘&’ to separate multiple parameters. Ex: -q \"v=true&s=order:desc,index_patterns\"")
59+
curlPutCmd.Flags().StringP(
60+
curlDataFlagName, "d", "",
61+
"Data for the REST API. If value starts with '@', the rest should be a file name to read the data from.")
62+
curlPutCmd.Flags().StringP(
63+
curlHeadersFlagName, "H", "",
64+
"Headers for the REST API. Consists of case-insensitive name followed by a colon (`:`), then by its value. Use ';' to separate multiple parameters. Ex: -H \"content-type:json;accept-encoding:gzip\"")
65+
curlPutCmd.Flags().BoolP("help", "h", false, "Help for curl "+curlPutCommandName)
66+
}

0 commit comments

Comments
 (0)