Skip to content

Commit

Permalink
Add support for Firewalls (#166)
Browse files Browse the repository at this point in the history
Signed-off-by: Lukas Kämmerling <[email protected]>

Co-authored-by: Günther Eberl <[email protected]>
  • Loading branch information
LKaemmerling and geberl authored Mar 10, 2021
1 parent 34e196b commit 75c3dca
Show file tree
Hide file tree
Showing 14 changed files with 1,516 additions and 31 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ module github.com/hetznercloud/hcloud-go

go 1.16

require github.com/google/go-cmp v0.5.0
require github.com/google/go-cmp v0.5.2
5 changes: 3 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
102 changes: 93 additions & 9 deletions hcloud/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,16 @@ func (c *ActionClient) GetByID(ctx context.Context, id int) (*Action, *Response,
// ActionListOpts specifies options for listing actions.
type ActionListOpts struct {
ListOpts
ID []int
Status []ActionStatus
Sort []string
}

func (l ActionListOpts) values() url.Values {
vals := l.ListOpts.values()
for _, id := range l.ID {
vals.Add("id", fmt.Sprintf("%d", id))
}
for _, status := range l.Status {
vals.Add("status", string(status))
}
Expand Down Expand Up @@ -157,24 +161,95 @@ func (c *ActionClient) All(ctx context.Context) ([]*Action, error) {
return allActions, nil
}

// WatchProgress watches the action's progress until it completes with success or error.
func (c *ActionClient) WatchProgress(ctx context.Context, action *Action) (<-chan int, <-chan error) {
errCh := make(chan error, 1)
// AllWithOpts returns all actions for the given options.
func (c *ActionClient) AllWithOpts(ctx context.Context, opts ActionListOpts) ([]*Action, error) {
allActions := []*Action{}

err := c.client.all(func(page int) (*Response, error) {
opts.Page = page
actions, resp, err := c.List(ctx, opts)
if err != nil {
return resp, err
}
allActions = append(allActions, actions...)
return resp, nil
})
if err != nil {
return nil, err
}

return allActions, nil
}

// WatchOverallProgress watches several actions' progress until they complete with success or error.
func (c *ActionClient) WatchOverallProgress(ctx context.Context, actions []*Action) (<-chan int, <-chan error) {
errCh := make(chan error, len(actions))
progressCh := make(chan int)

go func() {
defer close(errCh)
defer close(progressCh)

successIDs := make([]int, 0, len(actions))
watchIDs := make(map[int]struct{}, len(actions))
for _, action := range actions {
watchIDs[action.ID] = struct{}{}
}

ticker := time.NewTicker(c.client.pollInterval)
sendProgress := func(p int) {
defer ticker.Stop()
for {
select {
case progressCh <- p:
break
default:
case <-ctx.Done():
errCh <- ctx.Err()
return
case <-ticker.C:
break
}

opts := ActionListOpts{}
for watchID := range watchIDs {
opts.ID = append(opts.ID, watchID)
}

as, err := c.AllWithOpts(ctx, opts)
if err != nil {
errCh <- err
return
}

for _, a := range as {
switch a.Status {
case ActionStatusSuccess:
delete(watchIDs, a.ID)
successIDs := append(successIDs, a.ID)
sendProgress(progressCh, int(float64(len(actions)-len(successIDs))/float64(len(actions))*100))
case ActionStatusError:
delete(watchIDs, a.ID)
errCh <- fmt.Errorf("action %d failed: %w", a.ID, a.Error())
}
}

if len(watchIDs) == 0 {
return
}
}
}()

return progressCh, errCh
}

// WatchProgress watches one action's progress until it completes with success or error.
func (c *ActionClient) WatchProgress(ctx context.Context, action *Action) (<-chan int, <-chan error) {
errCh := make(chan error, 1)
progressCh := make(chan int)

go func() {
defer close(errCh)
defer close(progressCh)

ticker := time.NewTicker(c.client.pollInterval)
defer ticker.Stop()

for {
select {
Expand All @@ -193,9 +268,9 @@ func (c *ActionClient) WatchProgress(ctx context.Context, action *Action) (<-cha

switch a.Status {
case ActionStatusRunning:
sendProgress(a.Progress)
sendProgress(progressCh, a.Progress)
case ActionStatusSuccess:
sendProgress(100)
sendProgress(progressCh, 100)
errCh <- nil
return
case ActionStatusError:
Expand All @@ -207,3 +282,12 @@ func (c *ActionClient) WatchProgress(ctx context.Context, action *Action) (<-cha

return progressCh, errCh
}

func sendProgress(progressCh chan int, p int) {
select {
case progressCh <- p:
break
default:
break
}
}
1 change: 1 addition & 0 deletions hcloud/action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ func TestActionClientWatchProgress(t *testing.T) {
defer env.Teardown()

callCount := 0

env.Mux.HandleFunc("/actions/1", func(w http.ResponseWriter, r *http.Request) {
callCount++
w.Header().Set("Content-Type", "application/json")
Expand Down
2 changes: 2 additions & 0 deletions hcloud/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ type Client struct {
Action ActionClient
Certificate CertificateClient
Datacenter DatacenterClient
Firewall FirewallClient
FloatingIP FloatingIPClient
Image ImageClient
ISO ISOClient
Expand Down Expand Up @@ -162,6 +163,7 @@ func NewClient(options ...ClientOption) *Client {
client.LoadBalancer = LoadBalancerClient{client: client}
client.LoadBalancerType = LoadBalancerTypeClient{client: client}
client.Certificate = CertificateClient{client: client}
client.Firewall = FirewallClient{client: client}

return client
}
Expand Down
7 changes: 7 additions & 0 deletions hcloud/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ const (
ErrorCodeNoSpaceLeftInLocation ErrorCode = "no_space_left_in_location" // There is no volume space left in the given location
ErrorCodeVolumeAlreadyAttached ErrorCode = "volume_already_attached" // Volume is already attached to a server, detach first

// Firewall related error codes
ErrorCodeFirewallAlreadyApplied ErrorCode = "firewall_already_applied" // Firewall was already applied on resource
ErrorCodeFirewallAlreadyRemoved ErrorCode = "firewall_already_removed" // Firewall was already removed from the resource
ErrorCodeIncompatibleNetworkType ErrorCode = "incompatible_network_type" // The Network type is incompatible for the given resource
ErrorCodeResourceInUse ErrorCode = "resource_in_use" // Firewall must not be in use to be deleted
ErrorCodeServerAlreadyAdded ErrorCode = "server_already_added" // Server added more than one time to resource

// Deprecated error codes
// The actual value of this error code is limit_reached. The new error code
// rate_limit_exceeded for ratelimiting was introduced before Hetzner Cloud
Expand Down
Loading

0 comments on commit 75c3dca

Please sign in to comment.