kops/vendor/github.com/hetznercloud/hcloud-go/hcloud/firewall.go

371 lines
11 KiB
Go

package hcloud
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"net"
"net/url"
"strconv"
"time"
"github.com/hetznercloud/hcloud-go/hcloud/schema"
)
// Firewall represents a Firewall in the Hetzner Cloud.
type Firewall struct {
ID int
Name string
Labels map[string]string
Created time.Time
Rules []FirewallRule
AppliedTo []FirewallResource
}
// FirewallRule represents a Firewall's rules.
type FirewallRule struct {
Direction FirewallRuleDirection
SourceIPs []net.IPNet
DestinationIPs []net.IPNet
Protocol FirewallRuleProtocol
Port *string
Description *string
}
// FirewallRuleDirection specifies the direction of a Firewall rule.
type FirewallRuleDirection string
const (
// FirewallRuleDirectionIn specifies a rule for inbound traffic.
FirewallRuleDirectionIn FirewallRuleDirection = "in"
// FirewallRuleDirectionOut specifies a rule for outbound traffic.
FirewallRuleDirectionOut FirewallRuleDirection = "out"
)
// FirewallRuleProtocol specifies the protocol of a Firewall rule.
type FirewallRuleProtocol string
const (
// FirewallRuleProtocolTCP specifies a TCP rule.
FirewallRuleProtocolTCP FirewallRuleProtocol = "tcp"
// FirewallRuleProtocolUDP specifies a UDP rule.
FirewallRuleProtocolUDP FirewallRuleProtocol = "udp"
// FirewallRuleProtocolICMP specifies an ICMP rule.
FirewallRuleProtocolICMP FirewallRuleProtocol = "icmp"
// FirewallRuleProtocolESP specifies an esp rule.
FirewallRuleProtocolESP FirewallRuleProtocol = "esp"
// FirewallRuleProtocolGRE specifies an gre rule.
FirewallRuleProtocolGRE FirewallRuleProtocol = "gre"
)
// FirewallResourceType specifies the resource to apply a Firewall on.
type FirewallResourceType string
const (
// FirewallResourceTypeServer specifies a Server.
FirewallResourceTypeServer FirewallResourceType = "server"
// FirewallResourceTypeLabelSelector specifies a LabelSelector.
FirewallResourceTypeLabelSelector FirewallResourceType = "label_selector"
)
// FirewallResource represents a resource to apply the new Firewall on.
type FirewallResource struct {
Type FirewallResourceType
Server *FirewallResourceServer
LabelSelector *FirewallResourceLabelSelector
}
// FirewallResourceServer represents a Server to apply a Firewall on.
type FirewallResourceServer struct {
ID int
}
// FirewallResourceLabelSelector represents a LabelSelector to apply a Firewall on.
type FirewallResourceLabelSelector struct {
Selector string
}
// FirewallClient is a client for the Firewalls API.
type FirewallClient struct {
client *Client
}
// GetByID retrieves a Firewall by its ID. If the Firewall does not exist, nil is returned.
func (c *FirewallClient) GetByID(ctx context.Context, id int) (*Firewall, *Response, error) {
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/firewalls/%d", id), nil)
if err != nil {
return nil, nil, err
}
var body schema.FirewallGetResponse
resp, err := c.client.Do(req, &body)
if err != nil {
if IsError(err, ErrorCodeNotFound) {
return nil, resp, nil
}
return nil, nil, err
}
return FirewallFromSchema(body.Firewall), resp, nil
}
// GetByName retrieves a Firewall by its name. If the Firewall does not exist, nil is returned.
func (c *FirewallClient) GetByName(ctx context.Context, name string) (*Firewall, *Response, error) {
if name == "" {
return nil, nil, nil
}
firewalls, response, err := c.List(ctx, FirewallListOpts{Name: name})
if len(firewalls) == 0 {
return nil, response, err
}
return firewalls[0], response, err
}
// Get retrieves a Firewall by its ID if the input can be parsed as an integer, otherwise it
// retrieves a Firewall by its name. If the Firewall does not exist, nil is returned.
func (c *FirewallClient) Get(ctx context.Context, idOrName string) (*Firewall, *Response, error) {
if id, err := strconv.Atoi(idOrName); err == nil {
return c.GetByID(ctx, id)
}
return c.GetByName(ctx, idOrName)
}
// FirewallListOpts specifies options for listing Firewalls.
type FirewallListOpts struct {
ListOpts
Name string
Sort []string
}
func (l FirewallListOpts) values() url.Values {
vals := l.ListOpts.Values()
if l.Name != "" {
vals.Add("name", l.Name)
}
for _, sort := range l.Sort {
vals.Add("sort", sort)
}
return vals
}
// List returns a list of Firewalls for a specific page.
//
// Please note that filters specified in opts are not taken into account
// when their value corresponds to their zero value or when they are empty.
func (c *FirewallClient) List(ctx context.Context, opts FirewallListOpts) ([]*Firewall, *Response, error) {
path := "/firewalls?" + opts.values().Encode()
req, err := c.client.NewRequest(ctx, "GET", path, nil)
if err != nil {
return nil, nil, err
}
var body schema.FirewallListResponse
resp, err := c.client.Do(req, &body)
if err != nil {
return nil, nil, err
}
firewalls := make([]*Firewall, 0, len(body.Firewalls))
for _, s := range body.Firewalls {
firewalls = append(firewalls, FirewallFromSchema(s))
}
return firewalls, resp, nil
}
// All returns all Firewalls.
func (c *FirewallClient) All(ctx context.Context) ([]*Firewall, error) {
return c.AllWithOpts(ctx, FirewallListOpts{ListOpts: ListOpts{PerPage: 50}})
}
// AllWithOpts returns all Firewalls for the given options.
func (c *FirewallClient) AllWithOpts(ctx context.Context, opts FirewallListOpts) ([]*Firewall, error) {
allFirewalls := []*Firewall{}
err := c.client.all(func(page int) (*Response, error) {
opts.Page = page
firewalls, resp, err := c.List(ctx, opts)
if err != nil {
return resp, err
}
allFirewalls = append(allFirewalls, firewalls...)
return resp, nil
})
if err != nil {
return nil, err
}
return allFirewalls, nil
}
// FirewallCreateOpts specifies options for creating a new Firewall.
type FirewallCreateOpts struct {
Name string
Labels map[string]string
Rules []FirewallRule
ApplyTo []FirewallResource
}
// Validate checks if options are valid.
func (o FirewallCreateOpts) Validate() error {
if o.Name == "" {
return errors.New("missing name")
}
return nil
}
// FirewallCreateResult is the result of a create Firewall call.
type FirewallCreateResult struct {
Firewall *Firewall
Actions []*Action
}
// Create creates a new Firewall.
func (c *FirewallClient) Create(ctx context.Context, opts FirewallCreateOpts) (FirewallCreateResult, *Response, error) {
if err := opts.Validate(); err != nil {
return FirewallCreateResult{}, nil, err
}
reqBody := firewallCreateOptsToSchema(opts)
reqBodyData, err := json.Marshal(reqBody)
if err != nil {
return FirewallCreateResult{}, nil, err
}
req, err := c.client.NewRequest(ctx, "POST", "/firewalls", bytes.NewReader(reqBodyData))
if err != nil {
return FirewallCreateResult{}, nil, err
}
respBody := schema.FirewallCreateResponse{}
resp, err := c.client.Do(req, &respBody)
if err != nil {
return FirewallCreateResult{}, resp, err
}
result := FirewallCreateResult{
Firewall: FirewallFromSchema(respBody.Firewall),
Actions: ActionsFromSchema(respBody.Actions),
}
return result, resp, nil
}
// FirewallUpdateOpts specifies options for updating a Firewall.
type FirewallUpdateOpts struct {
Name string
Labels map[string]string
}
// Update updates a Firewall.
func (c *FirewallClient) Update(ctx context.Context, firewall *Firewall, opts FirewallUpdateOpts) (*Firewall, *Response, error) {
reqBody := schema.FirewallUpdateRequest{}
if opts.Name != "" {
reqBody.Name = &opts.Name
}
if opts.Labels != nil {
reqBody.Labels = &opts.Labels
}
reqBodyData, err := json.Marshal(reqBody)
if err != nil {
return nil, nil, err
}
path := fmt.Sprintf("/firewalls/%d", firewall.ID)
req, err := c.client.NewRequest(ctx, "PUT", path, bytes.NewReader(reqBodyData))
if err != nil {
return nil, nil, err
}
respBody := schema.FirewallUpdateResponse{}
resp, err := c.client.Do(req, &respBody)
if err != nil {
return nil, resp, err
}
return FirewallFromSchema(respBody.Firewall), resp, nil
}
// Delete deletes a Firewall.
func (c *FirewallClient) Delete(ctx context.Context, firewall *Firewall) (*Response, error) {
req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("/firewalls/%d", firewall.ID), nil)
if err != nil {
return nil, err
}
return c.client.Do(req, nil)
}
// FirewallSetRulesOpts specifies options for setting rules of a Firewall.
type FirewallSetRulesOpts struct {
Rules []FirewallRule
}
// SetRules sets the rules of a Firewall.
func (c *FirewallClient) SetRules(ctx context.Context, firewall *Firewall, opts FirewallSetRulesOpts) ([]*Action, *Response, error) {
reqBody := firewallSetRulesOptsToSchema(opts)
reqBodyData, err := json.Marshal(reqBody)
if err != nil {
return nil, nil, err
}
path := fmt.Sprintf("/firewalls/%d/actions/set_rules", firewall.ID)
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
if err != nil {
return nil, nil, err
}
var respBody schema.FirewallActionSetRulesResponse
resp, err := c.client.Do(req, &respBody)
if err != nil {
return nil, resp, err
}
return ActionsFromSchema(respBody.Actions), resp, nil
}
func (c *FirewallClient) ApplyResources(ctx context.Context, firewall *Firewall, resources []FirewallResource) ([]*Action, *Response, error) {
applyTo := make([]schema.FirewallResource, len(resources))
for i, r := range resources {
applyTo[i] = firewallResourceToSchema(r)
}
reqBody := schema.FirewallActionApplyToResourcesRequest{ApplyTo: applyTo}
reqBodyData, err := json.Marshal(reqBody)
if err != nil {
return nil, nil, err
}
path := fmt.Sprintf("/firewalls/%d/actions/apply_to_resources", firewall.ID)
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
if err != nil {
return nil, nil, err
}
var respBody schema.FirewallActionApplyToResourcesResponse
resp, err := c.client.Do(req, &respBody)
if err != nil {
return nil, resp, err
}
return ActionsFromSchema(respBody.Actions), resp, nil
}
func (c *FirewallClient) RemoveResources(ctx context.Context, firewall *Firewall, resources []FirewallResource) ([]*Action, *Response, error) {
removeFrom := make([]schema.FirewallResource, len(resources))
for i, r := range resources {
removeFrom[i] = firewallResourceToSchema(r)
}
reqBody := schema.FirewallActionRemoveFromResourcesRequest{RemoveFrom: removeFrom}
reqBodyData, err := json.Marshal(reqBody)
if err != nil {
return nil, nil, err
}
path := fmt.Sprintf("/firewalls/%d/actions/remove_from_resources", firewall.ID)
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
if err != nil {
return nil, nil, err
}
var respBody schema.FirewallActionRemoveFromResourcesResponse
resp, err := c.client.Do(req, &respBody)
if err != nil {
return nil, resp, err
}
return ActionsFromSchema(respBody.Actions), resp, nil
}