From 99a65b56a4316b83463a1c16d4d7313420781aef Mon Sep 17 00:00:00 2001 From: Charles Kenney Date: Thu, 21 May 2020 14:49:23 -0400 Subject: [PATCH 1/2] add firewall update method --- firewalls.go | 50 ++++- test/integration/firewall_rules_test.go | 5 - test/integration/firewalls_test.go | 44 ++++ .../fixtures/TestUpdateFirewall.yaml | 204 ++++++++++++++++++ 4 files changed, 293 insertions(+), 10 deletions(-) create mode 100644 test/integration/fixtures/TestUpdateFirewall.yaml diff --git a/firewalls.go b/firewalls.go index 66c20e650..75b45804e 100644 --- a/firewalls.go +++ b/firewalls.go @@ -44,8 +44,24 @@ type FirewallCreateOptions struct { Devices DevicesCreationOptions `json:"devices,omitempty"` } +// FirewallUpdateOptions is an options struct used when Updating a Firewall +type FirewallUpdateOptions struct { + Label string `json:"label,omitempty"` + Status FirewallStatus `json:"status,omitempty"` + Tags *[]string `json:"tags,omitempty"` +} + +// GetUpdateOptions converts a Firewall to FirewallUpdateOptions for use in Client.UpdateFirewall. +func (f *Firewall) GetUpdateOptions() FirewallUpdateOptions { + return FirewallUpdateOptions{ + Label: f.Label, + Status: f.Status, + Tags: &f.Tags, + } +} + // UnmarshalJSON for Firewall responses -func (i *Firewall) UnmarshalJSON(b []byte) error { +func (f *Firewall) UnmarshalJSON(b []byte) error { type Mask Firewall p := struct { @@ -53,16 +69,15 @@ func (i *Firewall) UnmarshalJSON(b []byte) error { Created *parseabletime.ParseableTime `json:"created"` Updated *parseabletime.ParseableTime `json:"updated"` }{ - Mask: (*Mask)(i), + Mask: (*Mask)(f), } if err := json.Unmarshal(b, &p); err != nil { return err } - i.Created = (*time.Time)(p.Created) - i.Updated = (*time.Time)(p.Updated) - + f.Created = (*time.Time)(p.Created) + f.Updated = (*time.Time)(p.Updated) return nil } @@ -141,6 +156,31 @@ func (c *Client) GetFirewall(ctx context.Context, id int) (*Firewall, error) { return r.Result().(*Firewall), nil } +// UpdateFirewall updates a Firewall with the given ID +func (c *Client) UpdateFirewall(ctx context.Context, id int, updateOpts FirewallUpdateOptions) (*Firewall, error) { + e, err := c.Firewalls.Endpoint() + if err != nil { + return nil, err + } + + req := c.R(ctx).SetResult(&Firewall{}) + + bodyData, err := json.Marshal(updateOpts) + if err != nil { + return nil, NewError(err) + } + + body := string(bodyData) + + e = fmt.Sprintf("%s/%d", e, id) + r, err := coupleAPIErrors(req.SetBody(body).Put(e)) + if err != nil { + return nil, err + } + + return r.Result().(*Firewall), nil +} + // DeleteFirewall deletes a single Firewall with the provided ID func (c *Client) DeleteFirewall(ctx context.Context, id int) error { e, err := c.Firewalls.Endpoint() diff --git a/test/integration/firewall_rules_test.go b/test/integration/firewall_rules_test.go index c0299b0e5..1f6a816c2 100644 --- a/test/integration/firewall_rules_test.go +++ b/test/integration/firewall_rules_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "github.com/linode/linodego" ) @@ -25,10 +24,6 @@ var ( } ) -// ignoreNetworkAddresses negates comparing IP addresses. Because of fixture sanitization, -// these addresses will be changed to bogus values when running tests. -var ignoreNetworkAddresses = cmpopts.IgnoreFields(linodego.FirewallRule{}, "Addresses") - func TestGetFirewallRules(t *testing.T) { client, firewall, teardown, err := setupFirewall(t, []firewallModifier{func(createOpts *linodego.FirewallCreateOptions) { createOpts.Rules = testFirewallRuleSet diff --git a/test/integration/firewalls_test.go b/test/integration/firewalls_test.go index f094660b6..8a05fade5 100644 --- a/test/integration/firewalls_test.go +++ b/test/integration/firewalls_test.go @@ -5,6 +5,8 @@ import ( "reflect" "testing" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/linode/linodego" ) @@ -16,6 +18,14 @@ var ( } ) +// ignoreNetworkAddresses negates comparing IP addresses. Because of fixture sanitization, +// these addresses will be changed to bogus values when running tests. +var ignoreNetworkAddresses = cmpopts.IgnoreFields(linodego.FirewallRule{}, "Addresses") + +// ignoreFirewallTimestamps negates comparing created and updated timestamps. Because of +// fixture sanitization, these addresses will be changed to bogus values when running tests. +var ignoreFirewallTimestamps = cmpopts.IgnoreFields(linodego.Firewall{}, "Created", "Updated") + // TestListFirewalls should return a paginated list of Firewalls func TestListFirewalls(t *testing.T) { client, _, teardown, err := setupFirewall(t, []firewallModifier{ @@ -72,6 +82,40 @@ func TestGetFirewall(t *testing.T) { } } +func TestUpdateFirewall(t *testing.T) { + label := randString(12, lowerBytes, upperBytes) + "-linodego-testing" + rules := linodego.FirewallRuleSet{ + Inbound: []linodego.FirewallRule{ + { + Protocol: linodego.ICMP, + Addresses: linodego.NetworkAddresses{ + IPv4: []string{"0.0.0.0/0"}, + }, + }, + }, + } + + client, firewall, teardown, err := setupFirewall(t, []firewallModifier{ + func(createOpts *linodego.FirewallCreateOptions) { + createOpts.Label = label + createOpts.Rules = rules + }, + }, "fixtures/TestUpdateFirewall") + if err != nil { + t.Error(err) + } + defer teardown() + + firewall.Status = linodego.FirewallDisabled + firewall.Label = "updatedFirewallLabel" + firewall.Tags = []string{"newTag"} + if updated, err := client.UpdateFirewall(context.Background(), firewall.ID, firewall.GetUpdateOptions()); err != nil { + t.Error(err) + } else if !cmp.Equal(firewall, updated, ignoreFirewallTimestamps, ignoreNetworkAddresses) { + t.Errorf("expected firewall to have updates but got diff: %s", cmp.Diff(firewall, updated, ignoreFirewallTimestamps, ignoreNetworkAddresses)) + } +} + type firewallModifier func(*linodego.FirewallCreateOptions) func createFirewall(t *testing.T, client *linodego.Client, firewallModifiers ...firewallModifier) (*linodego.Firewall, func(), error) { diff --git a/test/integration/fixtures/TestUpdateFirewall.yaml b/test/integration/fixtures/TestUpdateFirewall.yaml new file mode 100644 index 000000000..30c79db47 --- /dev/null +++ b/test/integration/fixtures/TestUpdateFirewall.yaml @@ -0,0 +1,204 @@ +--- +version: 1 +interactions: +- request: + body: '{"label":"aSTefFgoEhTM-linodego-testing","rules":{"inbound":[{"protocol":"ICMP","addresses":{"ipv4":["10.20.30.40/0"],"ipv6":null}}]},"tags":["testing"],"devices":{}}' + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego 0.12.0 https://github.com/linode/linodego + url: https://api.linode.com/v4beta/networking/firewalls + method: POST + response: + body: '{"id": 246, "label": "aSTefFgoEhTM-linodego-testing", "created": "2018-01-02T03:04:05", + "updated": "2018-01-02T03:04:05", "status": "enabled", "rules": {"inbound": + [{"protocol": "ICMP", "addresses": {"ipv4": ["10.20.30.40/0"], "ipv6": null}}]}, + "tags": ["testing"]}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Cache-Control: + - private, max-age=60, s-maxage=60 + Content-Length: + - "261" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Date: + - Thu, 21 May 2020 18:12:11 GMT + Retry-After: + - "118" + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - firewall:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "1600" + X-Ratelimit-Remaining: + - "1595" + X-Ratelimit-Reset: + - "1590084849" + X-Spec-Version: + - 4.65.1 + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"label":"updatedFirewallLabel","status":"disabled","tags":["newTag"]}' + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego 0.12.0 https://github.com/linode/linodego + url: https://api.linode.com/v4beta/networking/firewalls/246 + method: PUT + response: + body: '{"id": 246, "label": "updatedFirewallLabel", "created": "2018-01-02T03:04:05", + "updated": "2018-01-02T03:04:05", "status": "disabled", "rules": {"inbound": + [{"protocol": "ICMP", "addresses": {"ipv4": ["10.20.30.40/0"], "ipv6": null}}]}, + "tags": ["newTag"]}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Cache-Control: + - private, max-age=60, s-maxage=60 + Content-Length: + - "252" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Date: + - Thu, 21 May 2020 18:12:11 GMT + Retry-After: + - "118" + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - firewall:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "1600" + X-Ratelimit-Remaining: + - "1594" + X-Ratelimit-Reset: + - "1590084850" + X-Spec-Version: + - 4.65.1 + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego 0.12.0 https://github.com/linode/linodego + url: https://api.linode.com/v4beta/networking/firewalls/246 + method: DELETE + response: + body: '{}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Cache-Control: + - private, max-age=60, s-maxage=60 + Content-Length: + - "2" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Date: + - Thu, 21 May 2020 18:12:11 GMT + Retry-After: + - "118" + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - firewall:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "1600" + X-Ratelimit-Remaining: + - "1593" + X-Ratelimit-Reset: + - "1590084850" + X-Spec-Version: + - 4.65.1 + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" From de97c4a5a6d27abf624aac0a005119eb6d3e08db Mon Sep 17 00:00:00 2001 From: Charles Kenney Date: Fri, 22 May 2020 15:57:10 -0400 Subject: [PATCH 2/2] test firewall tag deletion --- test/integration/firewalls_test.go | 24 ++++++++++---- .../fixtures/TestUpdateFirewall.yaml | 32 +++++++++---------- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/test/integration/firewalls_test.go b/test/integration/firewalls_test.go index 8a05fade5..ceadf8486 100644 --- a/test/integration/firewalls_test.go +++ b/test/integration/firewalls_test.go @@ -99,6 +99,7 @@ func TestUpdateFirewall(t *testing.T) { func(createOpts *linodego.FirewallCreateOptions) { createOpts.Label = label createOpts.Rules = rules + createOpts.Tags = []string{"test"} }, }, "fixtures/TestUpdateFirewall") if err != nil { @@ -106,13 +107,24 @@ func TestUpdateFirewall(t *testing.T) { } defer teardown() - firewall.Status = linodego.FirewallDisabled - firewall.Label = "updatedFirewallLabel" - firewall.Tags = []string{"newTag"} - if updated, err := client.UpdateFirewall(context.Background(), firewall.ID, firewall.GetUpdateOptions()); err != nil { + updateOpts := firewall.GetUpdateOptions() + updateOpts.Status = linodego.FirewallDisabled + updateOpts.Label = "updatedFirewallLabel" + updateOpts.Tags = &[]string{} + + updated, err := client.UpdateFirewall(context.Background(), firewall.ID, updateOpts) + if err != nil { t.Error(err) - } else if !cmp.Equal(firewall, updated, ignoreFirewallTimestamps, ignoreNetworkAddresses) { - t.Errorf("expected firewall to have updates but got diff: %s", cmp.Diff(firewall, updated, ignoreFirewallTimestamps, ignoreNetworkAddresses)) + } + + if !cmp.Equal(updated.Tags, *updateOpts.Tags) { + t.Errorf("expected tags to be updated: %s", cmp.Diff(updated.Tags, *updateOpts.Tags)) + } + if updated.Status != updateOpts.Status { + t.Errorf("expected status %s but got %s", updateOpts.Status, updated.Status) + } + if updated.Label != updateOpts.Label { + t.Errorf(`expected label to be "%s" but got "%s"`, updateOpts.Label, updated.Label) } } diff --git a/test/integration/fixtures/TestUpdateFirewall.yaml b/test/integration/fixtures/TestUpdateFirewall.yaml index 30c79db47..e61385468 100644 --- a/test/integration/fixtures/TestUpdateFirewall.yaml +++ b/test/integration/fixtures/TestUpdateFirewall.yaml @@ -2,7 +2,7 @@ version: 1 interactions: - request: - body: '{"label":"aSTefFgoEhTM-linodego-testing","rules":{"inbound":[{"protocol":"ICMP","addresses":{"ipv4":["10.20.30.40/0"],"ipv6":null}}]},"tags":["testing"],"devices":{}}' + body: '{"label":"aSTefFgoEhTM-linodego-testing","rules":{"inbound":[{"protocol":"ICMP","addresses":{"ipv4":["10.20.30.40/0"],"ipv6":null}}]},"tags":["test"],"devices":{}}' form: {} headers: Accept: @@ -14,10 +14,10 @@ interactions: url: https://api.linode.com/v4beta/networking/firewalls method: POST response: - body: '{"id": 246, "label": "aSTefFgoEhTM-linodego-testing", "created": "2018-01-02T03:04:05", + body: '{"id": 250, "label": "aSTefFgoEhTM-linodego-testing", "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", "status": "enabled", "rules": {"inbound": [{"protocol": "ICMP", "addresses": {"ipv4": ["10.20.30.40/0"], "ipv6": null}}]}, - "tags": ["testing"]}' + "tags": ["test"]}' headers: Access-Control-Allow-Credentials: - "true" @@ -32,13 +32,13 @@ interactions: Cache-Control: - private, max-age=60, s-maxage=60 Content-Length: - - "261" + - "258" Content-Security-Policy: - default-src 'none' Content-Type: - application/json Date: - - Thu, 21 May 2020 18:12:11 GMT + - Fri, 22 May 2020 13:35:21 GMT Retry-After: - "118" Server: @@ -61,7 +61,7 @@ interactions: X-Ratelimit-Remaining: - "1595" X-Ratelimit-Reset: - - "1590084849" + - "1590154640" X-Spec-Version: - 4.65.1 X-Xss-Protection: @@ -70,7 +70,7 @@ interactions: code: 200 duration: "" - request: - body: '{"label":"updatedFirewallLabel","status":"disabled","tags":["newTag"]}' + body: '{"label":"updatedFirewallLabel","status":"disabled","tags":[]}' form: {} headers: Accept: @@ -79,13 +79,13 @@ interactions: - application/json User-Agent: - linodego 0.12.0 https://github.com/linode/linodego - url: https://api.linode.com/v4beta/networking/firewalls/246 + url: https://api.linode.com/v4beta/networking/firewalls/250 method: PUT response: - body: '{"id": 246, "label": "updatedFirewallLabel", "created": "2018-01-02T03:04:05", + body: '{"id": 250, "label": "updatedFirewallLabel", "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", "status": "disabled", "rules": {"inbound": [{"protocol": "ICMP", "addresses": {"ipv4": ["10.20.30.40/0"], "ipv6": null}}]}, - "tags": ["newTag"]}' + "tags": []}' headers: Access-Control-Allow-Credentials: - "true" @@ -100,13 +100,13 @@ interactions: Cache-Control: - private, max-age=60, s-maxage=60 Content-Length: - - "252" + - "244" Content-Security-Policy: - default-src 'none' Content-Type: - application/json Date: - - Thu, 21 May 2020 18:12:11 GMT + - Fri, 22 May 2020 13:35:22 GMT Retry-After: - "118" Server: @@ -129,7 +129,7 @@ interactions: X-Ratelimit-Remaining: - "1594" X-Ratelimit-Reset: - - "1590084850" + - "1590154641" X-Spec-Version: - 4.65.1 X-Xss-Protection: @@ -147,7 +147,7 @@ interactions: - application/json User-Agent: - linodego 0.12.0 https://github.com/linode/linodego - url: https://api.linode.com/v4beta/networking/firewalls/246 + url: https://api.linode.com/v4beta/networking/firewalls/250 method: DELETE response: body: '{}' @@ -171,7 +171,7 @@ interactions: Content-Type: - application/json Date: - - Thu, 21 May 2020 18:12:11 GMT + - Fri, 22 May 2020 13:35:22 GMT Retry-After: - "118" Server: @@ -194,7 +194,7 @@ interactions: X-Ratelimit-Remaining: - "1593" X-Ratelimit-Reset: - - "1590084850" + - "1590154641" X-Spec-Version: - 4.65.1 X-Xss-Protection: