Skip to content

Commit 056f4d6

Browse files
authored
deployment: New traffic filter update command (#480)
New command to perform update actions on extensions: - ecctl deployment traffic-filter update <traffic-filter id> {--file <file-path> | --generate-payload}
1 parent 605ca99 commit 056f4d6

12 files changed

+435
-4
lines changed

cmd/deployment/trafficfilter/create.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ var createCmd = &cobra.Command{
4141
var rules []*models.TrafficFilterRule
4242
for _, rule := range source {
4343
rules = append(rules, &models.TrafficFilterRule{
44-
Source: ec.String(rule),
44+
Source: rule,
4545
})
4646
}
4747

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"include_by_default": false,
3+
"name": "1235",
4+
"region": "azure-eastus2",
5+
"rules": [
6+
{
7+
"id": "fad8132b0470479f95c3a41c5d67bd57",
8+
"source": "0.0.0.0/0"
9+
}
10+
],
11+
"type": "ip"
12+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"id": "4e974d9476534d35b12fbdcfd0acee0a"
3+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Licensed to Elasticsearch B.V. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. Elasticsearch B.V. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package cmddeploymenttrafficfilter
19+
20+
import (
21+
"encoding/json"
22+
"errors"
23+
24+
"github.com/elastic/cloud-sdk-go/pkg/api/deploymentapi/trafficfilterapi"
25+
"github.com/elastic/cloud-sdk-go/pkg/models"
26+
"github.com/elastic/cloud-sdk-go/pkg/util/cmdutil"
27+
"github.com/spf13/cobra"
28+
29+
"github.com/elastic/ecctl/pkg/ecctl"
30+
)
31+
32+
const updateExample = `
33+
* Return the current traffic filter state as a valid update payload.
34+
ecctl deployment traffic-filter update <traffic-filter id> --generate-payload > update.json
35+
36+
* After editing the file with your new values pass it as an argument to the --file flag.
37+
ecctl deployment traffic-filter update <traffic-filter id> --file update.json`
38+
39+
var updateCmd = &cobra.Command{
40+
Use: "update <traffic-filter id> {--file <file-path> | --generate-payload}",
41+
Short: "Updates a traffic-filter",
42+
Example: updateExample,
43+
PreRunE: cmdutil.MinimumNArgsAndUUID(1),
44+
RunE: func(cmd *cobra.Command, args []string) error {
45+
genPayload, _ := cmd.Flags().GetBool("generate-payload")
46+
file, _ := cmd.Flags().GetString("file")
47+
48+
if err := flagRequirements(genPayload, file); err != nil {
49+
return err
50+
}
51+
52+
if genPayload {
53+
res, err := trafficfilterapi.Get(trafficfilterapi.GetParams{
54+
API: ecctl.Get().API,
55+
ID: args[0],
56+
})
57+
if err != nil {
58+
return err
59+
}
60+
61+
enc := json.NewEncoder(ecctl.Get().Config.OutputDevice)
62+
enc.SetIndent("", " ")
63+
return enc.Encode(
64+
trafficfilterapi.NewUpdateRequestFromGet(res),
65+
)
66+
}
67+
68+
var req models.TrafficFilterRulesetRequest
69+
if err := cmdutil.DecodeFile(file, &req); err != nil {
70+
return err
71+
}
72+
res, err := trafficfilterapi.Update(trafficfilterapi.UpdateParams{
73+
API: ecctl.Get().API,
74+
ID: args[0],
75+
Req: &req,
76+
})
77+
if err != nil {
78+
return err
79+
}
80+
81+
return ecctl.Get().Formatter.Format("", res)
82+
},
83+
}
84+
85+
func init() {
86+
initUpdateFlags()
87+
}
88+
89+
func initUpdateFlags() {
90+
Command.AddCommand(updateCmd)
91+
updateCmd.Flags().Bool("generate-payload", false, "Outputs JSON which can be used as an argument for the --file flag.")
92+
updateCmd.Flags().String("file", "", "Path to the file containing the update JSON definition.")
93+
updateCmd.MarkFlagFilename("file", "json")
94+
}
95+
96+
func flagRequirements(genPayload bool, file string) error {
97+
if genPayload && file != "" {
98+
return errors.New("both --file and --generate-payload are set. Only one may be used")
99+
}
100+
101+
if !genPayload && file == "" {
102+
return errors.New("one of --file or --generate-payload must be set")
103+
}
104+
105+
return nil
106+
}
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
// Licensed to Elasticsearch B.V. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. Elasticsearch B.V. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package cmddeploymenttrafficfilter
19+
20+
import (
21+
"encoding/json"
22+
"io/ioutil"
23+
"net/url"
24+
"testing"
25+
26+
"github.com/elastic/cloud-sdk-go/pkg/api"
27+
"github.com/elastic/cloud-sdk-go/pkg/api/mock"
28+
"github.com/elastic/cloud-sdk-go/pkg/models"
29+
30+
"github.com/elastic/ecctl/cmd/util/testutils"
31+
)
32+
33+
func Test_updateCmd(t *testing.T) {
34+
updatePayloadRawResp, err := ioutil.ReadFile("./testdata/update-payload.json")
35+
if err != nil {
36+
t.Fatal(err)
37+
}
38+
39+
var succeedPayloadResp = new(models.TrafficFilterRulesetRequest)
40+
if err := succeedPayloadResp.UnmarshalBinary(updatePayloadRawResp); err != nil {
41+
t.Fatal(err)
42+
}
43+
44+
updatePayloadJSONOutput, err := json.MarshalIndent(succeedPayloadResp, "", " ")
45+
if err != nil {
46+
t.Fatal(err)
47+
}
48+
49+
updateRawResp, err := ioutil.ReadFile("./testdata/update.json")
50+
if err != nil {
51+
t.Fatal(err)
52+
}
53+
54+
var succeedResp = new(models.TrafficFilterRulesetResponse)
55+
if err := succeedResp.UnmarshalBinary(updateRawResp); err != nil {
56+
t.Fatal(err)
57+
}
58+
59+
updateJSONOutput, err := json.MarshalIndent(succeedResp, "", " ")
60+
if err != nil {
61+
t.Fatal(err)
62+
}
63+
64+
tests := []struct {
65+
name string
66+
args testutils.Args
67+
want testutils.Assertion
68+
}{
69+
{
70+
name: "fails due to empty argument",
71+
args: testutils.Args{
72+
Cmd: updateCmd,
73+
Args: []string{
74+
"update",
75+
},
76+
Cfg: testutils.MockCfg{Responses: []mock.Response{
77+
mock.SampleInternalError(),
78+
}},
79+
},
80+
want: testutils.Assertion{
81+
Err: `requires at least 1 arg(s), only received 0`,
82+
},
83+
},
84+
{
85+
name: "fails due to missing flags",
86+
args: testutils.Args{
87+
Cmd: updateCmd,
88+
Args: []string{
89+
"update", "4e974d9476534d35b12fbdcfd0acee0a",
90+
},
91+
Cfg: testutils.MockCfg{Responses: []mock.Response{
92+
mock.SampleInternalError(),
93+
}},
94+
},
95+
want: testutils.Assertion{
96+
Err: `one of --file or --generate-payload must be set`,
97+
},
98+
},
99+
{
100+
name: "fails due to mismatching flags",
101+
args: testutils.Args{
102+
Cmd: updateCmd,
103+
Args: []string{
104+
"update", "4e974d9476534d35b12fbdcfd0acee0a", "--generate-payload", "--file", "testdata/update.json",
105+
},
106+
Cfg: testutils.MockCfg{Responses: []mock.Response{
107+
mock.SampleInternalError(),
108+
}},
109+
},
110+
want: testutils.Assertion{
111+
Err: "both --file and --generate-payload are set. Only one may be used",
112+
},
113+
},
114+
{
115+
name: "fails due to API error",
116+
args: testutils.Args{
117+
Cmd: updateCmd,
118+
Args: []string{
119+
"update", "4e974d9476534d35b12fbdcfd0acee0a", "--generate-payload",
120+
},
121+
Cfg: testutils.MockCfg{Responses: []mock.Response{
122+
mock.SampleInternalError(),
123+
}},
124+
},
125+
want: testutils.Assertion{
126+
Err: mock.MultierrorInternalError.Error(),
127+
},
128+
},
129+
{
130+
name: "succeeds to generate payload",
131+
args: testutils.Args{
132+
Cmd: updateCmd,
133+
Args: []string{
134+
"update", "4e974d9476534d35b12fbdcfd0acee0a", "--generate-payload",
135+
},
136+
Cfg: testutils.MockCfg{
137+
OutputFormat: "json",
138+
Responses: []mock.Response{
139+
mock.New200ResponseAssertion(
140+
&mock.RequestAssertion{
141+
Header: api.DefaultReadMockHeaders,
142+
Method: "GET",
143+
Path: "/api/v1/deployments/traffic-filter/rulesets/4e974d9476534d35b12fbdcfd0acee0a",
144+
Host: api.DefaultMockHost,
145+
Query: url.Values{
146+
"include_associations": []string{"false"},
147+
},
148+
},
149+
mock.NewByteBody(updatePayloadRawResp),
150+
),
151+
},
152+
},
153+
},
154+
want: testutils.Assertion{
155+
Stdout: string(updatePayloadJSONOutput) + "\n",
156+
},
157+
},
158+
{
159+
name: "succeeds to update",
160+
args: testutils.Args{
161+
Cmd: updateCmd,
162+
Args: []string{
163+
"update", "4e974d9476534d35b12fbdcfd0acee0a", "--file", "./testdata/update-payload.json",
164+
},
165+
Cfg: testutils.MockCfg{
166+
OutputFormat: "json",
167+
Responses: []mock.Response{
168+
mock.New200ResponseAssertion(
169+
&mock.RequestAssertion{
170+
Header: api.DefaultWriteMockHeaders,
171+
Method: "PUT",
172+
Path: "/api/v1/deployments/traffic-filter/rulesets/4e974d9476534d35b12fbdcfd0acee0a",
173+
Body: mock.NewStringBody(
174+
`{"include_by_default":false,"name":"1235","region":"azure-eastus2","rules":[{"id":"fad8132b0470479f95c3a41c5d67bd57","source":"0.0.0.0/0"}],"type":"ip"}` +
175+
"\n"),
176+
Host: api.DefaultMockHost,
177+
},
178+
mock.NewByteBody(updateRawResp),
179+
),
180+
},
181+
},
182+
},
183+
want: testutils.Assertion{
184+
Stdout: string(updateJSONOutput) + "\n",
185+
},
186+
},
187+
}
188+
for _, tt := range tests {
189+
t.Run(tt.name, func(t *testing.T) {
190+
testutils.RunCmdAssertion(t, tt.args, tt.want)
191+
tt.args.Cmd.ResetFlags()
192+
defer initUpdateFlags()
193+
})
194+
}
195+
}

docs/ecctl-command-reference-index.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ include::ecctl_deployment_traffic-filter_create.adoc[]
6060
include::ecctl_deployment_traffic-filter_delete.adoc[]
6161
include::ecctl_deployment_traffic-filter_list.adoc[]
6262
include::ecctl_deployment_traffic-filter_show.adoc[]
63+
include::ecctl_deployment_traffic-filter_update.adoc[]
6364
include::ecctl_deployment_update.adoc[]
6465
include::ecctl_generate.adoc[]
6566
include::ecctl_generate_completions.adoc[]

docs/ecctl_deployment_traffic-filter.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,4 @@ ecctl deployment traffic-filter [flags]
4747
* xref:ecctl_deployment_traffic-filter_delete[ecctl deployment traffic-filter delete] - Deletes a traffic filter ruleset
4848
* xref:ecctl_deployment_traffic-filter_list[ecctl deployment traffic-filter list] - Lists the traffic filter rulesets
4949
* xref:ecctl_deployment_traffic-filter_show[ecctl deployment traffic-filter show] - Shows information about a traffic filter ruleset
50+
* xref:ecctl_deployment_traffic-filter_update[ecctl deployment traffic-filter update] - Updates a traffic-filter

docs/ecctl_deployment_traffic-filter.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,5 @@ ecctl deployment traffic-filter [flags]
4343
* [ecctl deployment traffic-filter delete](ecctl_deployment_traffic-filter_delete.md) - Deletes a traffic filter ruleset
4444
* [ecctl deployment traffic-filter list](ecctl_deployment_traffic-filter_list.md) - Lists the traffic filter rulesets
4545
* [ecctl deployment traffic-filter show](ecctl_deployment_traffic-filter_show.md) - Shows information about a traffic filter ruleset
46+
* [ecctl deployment traffic-filter update](ecctl_deployment_traffic-filter_update.md) - Updates a traffic-filter
4647

0 commit comments

Comments
 (0)