Skip to content

Commit

Permalink
Add support for Firewalls. (#145)
Browse files Browse the repository at this point in the history
* Add support for Firewalls.

* Fix chagelog PR id.

* Add link to public api docs.
  • Loading branch information
viola authored Jun 6, 2017
1 parent 1bbce10 commit 4fa9e9d
Show file tree
Hide file tree
Showing 4 changed files with 1,115 additions and 1 deletion.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Change Log

## [v1.1.0] - 2017-06-06

### Added
- #145 Add FirewallsService for managing Firewalls with the DigitalOcean API. - @viola
- #139 Add TTL field to the Domains. - @xmudrii

### Fixed
- #143 Fix oauth2.NoContext depreciation. - @jbowens
- #141 Fix DropletActions on tagged resources. - @xmudrii

## [v1.0.0] - 2017-03-10

### Added
Expand Down
263 changes: 263 additions & 0 deletions firewalls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
package godo

import (
"path"
"strconv"

"github.com/digitalocean/godo/context"
)

const firewallsBasePath = "/v2/firewalls"

// FirewallsService is an interface for managing Firewalls with the DigitalOcean API.
// See: https://developers.digitalocean.com/documentation/documentation/v2/#firewalls
type FirewallsService interface {
Get(context.Context, string) (*Firewall, *Response, error)
Create(context.Context, *FirewallRequest) (*Firewall, *Response, error)
Update(context.Context, string, *FirewallRequest) (*Firewall, *Response, error)
Delete(context.Context, string) (*Response, error)
List(context.Context, *ListOptions) ([]Firewall, *Response, error)
ListByDroplet(context.Context, int, *ListOptions) ([]Firewall, *Response, error)
AddDroplets(context.Context, string, ...int) (*Response, error)
RemoveDroplets(context.Context, string, ...int) (*Response, error)
AddTags(context.Context, string, ...string) (*Response, error)
RemoveTags(context.Context, string, ...string) (*Response, error)
AddRules(context.Context, string, *FirewallRulesRequest) (*Response, error)
RemoveRules(context.Context, string, *FirewallRulesRequest) (*Response, error)
}

// FirewallsServiceOp handles communication with Firewalls methods of the DigitalOcean API.
type FirewallsServiceOp struct {
client *Client
}

// Firewall represents a DigitalOcean Firewall configuration.
type Firewall struct {
ID string `json:"id"`
Name string `json:"name"`
Status string `json:"status"`
InboundRules []InboundRule `json:"inbound_rules"`
OutboundRules []OutboundRule `json:"outbound_rules"`
DropletIDs []int `json:"droplet_ids"`
Tags []string `json:"tags"`
Created string `json:"created_at"`
PendingChanges []PendingChange `json:"pending_changes"`
}

// String creates a human-readable description of a Firewall.
func (fw Firewall) String() string {
return Stringify(fw)
}

// FirewallRequest represents the configuration to be applied to an existing or a new Firewall.
type FirewallRequest struct {
Name string `json:"name"`
InboundRules []InboundRule `json:"inbound_rules"`
OutboundRules []OutboundRule `json:"outbound_rules"`
DropletIDs []int `json:"droplet_ids"`
Tags []string `json:"tags"`
}

// FirewallRulesRequest represents rules configuration to be applied to an existing Firewall.
type FirewallRulesRequest struct {
InboundRules []InboundRule `json:"inbound_rules"`
OutboundRules []OutboundRule `json:"outbound_rules"`
}

// InboundRule represents a DigitalOcean Firewall inbound rule.
type InboundRule struct {
Protocol string `json:"protocol,omitempty"`
PortRange string `json:"ports,omitempty"`
Sources *Sources `json:"sources"`
}

// OutboundRule represents a DigitalOcean Firewall outbound rule.
type OutboundRule struct {
Protocol string `json:"protocol,omitempty"`
PortRange string `json:"ports,omitempty"`
Destinations *Destinations `json:"destinations"`
}

// Sources represents a DigitalOcean Firewall InboundRule sources.
type Sources struct {
Addresses []string `json:"addresses,omitempty"`
Tags []string `json:"tags,omitempty"`
DropletIDs []int `json:"droplet_ids,omitempty"`
LoadBalancerUIDs []string `json:"load_balancer_uids,omitempty"`
}

// PendingChange represents a DigitalOcean Firewall status details.
type PendingChange struct {
DropletID int `json:"droplet_id,omitempty"`
Removing bool `json:"removing,omitempty"`
Status string `json:"status,omitempty"`
}

// Destinations represents a DigitalOcean Firewall OutboundRule destinations.
type Destinations struct {
Addresses []string `json:"addresses,omitempty"`
Tags []string `json:"tags,omitempty"`
DropletIDs []int `json:"droplet_ids,omitempty"`
LoadBalancerUIDs []string `json:"load_balancer_uids,omitempty"`
}

var _ FirewallsService = &FirewallsServiceOp{}

// Get an existing Firewall by its identifier.
func (fw *FirewallsServiceOp) Get(ctx context.Context, fID string) (*Firewall, *Response, error) {
path := path.Join(firewallsBasePath, fID)

req, err := fw.client.NewRequest(ctx, "GET", path, nil)
if err != nil {
return nil, nil, err
}

root := new(firewallRoot)
resp, err := fw.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}

return root.Firewall, resp, err
}

// Create a new Firewall with a given configuration.
func (fw *FirewallsServiceOp) Create(ctx context.Context, fr *FirewallRequest) (*Firewall, *Response, error) {
req, err := fw.client.NewRequest(ctx, "POST", firewallsBasePath, fr)
if err != nil {
return nil, nil, err
}

root := new(firewallRoot)
resp, err := fw.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}

return root.Firewall, resp, err
}

// Update an existing Firewall with new configuration.
func (fw *FirewallsServiceOp) Update(ctx context.Context, fID string, fr *FirewallRequest) (*Firewall, *Response, error) {
path := path.Join(firewallsBasePath, fID)

req, err := fw.client.NewRequest(ctx, "PUT", path, fr)
if err != nil {
return nil, nil, err
}

root := new(firewallRoot)
resp, err := fw.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}

return root.Firewall, resp, err
}

// Delete a Firewall by its identifier.
func (fw *FirewallsServiceOp) Delete(ctx context.Context, fID string) (*Response, error) {
path := path.Join(firewallsBasePath, fID)
return fw.createAndDoReq(ctx, "DELETE", path, nil)
}

// List Firewalls.
func (fw *FirewallsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Firewall, *Response, error) {
path, err := addOptions(firewallsBasePath, opt)
if err != nil {
return nil, nil, err
}

return fw.listHelper(ctx, path)
}

// ListByDroplet Firewalls.
func (fw *FirewallsServiceOp) ListByDroplet(ctx context.Context, dID int, opt *ListOptions) ([]Firewall, *Response, error) {
basePath := path.Join(dropletBasePath, strconv.Itoa(dID), "firewalls")
path, err := addOptions(basePath, opt)
if err != nil {
return nil, nil, err
}

return fw.listHelper(ctx, path)
}

// AddDroplets to a Firewall.
func (fw *FirewallsServiceOp) AddDroplets(ctx context.Context, fID string, dropletIDs ...int) (*Response, error) {
path := path.Join(firewallsBasePath, fID, "droplets")
return fw.createAndDoReq(ctx, "POST", path, &dropletsRequest{IDs: dropletIDs})
}

// RemoveDroplets from a Firewall.
func (fw *FirewallsServiceOp) RemoveDroplets(ctx context.Context, fID string, dropletIDs ...int) (*Response, error) {
path := path.Join(firewallsBasePath, fID, "droplets")
return fw.createAndDoReq(ctx, "DELETE", path, &dropletsRequest{IDs: dropletIDs})
}

// AddTags to a Firewall.
func (fw *FirewallsServiceOp) AddTags(ctx context.Context, fID string, tags ...string) (*Response, error) {
path := path.Join(firewallsBasePath, fID, "tags")
return fw.createAndDoReq(ctx, "POST", path, &tagsRequest{Tags: tags})
}

// RemoveTags from a Firewall.
func (fw *FirewallsServiceOp) RemoveTags(ctx context.Context, fID string, tags ...string) (*Response, error) {
path := path.Join(firewallsBasePath, fID, "tags")
return fw.createAndDoReq(ctx, "DELETE", path, &tagsRequest{Tags: tags})
}

// AddRules to a Firewall.
func (fw *FirewallsServiceOp) AddRules(ctx context.Context, fID string, rr *FirewallRulesRequest) (*Response, error) {
path := path.Join(firewallsBasePath, fID, "rules")
return fw.createAndDoReq(ctx, "POST", path, rr)
}

// RemoveRules from a Firewall.
func (fw *FirewallsServiceOp) RemoveRules(ctx context.Context, fID string, rr *FirewallRulesRequest) (*Response, error) {
path := path.Join(firewallsBasePath, fID, "rules")
return fw.createAndDoReq(ctx, "DELETE", path, rr)
}

type dropletsRequest struct {
IDs []int `json:"droplet_ids"`
}

type tagsRequest struct {
Tags []string `json:"tags"`
}

type firewallRoot struct {
Firewall *Firewall `json:"firewall"`
}

type firewallsRoot struct {
Firewalls []Firewall `json:"firewalls"`
Links *Links `json:"links"`
}

func (fw *FirewallsServiceOp) createAndDoReq(ctx context.Context, method, path string, v interface{}) (*Response, error) {
req, err := fw.client.NewRequest(ctx, method, path, v)
if err != nil {
return nil, err
}

return fw.client.Do(ctx, req, nil)
}

func (fw *FirewallsServiceOp) listHelper(ctx context.Context, path string) ([]Firewall, *Response, error) {
req, err := fw.client.NewRequest(ctx, "GET", path, nil)
if err != nil {
return nil, nil, err
}

root := new(firewallsRoot)
resp, err := fw.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
if l := root.Links; l != nil {
resp.Links = l
}

return root.Firewalls, resp, err
}
Loading

0 comments on commit 4fa9e9d

Please sign in to comment.