diff --git a/account_events.go b/account_events.go index f5e3d6ecc..b3c9c51eb 100644 --- a/account_events.go +++ b/account_events.go @@ -74,6 +74,13 @@ const ( ActionDNSZoneCreate EventAction = "dns_zone_create" ActionDNSZoneDelete EventAction = "dns_zone_delete" ActionDNSZoneUpdate EventAction = "dns_zone_update" + ActionFirewallCreate EventAction = "firewall_create" + ActionFirewallDelete EventAction = "firewall_delete" + ActionFirewallDisable EventAction = "firewall_disable" + ActionFirewallEnable EventAction = "firewall_enable" + ActionFirewallUpdate EventAction = "firewall_update" + ActionFirewallDeviceAdd EventAction = "firewall_device_add" + ActionFirewallDeviceRemove EventAction = "firewall_device_remove" ActionHostReboot EventAction = "host_reboot" ActionImageDelete EventAction = "image_delete" ActionImageUpdate EventAction = "image_update" @@ -141,6 +148,7 @@ const ( EntityLinode EntityType = "linode" EntityDisk EntityType = "disk" EntityDomain EntityType = "domain" + EntityFirewall EntityType = "firewall" EntityNodebalancer EntityType = "nodebalancer" ) diff --git a/client.go b/client.go index cd75ee876..c36bf429d 100644 --- a/client.go +++ b/client.go @@ -58,6 +58,7 @@ type Client struct { Domains *Resource Events *Resource Firewalls *Resource + FirewallDevices *Resource IPAddresses *Resource IPv6Pools *Resource IPv6Ranges *Resource @@ -261,6 +262,7 @@ func addResources(client *Client) { domainsName: NewResource(client, domainsName, domainsEndpoint, false, Domain{}, DomainsPagedResponse{}), eventsName: NewResource(client, eventsName, eventsEndpoint, false, Event{}, EventsPagedResponse{}), firewallsName: NewResource(client, firewallsName, firewallsEndpoint, false, Firewall{}, FirewallsPagedResponse{}), + firewallDevicesName: NewResource(client, firewallDevicesName, firewallDevicesEndpoint, true, FirewallDevice{}, FirewallDevicesPagedResponse{}), imagesName: NewResource(client, imagesName, imagesEndpoint, false, Image{}, ImagesPagedResponse{}), instanceConfigsName: NewResource(client, instanceConfigsName, instanceConfigsEndpoint, true, InstanceConfig{}, InstanceConfigsPagedResponse{}), instanceDisksName: NewResource(client, instanceDisksName, instanceDisksEndpoint, true, InstanceDisk{}, InstanceDisksPagedResponse{}), @@ -312,6 +314,7 @@ func addResources(client *Client) { client.Domains = resources[domainsName] client.Events = resources[eventsName] client.Firewalls = resources[firewallsName] + client.FirewallDevices = resources[firewallDevicesName] client.IPAddresses = resources[ipaddressesName] client.IPv6Pools = resources[ipv6poolsName] client.IPv6Ranges = resources[ipv6rangesName] diff --git a/firewall_devices.go b/firewall_devices.go new file mode 100644 index 000000000..f05ad7c6d --- /dev/null +++ b/firewall_devices.go @@ -0,0 +1,141 @@ +package linodego + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/linode/linodego/internal/parseabletime" +) + +// FirewallDeviceType represents the different kinds of devices governable by a Firewall +type FirewallDeviceType string + +// FirewallDeviceType constants start with FirewallDevice +const ( + FirewallDeviceLinode FirewallDeviceType = "linode" + FirewallDeviceNodeBalancer FirewallDeviceType = "nodebalancer" +) + +// FirewallDevice represents a device governed by a Firewall +type FirewallDevice struct { + ID int `json:"id"` + Entity FirewallDeviceEntity `json:"entity"` + Created *time.Time `json:"-"` + Updated *time.Time `json:"-"` +} + +// FirewallDeviceCreateOptions fields are those accepted by CreateFirewallDevice +type FirewallDeviceCreateOptions struct { + ID int `json:"id"` + Type FirewallDeviceType `json:"type"` +} + +// UnmarshalJSON implements the json.Unmarshaler interface +func (device *FirewallDevice) UnmarshalJSON(b []byte) error { + type Mask FirewallDevice + + p := struct { + *Mask + Created *parseabletime.ParseableTime `json:"created"` + Updated *parseabletime.ParseableTime `json:"updated"` + }{ + Mask: (*Mask)(device), + } + + if err := json.Unmarshal(b, &p); err != nil { + return err + } + + device.Created = (*time.Time)(p.Created) + device.Updated = (*time.Time)(p.Updated) + return nil +} + +// FirewallDeviceEntity contains information about a device associated with a Firewall +type FirewallDeviceEntity struct { + ID int `json:"id"` + Type FirewallDeviceType `json:"type"` + Label string `json:"label"` + URL string `json:"url"` +} + +// FirewallDevicesPagedResponse represents a Linode API response for FirewallDevices +type FirewallDevicesPagedResponse struct { + *PageOptions + Data []FirewallDevice `json:"data"` +} + +// endpointWithID gets the endpoint URL for FirewallDevices of a given Firewall +func (FirewallDevicesPagedResponse) endpointWithID(c *Client, id int) string { + endpoint, err := c.FirewallDevices.endpointWithID(id) + if err != nil { + panic(err) + } + return endpoint +} + +func (resp *FirewallDevicesPagedResponse) appendData(r *FirewallDevicesPagedResponse) { + resp.Data = append(resp.Data, r.Data...) +} + +// ListFirewallDevices get devices associated with a given Firewall +func (c *Client) ListFirewallDevices(ctx context.Context, firewallID int, opts *ListOptions) ([]FirewallDevice, error) { + response := FirewallDevicesPagedResponse{} + err := c.listHelperWithID(ctx, &response, firewallID, opts) + + if err != nil { + return nil, err + } + return response.Data, nil +} + +// GetFirewallDevice gets a FirewallDevice given an ID +func (c *Client) GetFirewallDevice(ctx context.Context, firewallID, deviceID int) (*FirewallDevice, error) { + e, err := c.FirewallDevices.endpointWithID(firewallID) + if err != nil { + return nil, err + } + + e = fmt.Sprintf("%s/%d", e, deviceID) + r, err := coupleAPIErrors(c.R(ctx).SetResult(&FirewallDevice{}).Get(e)) + if err != nil { + return nil, err + } + return r.Result().(*FirewallDevice), nil +} + +// AddFirewallDevice associates a Device with a given Firewall +func (c *Client) CreateFirewallDevice(ctx context.Context, firewallID int, createOpts FirewallDeviceCreateOptions) (*FirewallDevice, error) { + var body string + e, err := c.FirewallDevices.endpointWithID(firewallID) + if err != nil { + return nil, err + } + + req := c.R(ctx).SetResult(&FirewallDevice{}) + if bodyData, err := json.Marshal(createOpts); err == nil { + body = string(bodyData) + } else { + return nil, NewError(err) + } + + r, err := coupleAPIErrors(req.SetBody(body).Post(e)) + if err != nil { + return nil, err + } + return r.Result().(*FirewallDevice), nil +} + +// DeleteFirewallDevice disassociates a Device with a given Firewall +func (c *Client) DeleteFirewallDevice(ctx context.Context, firewallID, deviceID int) error { + e, err := c.FirewallDevices.endpointWithID(firewallID) + if err != nil { + return err + } + + e = fmt.Sprintf("%s/%d", e, deviceID) + _, err = coupleAPIErrors(c.R(ctx).Delete(e)) + return err +} diff --git a/firewalls.go b/firewalls.go index 9052464f5..66c20e650 100644 --- a/firewalls.go +++ b/firewalls.go @@ -32,8 +32,8 @@ type Firewall struct { // DevicesCreationOptions fields are used when adding devices during the Firewall creation process. type DevicesCreationOptions struct { - Linodes []string `json:"linodes,omitempty"` - NodeBalancers []string `json:"nodebalancers,omitempty"` + Linodes []int `json:"linodes,omitempty"` + NodeBalancers []int `json:"nodebalancers,omitempty"` } // FirewallCreateOptions fields are those accepted by CreateFirewall diff --git a/pagination.go b/pagination.go index 734c7dbb7..f11175302 100644 --- a/pagination.go +++ b/pagination.go @@ -324,6 +324,12 @@ func (c *Client) listHelperWithID(ctx context.Context, i interface{}, idRaw inte results = response.Results v.appendData(response) } + case *FirewallDevicesPagedResponse: + if r, err = coupleAPIErrors(req.SetResult(FirewallDevicesPagedResponse{}).Get(v.endpointWithID(c, id))); err == nil { + pages = r.Result().(*FirewallDevicesPagedResponse).Pages + results = r.Result().(*FirewallDevicesPagedResponse).Results + v.appendData(r.Result().(*FirewallDevicesPagedResponse)) + } case *InstanceConfigsPagedResponse: if r, err = coupleAPIErrors(req.SetResult(InstanceConfigsPagedResponse{}).Get(v.endpointWithID(c, id))); err == nil { pages = r.Result().(*InstanceConfigsPagedResponse).Pages diff --git a/resources.go b/resources.go index d2c42d23f..8dd925f1c 100644 --- a/resources.go +++ b/resources.go @@ -16,6 +16,7 @@ const ( domainsName = "domains" eventsName = "events" firewallsName = "firewalls" + firewallDevicesName = "firewalldevices" imagesName = "images" instanceConfigsName = "configs" instanceDisksName = "disks" @@ -65,6 +66,7 @@ const ( domainsEndpoint = "domains" eventsEndpoint = "account/events" firewallsEndpoint = "networking/firewalls" + firewallDevicesEndpoint = "networking/firewalls/{{ .ID }}/devices" imagesEndpoint = "images" instanceConfigsEndpoint = "linode/instances/{{ .ID }}/configs" instanceDisksEndpoint = "linode/instances/{{ .ID }}/disks" diff --git a/test/integration/firewalls_devices_test.go b/test/integration/firewalls_devices_test.go new file mode 100644 index 000000000..c61feb208 --- /dev/null +++ b/test/integration/firewalls_devices_test.go @@ -0,0 +1,98 @@ +package integration + +import ( + "context" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/linode/linodego" +) + +func TestListFirewallDevices(t *testing.T) { + client, instance, teardown, err := setupInstance(t, "fixtures/TestListFirewallDevices") + if err != nil { + t.Error(err) + } + defer teardown() + + firewall, teardownFirewall, err := createFirewall(t, client, func(opts *linodego.FirewallCreateOptions) { + opts.Devices.Linodes = []int{instance.ID} + }) + if err != nil { + t.Error(err) + } + defer teardownFirewall() + + firewallDevices, err := client.ListFirewallDevices(context.Background(), firewall.ID, nil) + if err != nil { + t.Error(err) + } + + if len(firewallDevices) != 1 { + t.Errorf("expected 1 firewall device but got %d", len(firewallDevices)) + } +} + +func TestGetFirewallDevice(t *testing.T) { + client, instance, teardown, err := setupInstance(t, "fixtures/TestGetFirewallDevice") + if err != nil { + t.Error(err) + } + defer teardown() + + firewall, teardownFirewall, err := createFirewall(t, client) + if err != nil { + t.Error(err) + } + defer teardownFirewall() + + firewallDevice, err := client.CreateFirewallDevice(context.Background(), firewall.ID, linodego.FirewallDeviceCreateOptions{ + Type: linodego.FirewallDeviceLinode, + ID: instance.ID, + }) + if err != nil { + t.Error(err) + } + + if device, err := client.GetFirewallDevice(context.Background(), firewall.ID, firewallDevice.ID); err != nil { + t.Error(err) + } else if !cmp.Equal(device, firewallDevice) { + t.Errorf("expected device to match create result but got diffs: %s", cmp.Diff(device, firewallDevice)) + } +} + +func TestDeleteFirewallDevice(t *testing.T) { + client, instance, teardown, err := setupInstance(t, "fixtures/TestDeleteFirewallDevice") + if err != nil { + t.Error(err) + } + defer teardown() + + firewall, teardownFirewall, err := createFirewall(t, client) + if err != nil { + t.Error(err) + } + defer teardownFirewall() + + firewallDevice, err := client.CreateFirewallDevice(context.Background(), firewall.ID, linodego.FirewallDeviceCreateOptions{ + Type: linodego.FirewallDeviceLinode, + ID: instance.ID, + }) + if err != nil { + t.Error(err) + } + + assertDateSet(t, firewallDevice.Created) + assertDateSet(t, firewallDevice.Updated) + + if err := client.DeleteFirewallDevice(context.Background(), firewall.ID, firewallDevice.ID); err != nil { + t.Error(err) + } + + if _, getErr := client.GetFirewallDevice(context.Background(), firewall.ID, firewallDevice.ID); err != nil { + t.Error("expected fetching firewall device to fail") + } else if apiError, ok := getErr.(*linodego.Error); !ok || apiError.Code != http.StatusNotFound { + t.Errorf("expected fetching firewall device to throw Not Found but got: %s", getErr) + } +} diff --git a/test/integration/firewalls_test.go b/test/integration/firewalls_test.go index b49a0acdb..f094660b6 100644 --- a/test/integration/firewalls_test.go +++ b/test/integration/firewalls_test.go @@ -74,10 +74,8 @@ func TestGetFirewall(t *testing.T) { type firewallModifier func(*linodego.FirewallCreateOptions) -func setupFirewall(t *testing.T, firewallModifiers []firewallModifier, fixturesYaml string) (*linodego.Client, *linodego.Firewall, func(), error) { +func createFirewall(t *testing.T, client *linodego.Client, firewallModifiers ...firewallModifier) (*linodego.Firewall, func(), error) { t.Helper() - var fixtureTeardown func() - client, fixtureTeardown := createTestClient(t, fixturesYaml) createOpts := testFirewallCreateOpts for _, modifier := range firewallModifiers { @@ -86,13 +84,24 @@ func setupFirewall(t *testing.T, firewallModifiers []firewallModifier, fixturesY firewall, err := client.CreateFirewall(context.Background(), createOpts) if err != nil { - t.Errorf("Error creating Firewall, expected struct, got error %v", err) + t.Errorf("failed to create firewall: %s", err) } teardown := func() { if err := client.DeleteFirewall(context.Background(), firewall.ID); err != nil { - t.Errorf("Expected to delete a Firewall, but got %v", err) + t.Errorf("failed to delete firewall: %s", err) } + } + return firewall, teardown, nil +} + +func setupFirewall(t *testing.T, firewallModifiers []firewallModifier, fixturesYaml string) (*linodego.Client, *linodego.Firewall, func(), error) { + t.Helper() + client, fixtureTeardown := createTestClient(t, fixturesYaml) + firewall, firewallTeardown, err := createFirewall(t, client, firewallModifiers...) + + teardown := func() { + firewallTeardown() fixtureTeardown() } return client, firewall, teardown, err diff --git a/test/integration/fixtures/TestDeleteFirewallDevice.yaml b/test/integration/fixtures/TestDeleteFirewallDevice.yaml new file mode 100644 index 000000000..381887865 --- /dev/null +++ b/test/integration/fixtures/TestDeleteFirewallDevice.yaml @@ -0,0 +1,458 @@ +--- +version: 1 +interactions: +- request: + body: '{"region":"us-west","type":"g6-nanode-1","label":"linodego-test-instance","root_pass":"R34lBAdP455","image":"linode/debian9","booted":false}' + 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/linode/instances + method: POST + response: + body: '{"id": 20346485, "label": "linodego-test-instance", "group": "", "status": + "provisioning", "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", + "type": "g6-nanode-1", "ipv4": ["10.20.30.40"], "ipv6": "1234::5678/64", + "image": "linode/debian9", "region": "us-west", "specs": {"disk": 25600, "memory": + 1024, "vcpus": 1, "gpus": 0, "transfer": 1000}, "alerts": {"cpu": 90, "network_in": + 10, "network_out": 10, "transfer_quota": 80, "io": 10000}, "backups": {"enabled": + false, "schedule": {"day": null, "window": null}, "last_successful": null}, + "hypervisor": "kvm", "watchdog_enabled": true, "tags": []}' + 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: + - "636" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Date: + - Mon, 04 May 2020 22:55:32 GMT + Retry-After: + - "117" + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "1600" + X-Ratelimit-Remaining: + - "1599" + X-Ratelimit-Reset: + - "1588633050" + X-Spec-Version: + - 4.64.0 + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"label":"label","rules":{"inbound":[{"ports":"22","protocol":"TCP","addresses":{"ipv4":["10.20.30.40/0"],"ipv6":["1234::5678/0"]}}],"outbound":[{"ports":"22","protocol":"TCP","addresses":{"ipv4":["10.20.30.40/0"],"ipv6":["1234::5678/0"]}}]},"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": 191, "label": "label", "created": "2018-01-02T03:04:05", "updated": + "2018-01-02T03:04:05", "status": "enabled", "rules": {"inbound": [{"ports": + "22", "protocol": "TCP", "addresses": {"ipv4": ["10.20.30.40/0"], "ipv6": ["1234::5678/0"]}}], + "outbound": [{"ports": "22", "protocol": "TCP", "addresses": {"ipv4": ["10.20.30.40/0"], + "ipv6": ["1234::5678/0"]}}]}, "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: + - "363" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Date: + - Mon, 04 May 2020 22:55:33 GMT + Retry-After: + - "117" + 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: + - "1598" + X-Ratelimit-Reset: + - "1588633051" + X-Spec-Version: + - 4.64.0 + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"id":20346485,"type":"linode"}' + 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/191/devices + method: POST + response: + body: '{"id": 2685, "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", + "entity": {"id": 20346485, "type": "linode", "label": "linodego-test-instance", + "url": "/v4/linode/instances/20346485"}}' + 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: + - "201" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Date: + - Mon, 04 May 2020 22:55:33 GMT + Retry-After: + - "116" + 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: + - "1597" + X-Ratelimit-Reset: + - "1588633050" + X-Spec-Version: + - 4.64.0 + 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/191/devices/2685 + 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: + - Mon, 04 May 2020 22:55:33 GMT + Retry-After: + - "116" + 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: + - "1596" + X-Ratelimit-Reset: + - "1588633050" + X-Spec-Version: + - 4.64.0 + 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/191/devices/2685 + method: GET + response: + body: '{"errors": [{"reason": "Not found"}]}' + headers: + 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: + - '*' + Cache-Control: + - private, max-age=0, s-maxage=0, no-cache, no-store + Content-Length: + - "37" + Content-Type: + - application/json + Date: + - Mon, 04 May 2020 22:55:33 GMT + Retry-After: + - "116" + Server: + - nginx + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - firewall:read_only + X-Frame-Options: + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "1600" + X-Ratelimit-Remaining: + - "1595" + X-Ratelimit-Reset: + - "1588633050" + X-Spec-Version: + - 4.64.0 + status: 404 Not Found + code: 404 + 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/191 + 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: + - Mon, 04 May 2020 22:55:33 GMT + Retry-After: + - "116" + 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: + - "1588633050" + X-Spec-Version: + - 4.64.0 + 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/linode/instances/20346485 + 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: + - Mon, 04 May 2020 22:55:34 GMT + Retry-After: + - "116" + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes: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: + - "1588633051" + X-Spec-Version: + - 4.64.0 + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" diff --git a/test/integration/fixtures/TestGetFirewallDevice.yaml b/test/integration/fixtures/TestGetFirewallDevice.yaml new file mode 100644 index 000000000..b435d0004 --- /dev/null +++ b/test/integration/fixtures/TestGetFirewallDevice.yaml @@ -0,0 +1,410 @@ +--- +version: 1 +interactions: +- request: + body: '{"region":"us-west","type":"g6-nanode-1","label":"linodego-test-instance","root_pass":"R34lBAdP455","image":"linode/debian9","booted":false}' + 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/linode/instances + method: POST + response: + body: '{"id": 20346491, "label": "linodego-test-instance", "group": "", "status": + "provisioning", "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", + "type": "g6-nanode-1", "ipv4": ["10.20.30.40"], "ipv6": "1234::5678/64", + "image": "linode/debian9", "region": "us-west", "specs": {"disk": 25600, "memory": + 1024, "vcpus": 1, "gpus": 0, "transfer": 1000}, "alerts": {"cpu": 90, "network_in": + 10, "network_out": 10, "transfer_quota": 80, "io": 10000}, "backups": {"enabled": + false, "schedule": {"day": null, "window": null}, "last_successful": null}, + "hypervisor": "kvm", "watchdog_enabled": true, "tags": []}' + 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: + - "635" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Date: + - Mon, 04 May 2020 22:56:25 GMT + Retry-After: + - "64" + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "1600" + X-Ratelimit-Remaining: + - "1587" + X-Ratelimit-Reset: + - "1588633050" + X-Spec-Version: + - 4.64.0 + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"label":"label","rules":{"inbound":[{"ports":"22","protocol":"TCP","addresses":{"ipv4":["10.20.30.40/0"],"ipv6":["1234::5678/0"]}}],"outbound":[{"ports":"22","protocol":"TCP","addresses":{"ipv4":["10.20.30.40/0"],"ipv6":["1234::5678/0"]}}]},"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": 193, "label": "label", "created": "2018-01-02T03:04:05", "updated": + "2018-01-02T03:04:05", "status": "enabled", "rules": {"inbound": [{"ports": + "22", "protocol": "TCP", "addresses": {"ipv4": ["10.20.30.40/0"], "ipv6": ["1234::5678/0"]}}], + "outbound": [{"ports": "22", "protocol": "TCP", "addresses": {"ipv4": ["10.20.30.40/0"], + "ipv6": ["1234::5678/0"]}}]}, "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: + - "363" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Date: + - Mon, 04 May 2020 22:56:25 GMT + Retry-After: + - "64" + 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: + - "1586" + X-Ratelimit-Reset: + - "1588633050" + X-Spec-Version: + - 4.64.0 + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"id":20346491,"type":"linode"}' + 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/193/devices + method: POST + response: + body: '{"id": 2687, "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", + "entity": {"id": 20346491, "type": "linode", "label": "linodego-test-instance", + "url": "/v4/linode/instances/20346491"}}' + 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: + - "201" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Date: + - Mon, 04 May 2020 22:56:26 GMT + Retry-After: + - "64" + 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: + - "1585" + X-Ratelimit-Reset: + - "1588633051" + X-Spec-Version: + - 4.64.0 + 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/193/devices/2687 + method: GET + response: + body: '{"id": 2687, "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", + "entity": {"id": 20346491, "type": "linode", "label": "linodego-test-instance", + "url": "/v4/linode/instances/20346491"}}' + 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=0, s-maxage=0, no-cache, no-store + - private, max-age=60, s-maxage=60 + Content-Length: + - "201" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Date: + - Mon, 04 May 2020 22:56:26 GMT + Retry-After: + - "64" + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - firewall:read_only + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "1600" + X-Ratelimit-Remaining: + - "1584" + X-Ratelimit-Reset: + - "1588633051" + X-Spec-Version: + - 4.64.0 + 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/193 + 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: + - Mon, 04 May 2020 22:56:26 GMT + Retry-After: + - "63" + 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: + - "1583" + X-Ratelimit-Reset: + - "1588633050" + X-Spec-Version: + - 4.64.0 + 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/linode/instances/20346491 + 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: + - Mon, 04 May 2020 22:56:26 GMT + Retry-After: + - "63" + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "1600" + X-Ratelimit-Remaining: + - "1582" + X-Ratelimit-Reset: + - "1588633050" + X-Spec-Version: + - 4.64.0 + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" diff --git a/test/integration/fixtures/TestListFirewallDevices.yaml b/test/integration/fixtures/TestListFirewallDevices.yaml new file mode 100644 index 000000000..ee68041c0 --- /dev/null +++ b/test/integration/fixtures/TestListFirewallDevices.yaml @@ -0,0 +1,344 @@ +--- +version: 1 +interactions: +- request: + body: '{"region":"us-west","type":"g6-nanode-1","label":"linodego-test-instance","root_pass":"R34lBAdP455","image":"linode/debian9","booted":false}' + 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/linode/instances + method: POST + response: + body: '{"id": 20346486, "label": "linodego-test-instance", "group": "", "status": + "provisioning", "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", + "type": "g6-nanode-1", "ipv4": ["10.20.30.40"], "ipv6": "1234::5678/64", + "image": "linode/debian9", "region": "us-west", "specs": {"disk": 25600, "memory": + 1024, "vcpus": 1, "gpus": 0, "transfer": 1000}, "alerts": {"cpu": 90, "network_in": + 10, "network_out": 10, "transfer_quota": 80, "io": 10000}, "backups": {"enabled": + false, "schedule": {"day": null, "window": null}, "last_successful": null}, + "hypervisor": "kvm", "watchdog_enabled": true, "tags": []}' + 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: + - "636" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Date: + - Mon, 04 May 2020 22:56:06 GMT + Retry-After: + - "83" + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "1600" + X-Ratelimit-Remaining: + - "1592" + X-Ratelimit-Reset: + - "1588633050" + X-Spec-Version: + - 4.64.0 + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"label":"label","rules":{"inbound":[{"ports":"22","protocol":"TCP","addresses":{"ipv4":["10.20.30.40/0"],"ipv6":["1234::5678/0"]}}],"outbound":[{"ports":"22","protocol":"TCP","addresses":{"ipv4":["10.20.30.40/0"],"ipv6":["1234::5678/0"]}}]},"tags":["testing"],"devices":{"linodes":[20346486]}}' + 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": 192, "label": "label", "created": "2018-01-02T03:04:05", "updated": + "2018-01-02T03:04:05", "status": "enabled", "rules": {"inbound": [{"ports": + "22", "protocol": "TCP", "addresses": {"ipv4": ["10.20.30.40/0"], "ipv6": ["1234::5678/0"]}}], + "outbound": [{"ports": "22", "protocol": "TCP", "addresses": {"ipv4": ["10.20.30.40/0"], + "ipv6": ["1234::5678/0"]}}]}, "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: + - "363" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Date: + - Mon, 04 May 2020 22:56:07 GMT + Retry-After: + - "83" + 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: + - "1591" + X-Ratelimit-Reset: + - "1588633051" + X-Spec-Version: + - 4.64.0 + 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/192/devices + method: GET + response: + body: '{"data": [{"id": 2686, "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", + "entity": {"id": 20346486, "type": "linode", "label": "linodego-test-instance", + "url": "/v4/linode/instances/20346486"}}], "page": 1, "pages": 1, "results": + 1}' + 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=0, s-maxage=0, no-cache, no-store + - private, max-age=60, s-maxage=60 + Content-Length: + - "250" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Date: + - Mon, 04 May 2020 22:56:07 GMT + Retry-After: + - "83" + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - firewall:read_only + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "1600" + X-Ratelimit-Remaining: + - "1590" + X-Ratelimit-Reset: + - "1588633051" + X-Spec-Version: + - 4.64.0 + 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/192 + 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: + - Mon, 04 May 2020 22:56:07 GMT + Retry-After: + - "82" + 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: + - "1589" + X-Ratelimit-Reset: + - "1588633050" + X-Spec-Version: + - 4.64.0 + 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/linode/instances/20346486 + 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: + - Mon, 04 May 2020 22:56:07 GMT + Retry-After: + - "82" + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "1600" + X-Ratelimit-Remaining: + - "1588" + X-Ratelimit-Reset: + - "1588633050" + X-Spec-Version: + - 4.64.0 + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: ""