Repository: hetznercloud/hcloud-cloud-controller-manager
Pull request #628 (2024-03-27 18:22:53)

deps: update module github.com/hetznercloud/hcloud-go/v2 to v2.13.1
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [github.com/hetznercloud/hcloud-go/v2](https://togithub.com/hetznercloud/hcloud-go) | `v2.6.0` -> `v2.13.1` | [![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fhetznercloud%2fhcloud-go%2fv2/v2.13.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/github.com%2fhetznercloud%2fhcloud-go%2fv2/v2.13.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/github.com%2fhetznercloud%2fhcloud-go%2fv2/v2.6.0/v2.13.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fhetznercloud%2fhcloud-go%2fv2/v2.6.0/v2.13.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>hetznercloud/hcloud-go (github.com/hetznercloud/hcloud-go/v2)</summary>

### [`v2.13.1`](https://togithub.com/hetznercloud/hcloud-go/releases/tag/v2.13.1)

[Compare Source](https://togithub.com/hetznercloud/hcloud-go/compare/v2.13.0...v2.13.1)

##### Bug Fixes

-   `invalid_input` API errors may not return details ([#&#8203;507](https://togithub.com/hetznercloud/hcloud-go/issues/507)) ([ca78af2](https://togithub.com/hetznercloud/hcloud-go/commit/ca78af2af8acd375460c0f9705ea9d62d5ee1cc4))

### [`v2.13.0`](https://togithub.com/hetznercloud/hcloud-go/releases/tag/v2.13.0)

[Compare Source](https://togithub.com/hetznercloud/hcloud-go/compare/v2.12.0...v2.13.0)

##### Features

-   **network:** add new network zone ap-southeast ([#&#8203;501](https://togithub.com/hetznercloud/hcloud-go/issues/501)) ([a79a06b](https://togithub.com/hetznercloud/hcloud-go/commit/a79a06b0dda182e968a7a6e5cef9a4181414f29e))

##### Bug Fixes

-   update `NetworkSubnetType` constants ([#&#8203;499](https://togithub.com/hetznercloud/hcloud-go/issues/499)) ([ce497fe](https://togithub.com/hetznercloud/hcloud-go/commit/ce497fe89ccd9cccd8dc84ccd854427484fdd031))

### [`v2.12.0`](https://togithub.com/hetznercloud/hcloud-go/releases/tag/v2.12.0)

[Compare Source](https://togithub.com/hetznercloud/hcloud-go/compare/v2.11.0...v2.12.0)

##### API Changes for Traffic Prices and Server Type Included Traffic

There will be a breaking change in the API regarding Traffic Prices and Server Type Included Traffic on 2024-08-05. This release marks the affected fields as `Deprecated`. Please check if this affects any of your code and switch to the replacement fields where necessary.

You can learn more about this change in [our changelog](https://docs.hetzner.cloud/changelog#2024-07-25-cloud-api-returns-traffic-information-in-different-format).

##### Upgrading

##### Server Type Included Traffic

If you were using the field `hcloud.ServerType.IncludedTraffic`, you can now get the information through `hcloud.ServerType.Pricings`:

```go
func main() {
// previous
includedTraffic := serverType.IncludedTraffic

    // now
    locationOfInterest := "fsn1"
    var includedTraffic uint64
    for _, price := range serverType.Pricings {
        if price.Location.Name == locationOfInterest {
            includedTraffic = price.IncludedTraffic
            break
        }
    }
}
```

##### Traffic Prices

If you were using the field `hcloud.Pricing.Traffic`, you can now get the information through `hcloud.Pricing.ServerTypes` or `hcloud.Pricing.LoadBalancerTypes`:

```go
func main() {
// previous
trafficPrice := pricing.Traffic

    // now
    serverTypeOfInterest := "cx22"
    locationOfInterest := "fsn1"

    var trafficPrice hcloud.Price
    for _, serverTypePricings := range pricing.ServerTypes {
        if serverTypePricings.ServerType.Name == serverTypeOfInterest {
            for _, price := range serverTypePricings {
               if price.Location.Name == locationOfInterest {
                   trafficPrice = price.PerTBTraffic
                   break
               }
            }
        }
    }
}
```

##### Features

-   add jitter in the client default retry exponential backoff ([#&#8203;492](https://togithub.com/hetznercloud/hcloud-go/issues/492)) ([6205076](https://togithub.com/hetznercloud/hcloud-go/commit/6205076b89350bdbf08bc6b771a1d1267a3ac422))
-   add new `WithPollOpts` client option ([#&#8203;493](https://togithub.com/hetznercloud/hcloud-go/issues/493)) ([2c1a2d6](https://togithub.com/hetznercloud/hcloud-go/commit/2c1a2d65596bcbe282ff004c1a9da89950e754df))
-   allow checking multiple errors codes in `IsError` ([#&#8203;491](https://togithub.com/hetznercloud/hcloud-go/issues/491)) ([af59ab8](https://togithub.com/hetznercloud/hcloud-go/commit/af59ab846665abd735c9717eb2a47c0a8c79776d))
-   **load-balancer-type:** new traffic price fields ([94e0f44](https://togithub.com/hetznercloud/hcloud-go/commit/94e0f44d269fdb5138485a6e69dae9105690e4b0))
-   **pricing:** mark traffic field as deprecated ([94e0f44](https://togithub.com/hetznercloud/hcloud-go/commit/94e0f44d269fdb5138485a6e69dae9105690e4b0))
-   **server-type:** mark included traffic field as deprecated ([94e0f44](https://togithub.com/hetznercloud/hcloud-go/commit/94e0f44d269fdb5138485a6e69dae9105690e4b0))
-   **server-type:** new traffic price fields ([94e0f44](https://togithub.com/hetznercloud/hcloud-go/commit/94e0f44d269fdb5138485a6e69dae9105690e4b0))

### [`v2.11.0`](https://togithub.com/hetznercloud/hcloud-go/releases/tag/v2.11.0)

[Compare Source](https://togithub.com/hetznercloud/hcloud-go/compare/v2.10.2...v2.11.0)

##### Features

-   add truncated exponential backoff with full jitter ([#&#8203;459](https://togithub.com/hetznercloud/hcloud-go/issues/459)) ([fd1f46c](https://togithub.com/hetznercloud/hcloud-go/commit/fd1f46cc35e61dde1e524399eef88c38a757636e))
-   allow configuring retry options ([#&#8203;488](https://togithub.com/hetznercloud/hcloud-go/issues/488)) ([2db9575](https://togithub.com/hetznercloud/hcloud-go/commit/2db95753e2c826aeafa3bd9b864e95efd89ace7f))
-   **exp:** add sliceutil package ([#&#8203;489](https://togithub.com/hetznercloud/hcloud-go/issues/489)) ([f4ad6bc](https://togithub.com/hetznercloud/hcloud-go/commit/f4ad6bc93ff5017dda1b71a1606b67a79b56eb57))
-   **exp:** rename `*utils` package to `*util` ([#&#8203;487](https://togithub.com/hetznercloud/hcloud-go/issues/487)) ([19da475](https://togithub.com/hetznercloud/hcloud-go/commit/19da4759f4cbee7ed94ed6996350b45650f8b0b9))
-   respect cancelled contexts during retry sleep ([#&#8203;470](https://togithub.com/hetznercloud/hcloud-go/issues/470)) ([756f605](https://togithub.com/hetznercloud/hcloud-go/commit/756f605c97ac570adec531a479fc61c1ed27ab72))
-   retry requests when the api gateway errors ([#&#8203;470](https://togithub.com/hetznercloud/hcloud-go/issues/470)) ([756f605](https://togithub.com/hetznercloud/hcloud-go/commit/756f605c97ac570adec531a479fc61c1ed27ab72))
-   retry requests when the network timed out ([#&#8203;470](https://togithub.com/hetznercloud/hcloud-go/issues/470)) ([756f605](https://togithub.com/hetznercloud/hcloud-go/commit/756f605c97ac570adec531a479fc61c1ed27ab72))
-   retry requests when the rate limit was reached ([#&#8203;470](https://togithub.com/hetznercloud/hcloud-go/issues/470)) ([756f605](https://togithub.com/hetznercloud/hcloud-go/commit/756f605c97ac570adec531a479fc61c1ed27ab72))

##### Bug Fixes

-   **exp:** set capacity for each batch ([#&#8203;490](https://togithub.com/hetznercloud/hcloud-go/issues/490)) ([57f53c1](https://togithub.com/hetznercloud/hcloud-go/commit/57f53c1dca54ee79a33bab35740e7de7ece3b75f))

### [`v2.10.2`](https://togithub.com/hetznercloud/hcloud-go/releases/tag/v2.10.2)

[Compare Source](https://togithub.com/hetznercloud/hcloud-go/compare/v2.10.1...v2.10.2)

##### Bug Fixes

-   **exp:** allow request path matching in the want function ([#&#8203;475](https://togithub.com/hetznercloud/hcloud-go/issues/475)) ([267879b](https://togithub.com/hetznercloud/hcloud-go/commit/267879b78989ae870d581e9c929105ff76c60fb0))

### [`v2.10.1`](https://togithub.com/hetznercloud/hcloud-go/releases/tag/v2.10.1)

[Compare Source](https://togithub.com/hetznercloud/hcloud-go/compare/v2.10.0...v2.10.1)

##### Bug Fixes

-   **exp:** configure response headers before sending them ([#&#8203;473](https://togithub.com/hetznercloud/hcloud-go/issues/473)) ([07d4a35](https://togithub.com/hetznercloud/hcloud-go/commit/07d4a356dec0e2f44a6f3eec1ea3affec8932c22))

### [`v2.10.0`](https://togithub.com/hetznercloud/hcloud-go/releases/tag/v2.10.0)

[Compare Source](https://togithub.com/hetznercloud/hcloud-go/compare/v2.9.0...v2.10.0)

##### Features

-   **exp:** add envutils package ([#&#8203;466](https://togithub.com/hetznercloud/hcloud-go/issues/466)) ([a7636bd](https://togithub.com/hetznercloud/hcloud-go/commit/a7636bdcf5e4d55860f40da684c64cb72f8ddc03))
-   **exp:** add labelutils with selector ([#&#8203;465](https://togithub.com/hetznercloud/hcloud-go/issues/465)) ([1a55a7e](https://togithub.com/hetznercloud/hcloud-go/commit/1a55a7ed65bffdbf73000ffcf1ef22c9e55650f8))
-   **exp:** add mock utils package ([#&#8203;460](https://togithub.com/hetznercloud/hcloud-go/issues/460)) ([92f7c62](https://togithub.com/hetznercloud/hcloud-go/commit/92f7c624edfc76e06abe8c9c60e9c78d4b28f12f))
-   **exp:** rename `AppendNextActions` to `AppendNext` ([#&#8203;452](https://togithub.com/hetznercloud/hcloud-go/issues/452)) ([9b6239a](https://togithub.com/hetznercloud/hcloud-go/commit/9b6239ad188e601f22bc28e0072603c07fea201c))
-   filter out nil actions in action waiter ([#&#8203;464](https://togithub.com/hetznercloud/hcloud-go/issues/464)) ([4fc9a40](https://togithub.com/hetznercloud/hcloud-go/commit/4fc9a4039d45071124a435121642ca396a8237c0))

##### Bug Fixes

-   nil check against the embedded `http.Response` ([#&#8203;469](https://togithub.com/hetznercloud/hcloud-go/issues/469)) ([46e489a](https://togithub.com/hetznercloud/hcloud-go/commit/46e489a1782e8477d1c5a234dc203fa356c2a583))

### [`v2.9.0`](https://togithub.com/hetznercloud/hcloud-go/releases/tag/v2.9.0)

[Compare Source](https://togithub.com/hetznercloud/hcloud-go/compare/v2.8.0...v2.9.0)

##### Features

-   **exp:** add `AppendNextActions` function ([#&#8203;440](https://togithub.com/hetznercloud/hcloud-go/issues/440)) ([b07d7ad](https://togithub.com/hetznercloud/hcloud-go/commit/b07d7adf0bb08dd372f1f6ff630d16af8cb1265c))
-   **exp:** add ssh key functions ([#&#8203;441](https://togithub.com/hetznercloud/hcloud-go/issues/441)) ([d766e96](https://togithub.com/hetznercloud/hcloud-go/commit/d766e96e2220b069a0a8067a3249afe48cf68b2e))

##### Bug Fixes

-   **exp:** rename to `sshutils` package name ([#&#8203;450](https://togithub.com/hetznercloud/hcloud-go/issues/450)) ([6d4100d](https://togithub.com/hetznercloud/hcloud-go/commit/6d4100dbceb9a43a66bad3ce91cee039198130c3))

### [`v2.8.0`](https://togithub.com/hetznercloud/hcloud-go/releases/tag/v2.8.0)

[Compare Source](https://togithub.com/hetznercloud/hcloud-go/compare/v2.7.2...v2.8.0)

##### Features

-   **error:** show internal correlation id in error messages ([#&#8203;411](https://togithub.com/hetznercloud/hcloud-go/issues/411)) ([6c96d19](https://togithub.com/hetznercloud/hcloud-go/commit/6c96d19dacde736b52abd7b8fc8879c8f721f23b))
-   implement actions waiter ([#&#8203;407](https://togithub.com/hetznercloud/hcloud-go/issues/407)) ([1e3fa70](https://togithub.com/hetznercloud/hcloud-go/commit/1e3fa7033d8a1cea1c0a9007a6255798979f0771))
-   require Go >= 1.21 ([#&#8203;424](https://togithub.com/hetznercloud/hcloud-go/issues/424)) ([d4f4000](https://togithub.com/hetznercloud/hcloud-go/commit/d4f40009016c3ed5bb14ef9ae16bcf6aefa90fee))

##### Bug Fixes

-   improve error message format with correlation id ([#&#8203;430](https://togithub.com/hetznercloud/hcloud-go/issues/430)) ([013477f](https://togithub.com/hetznercloud/hcloud-go/commit/013477f4227bdc69f376d8f13a875b09c32171f6))

### [`v2.7.2`](https://togithub.com/hetznercloud/hcloud-go/releases/tag/v2.7.2)

[Compare Source](https://togithub.com/hetznercloud/hcloud-go/compare/v2.7.1...v2.7.2)

##### Bug Fixes

-   creating firewall with rules not working correctly ([#&#8203;412](https://togithub.com/hetznercloud/hcloud-go/issues/412)) ([43b2bab](https://togithub.com/hetznercloud/hcloud-go/commit/43b2bab8c82cab276c07e9deb9f4e422e10ccc82))

### [`v2.7.1`](https://togithub.com/hetznercloud/hcloud-go/releases/tag/v2.7.1)

[Compare Source](https://togithub.com/hetznercloud/hcloud-go/compare/v2.7.0...v2.7.1)

##### Bug Fixes

-   setting firewall rules not working correctly ([#&#8203;405](https://togithub.com/hetznercloud/hcloud-go/issues/405)) ([16daea0](https://togithub.com/hetznercloud/hcloud-go/commit/16daea0dfb32a5e2b8a71fce88d1c897cd4134eb))

### [`v2.7.0`](https://togithub.com/hetznercloud/hcloud-go/releases/tag/v2.7.0)

[Compare Source](https://togithub.com/hetznercloud/hcloud-go/compare/v2.6.0...v2.7.0)

##### Features

-   add volume format property ([#&#8203;397](https://togithub.com/hetznercloud/hcloud-go/issues/397)) ([c0940af](https://togithub.com/hetznercloud/hcloud-go/commit/c0940afce9eb01c0e6838502c91aa569ab411a03))
-   **error:** handle wrapped errors in IsError() ([#&#8203;374](https://togithub.com/hetznercloud/hcloud-go/issues/374)) ([83df108](https://togithub.com/hetznercloud/hcloud-go/commit/83df108228519617a919470e5ebbb4a1a2587b34))
-   require Go >= 1.20 ([#&#8203;392](https://togithub.com/hetznercloud/hcloud-go/issues/392)) ([299f181](https://togithub.com/hetznercloud/hcloud-go/commit/299f181c469a48e977743f2587e232a293cd9db5))
-   **server:** deprecate ServerRescueTypeLinux32 ([#&#8203;378](https://togithub.com/hetznercloud/hcloud-go/issues/378)) ([2f334c3](https://togithub.com/hetznercloud/hcloud-go/commit/2f334c3de2aecfb7aaf179424bb9a1a7b0da53ea))
-   test with Go 1.22 ([#&#8203;391](https://togithub.com/hetznercloud/hcloud-go/issues/391)) ([49be506](https://togithub.com/hetznercloud/hcloud-go/commit/49be50664f56e4e315e687543ee0bf7fbb2af186))

##### Bug Fixes

-   falsely omitted fields in firewall schema ([#&#8203;396](https://togithub.com/hetznercloud/hcloud-go/issues/396)) ([a3509b6](https://togithub.com/hetznercloud/hcloud-go/commit/a3509b6b0f2762b5d1e27f2374d3fb5fb53206e2))
-   LoadBalancerUpdateServiceOpts not converted correctly ([#&#8203;394](https://togithub.com/hetznercloud/hcloud-go/issues/394)) ([0f187ce](https://togithub.com/hetznercloud/hcloud-go/commit/0f187cef1f568d87a32b2ec063847dd9ff314740))
-   primary ip assignee id not mapped to nil ([#&#8203;395](https://togithub.com/hetznercloud/hcloud-go/issues/395)) ([b5fea38](https://togithub.com/hetznercloud/hcloud-go/commit/b5fea38f5d9d62c88af470ba69195a73ee075f09))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR was generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View the [repository job log](https://developer.mend.io/github/hetznercloud/hcloud-cloud-controller-manager).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4yNjkuMiIsInVwZGF0ZWRJblZlciI6IjM4LjI2LjEiLCJ0YXJnZXRCcmFuY2giOiJtYWluIn0=-->


renovate[bot]: ### Edited/Blocked Notification

Renovate will not automatically rebase this PR, because it does not recognize the last commit author and assumes somebody else may have edited the PR.

You can manually request rebase by checking the rebase/retry box above.

 ⚠ **Warning**: custom changes will be lost.
renovate[bot]: ### Edited/Blocked Notification

Renovate will not automatically rebase this PR, because it does not recognize the last commit author and assumes somebody else may have edited the PR.

You can manually request rebase by checking the rebase/retry box above.

 ⚠️ **Warning**: custom changes will be lost.
renovate[bot]: ### ℹ Artifact update notice

##### File name: go.mod

In order to perform the update(s) described in the table above, Renovate ran the `go get` command, which resulted in the following additional change(s):


- 10 additional dependencies were updated


Details:


| **Package**                           | **Change**             |
| :------------------------------------ | :--------------------- |
| `github.com/prometheus/client_golang` | `v1.18.0` -> `v1.19.1` |
| `github.com/prometheus/common`        | `v0.45.0` -> `v0.48.0` |
| `golang.org/x/crypto`                 | `v0.18.0` -> `v0.24.0` |
| `golang.org/x/net`                    | `v0.20.0` -> `v0.26.0` |
| `golang.org/x/oauth2`                 | `v0.12.0` -> `v0.16.0` |
| `golang.org/x/sync`                   | `v0.5.0` -> `v0.7.0`   |
| `golang.org/x/sys`                    | `v0.16.0` -> `v0.21.0` |
| `golang.org/x/term`                   | `v0.16.0` -> `v0.21.0` |
| `golang.org/x/text`                   | `v0.14.0` -> `v0.16.0` |
| `google.golang.org/protobuf`          | `v1.31.0` -> `v1.33.0` |
renovate[bot]: ### Edited/Blocked Notification

Renovate will not automatically rebase this PR, because it does not recognize the last commit author and assumes somebody else may have edited the PR.

You can manually request rebase by checking the rebase/retry box above.

 ⚠️ **Warning**: custom changes will be lost.
renovate[bot]: ### Edited/Blocked Notification

Renovate will not automatically rebase this PR, because it does not recognize the last commit author and assumes somebody else may have edited the PR.

You can manually request rebase by checking the rebase/retry box above.

 ⚠️ **Warning**: custom changes will be lost.
jooola: Since the hcloud-go library forces us to upgrade the prometheus client library, we depend also on https://github.com/hetznercloud/hcloud-cloud-controller-manager/pull/623#issuecomment-2211066872



Patch:
diff --git a/go.mod b/go.mod
index 62511633..a4a59ffe 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.22.0
 toolchain go1.22.6
 
 require (
-	github.com/hetznercloud/hcloud-go/v2 v2.6.0
+	github.com/hetznercloud/hcloud-go/v2 v2.13.1
 	github.com/prometheus/client_golang v1.19.1
 	github.com/spf13/pflag v1.0.5
 	github.com/stretchr/testify v1.9.0
@@ -85,13 +85,13 @@ require (
 	go.opentelemetry.io/proto/otlp v1.3.1 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	go.uber.org/zap v1.26.0 // indirect
-	golang.org/x/crypto v0.24.0 // indirect
+	golang.org/x/crypto v0.25.0 // indirect
 	golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect
-	golang.org/x/net v0.26.0 // indirect
+	golang.org/x/net v0.27.0 // indirect
 	golang.org/x/oauth2 v0.21.0 // indirect
 	golang.org/x/sync v0.7.0 // indirect
-	golang.org/x/sys v0.21.0 // indirect
-	golang.org/x/term v0.21.0 // indirect
+	golang.org/x/sys v0.22.0 // indirect
+	golang.org/x/term v0.22.0 // indirect
 	golang.org/x/text v0.16.0 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
 	google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect
diff --git a/go.sum b/go.sum
index e13d9ba8..af10bafe 100644
--- a/go.sum
+++ b/go.sum
@@ -122,6 +122,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
 github.com/hetznercloud/hcloud-go/v2 v2.6.0 h1:RJOA2hHZ7rD1pScA4O1NF6qhkHyUdbbxjHgFNot8928=
 github.com/hetznercloud/hcloud-go/v2 v2.6.0/go.mod h1:4J1cSE57+g0WS93IiHLV7ubTHItcp+awzeBp5bM9mfA=
+github.com/hetznercloud/hcloud-go/v2 v2.13.1 h1:jq0GP4QaYE5d8xR/Zw17s9qoaESRJMXfGmtD1a/qckQ=
+github.com/hetznercloud/hcloud-go/v2 v2.13.1/go.mod h1:dhix40Br3fDiBhwaSG/zgaYOFFddpfBm/6R1Zz0IiF0=
 github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
 github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@@ -290,6 +292,8 @@ golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
 golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
 golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
 golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
+golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
+golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
 golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
 golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
 golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU=
@@ -305,6 +309,8 @@ golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
 golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
 golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
 golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
+golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
+golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
 golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4=
 golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4=
 golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
@@ -324,10 +330,14 @@ golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
 golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
 golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
+golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
 golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
 golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
 golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
+golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
+golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
diff --git a/hcloud/routes.go b/hcloud/routes.go
index fd2b7054..7d822a40 100644
--- a/hcloud/routes.go
+++ b/hcloud/routes.go
@@ -133,7 +133,7 @@ func (r *routes) CreateRoute(ctx context.Context, clusterName string, nameHint s
 		}
 		action, _, err := r.client.Network.AddRoute(ctx, r.network, opts)
 		if err != nil {
-			if hcloud.IsError(err, hcloud.ErrorCodeLocked) || hcloud.IsError(err, hcloud.ErrorCodeConflict) {
+			if hcloud.IsError(err, hcloud.ErrorCodeLocked, hcloud.ErrorCodeConflict) {
 				retryDelay := time.Second * 5
 				klog.InfoS("retry due to conflict or lock",
 					"op", op, "delay", fmt.Sprintf("%v", retryDelay), "err", fmt.Sprintf("%v", err))
@@ -144,7 +144,7 @@ func (r *routes) CreateRoute(ctx context.Context, clusterName string, nameHint s
 			return fmt.Errorf("%s: %w", op, err)
 		}
 
-		if err := hcops.WatchAction(ctx, &r.client.Action, action); err != nil {
+		if err := r.client.Action.WaitFor(ctx, action); err != nil {
 			return fmt.Errorf("%s: %w", op, err)
 		}
 	}
@@ -194,7 +194,7 @@ func (r *routes) deleteRouteFromHcloud(ctx context.Context, cidr *net.IPNet, ip
 
 	action, _, err := r.client.Network.DeleteRoute(ctx, r.network, opts)
 	if err != nil {
-		if hcloud.IsError(err, hcloud.ErrorCodeLocked) || hcloud.IsError(err, hcloud.ErrorCodeConflict) {
+		if hcloud.IsError(err, hcloud.ErrorCodeLocked, hcloud.ErrorCodeConflict) {
 			retryDelay := time.Second * 5
 			klog.InfoS("retry due to conflict or lock",
 				"op", op, "delay", fmt.Sprintf("%v", retryDelay), "err", fmt.Sprintf("%v", err))
@@ -204,7 +204,7 @@ func (r *routes) deleteRouteFromHcloud(ctx context.Context, cidr *net.IPNet, ip
 		}
 		return fmt.Errorf("%s: %w", op, err)
 	}
-	if err := hcops.WatchAction(ctx, &r.client.Action, action); err != nil {
+	if err := r.client.Action.WaitFor(ctx, action); err != nil {
 		return fmt.Errorf("%s: %w", op, err)
 	}
 	return nil
@@ -262,7 +262,7 @@ func (r *routes) checkIfRouteAlreadyExists(ctx context.Context, route *cloudprov
 					return false, fmt.Errorf("%s: %w", op, err)
 				}
 
-				if err := hcops.WatchAction(ctx, &r.client.Action, action); err != nil {
+				if err := r.client.Action.WaitFor(ctx, action); err != nil {
 					return false, fmt.Errorf("%s: %w", op, err)
 				}
 			}
diff --git a/internal/config/config.go b/internal/config/config.go
index 504858a4..81c93e9a 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -5,8 +5,9 @@ import (
 	"fmt"
 	"os"
 	"strconv"
-	"strings"
 	"time"
+
+	"github.com/hetznercloud/hcloud-go/v2/hcloud/exp/kit/envutil"
 )
 
 const (
@@ -97,32 +98,6 @@ type HCCMConfiguration struct {
 	Route        RouteConfiguration
 }
 
-// read values from environment variables or from file set via _FILE env var
-// values set directly via env var take precedence over values set via file.
-func readFromEnvOrFile(envVar string) (string, error) {
-	// check if the value is set directly via env (e.g. HCLOUD_TOKEN)
-	value, ok := os.LookupEnv(envVar)
-	if ok {
-		return value, nil
-	}
-
-	// check if the value is set via a file (e.g. HCLOUD_TOKEN_FILE)
-	value, ok = os.LookupEnv(envVar + "_FILE")
-	if !ok {
-		// return no error here, the values could be optional
-		// and the function "Validate()" below checks that all required variables are set
-		return "", nil
-	}
-
-	// read file content
-	valueBytes, err := os.ReadFile(value)
-	if err != nil {
-		return "", fmt.Errorf("failed to read %s: %w", envVar+"_FILE", err)
-	}
-
-	return strings.TrimSpace(string(valueBytes)), nil
-}
-
 // Read evaluates all environment variables and returns a [HCCMConfiguration]. It only validates as far as
 // it needs to parse the values. For business logic validation, check out [HCCMConfiguration.Validate].
 func Read() (HCCMConfiguration, error) {
@@ -133,7 +108,7 @@ func Read() (HCCMConfiguration, error) {
 	var errs []error
 	var cfg HCCMConfiguration
 
-	cfg.HCloudClient.Token, err = readFromEnvOrFile(hcloudToken)
+	cfg.HCloudClient.Token, err = envutil.LookupEnvWithFile(hcloudToken)
 	if err != nil {
 		errs = append(errs, err)
 	}
@@ -147,11 +122,11 @@ func Read() (HCCMConfiguration, error) {
 	if err != nil {
 		errs = append(errs, err)
 	}
-	cfg.Robot.User, err = readFromEnvOrFile(robotUser)
+	cfg.Robot.User, err = envutil.LookupEnvWithFile(robotUser)
 	if err != nil {
 		errs = append(errs, err)
 	}
-	cfg.Robot.Password, err = readFromEnvOrFile(robotPassword)
+	cfg.Robot.Password, err = envutil.LookupEnvWithFile(robotPassword)
 	if err != nil {
 		errs = append(errs, err)
 	}
diff --git a/internal/hcops/action.go b/internal/hcops/action.go
index ed67aefc..77692ee5 100644
--- a/internal/hcops/action.go
+++ b/internal/hcops/action.go
@@ -7,11 +7,5 @@ import (
 )
 
 type HCloudActionClient interface {
-	WatchProgress(ctx context.Context, a *hcloud.Action) (<-chan int, <-chan error)
-}
-
-func WatchAction(ctx context.Context, ac HCloudActionClient, a *hcloud.Action) error {
-	_, errCh := ac.WatchProgress(ctx, a)
-	err := <-errCh
-	return err
+	WaitFor(ctx context.Context, actions ...*hcloud.Action) error
 }
diff --git a/internal/hcops/load_balancer.go b/internal/hcops/load_balancer.go
index 8c747d17..5f1a0f68 100644
--- a/internal/hcops/load_balancer.go
+++ b/internal/hcops/load_balancer.go
@@ -225,7 +225,7 @@ func (l *LoadBalancerOps) Create(
 	if err != nil {
 		return nil, fmt.Errorf("%s: %w", op, err)
 	}
-	if err := WatchAction(ctx, l.ActionClient, result.Action); err != nil {
+	if err := l.ActionClient.WaitFor(ctx, result.Action); err != nil {
 		return nil, fmt.Errorf("%s: %w", op, err)
 	}
 
@@ -374,7 +374,7 @@ func (l *LoadBalancerOps) changeIPv4RDNS(ctx context.Context, lb *hcloud.LoadBal
 	if err != nil {
 		return false, fmt.Errorf("%s: %w", op, err)
 	}
-	err = WatchAction(ctx, l.ActionClient, action)
+	err = l.ActionClient.WaitFor(ctx, action)
 	if err != nil {
 		return false, fmt.Errorf("%s: %w", op, err)
 	}
@@ -399,7 +399,7 @@ func (l *LoadBalancerOps) changeIPv6RDNS(ctx context.Context, lb *hcloud.LoadBal
 	if err != nil {
 		return false, fmt.Errorf("%s: %w", op, err)
 	}
-	err = WatchAction(ctx, l.ActionClient, action)
+	err = l.ActionClient.WaitFor(ctx, action)
 	if err != nil {
 		return false, fmt.Errorf("%s: %w", op, err)
 	}
@@ -426,7 +426,7 @@ func (l *LoadBalancerOps) changeAlgorithm(ctx context.Context, lb *hcloud.LoadBa
 	if err != nil {
 		return false, fmt.Errorf("%s: %w", op, err)
 	}
-	err = WatchAction(ctx, l.ActionClient, action)
+	err = l.ActionClient.WaitFor(ctx, action)
 	if err != nil {
 		return false, fmt.Errorf("%s: %w", op, err)
 	}
@@ -450,7 +450,7 @@ func (l *LoadBalancerOps) changeType(ctx context.Context, lb *hcloud.LoadBalance
 	if err != nil {
 		return false, fmt.Errorf("%s: %w", op, err)
 	}
-	err = WatchAction(ctx, l.ActionClient, action)
+	err = l.ActionClient.WaitFor(ctx, action)
 	if err != nil {
 		return false, fmt.Errorf("%s: %w", op, err)
 	}
@@ -476,7 +476,7 @@ func (l *LoadBalancerOps) detachFromNetwork(ctx context.Context, lb *hcloud.Load
 		if err != nil {
 			return changed, fmt.Errorf("%s: %w", op, err)
 		}
-		if err := WatchAction(ctx, l.ActionClient, a); err != nil {
+		if err := l.ActionClient.WaitFor(ctx, a); err != nil {
 			return changed, fmt.Errorf("%s: %w", op, err)
 		}
 		changed = true
@@ -509,7 +509,7 @@ func (l *LoadBalancerOps) attachToNetwork(ctx context.Context, lb *hcloud.LoadBa
 	}
 	opts := hcloud.LoadBalancerAttachToNetworkOpts{Network: nw}
 	a, _, err := l.LBClient.AttachToNetwork(ctx, lb, opts)
-	if hcloud.IsError(err, hcloud.ErrorCodeConflict) || hcloud.IsError(err, hcloud.ErrorCodeLocked) {
+	if hcloud.IsError(err, hcloud.ErrorCodeConflict, hcloud.ErrorCodeLocked) {
 		klog.InfoS("retry due to conflict or lock",
 			"op", op, "delay", fmt.Sprintf("%v", retryDelay), "err", fmt.Sprintf("%v", err))
 
@@ -520,7 +520,7 @@ func (l *LoadBalancerOps) attachToNetwork(ctx context.Context, lb *hcloud.LoadBa
 		return false, fmt.Errorf("%s: %w", op, err)
 	}
 
-	if err := WatchAction(ctx, l.ActionClient, a); err != nil {
+	if err := l.ActionClient.WaitFor(ctx, a); err != nil {
 		return false, fmt.Errorf("%s: %w", op, err)
 	}
 
@@ -554,7 +554,7 @@ func (l *LoadBalancerOps) togglePublicInterface(ctx context.Context, lb *hcloud.
 		return false, fmt.Errorf("%s: %w", op, err)
 	}
 
-	if err := WatchAction(ctx, l.ActionClient, a); err != nil {
+	if err := l.ActionClient.WaitFor(ctx, a); err != nil {
 		return false, fmt.Errorf("%s: %w", op, err)
 	}
 	return true, nil
@@ -658,7 +658,7 @@ func (l *LoadBalancerOps) ReconcileHCLBTargets(
 			if err != nil {
 				return changed, fmt.Errorf("%s: target: %s: %w", op, nodeName, err)
 			}
-			if err := WatchAction(ctx, l.ActionClient, a); err != nil {
+			if err := l.ActionClient.WaitFor(ctx, a); err != nil {
 				return changed, fmt.Errorf("%s: target: %s: %w", op, nodeName, err)
 			}
 			changed = true
@@ -695,7 +695,7 @@ func (l *LoadBalancerOps) ReconcileHCLBTargets(
 				}
 				return changed, e
 			}
-			if err := WatchAction(ctx, l.ActionClient, a); err != nil {
+			if err := l.ActionClient.WaitFor(ctx, a); err != nil {
 				var e error
 				if foundServer {
 					e = fmt.Errorf("%s: target: %s: %w", op, nodeName, err)
@@ -738,7 +738,7 @@ func (l *LoadBalancerOps) ReconcileHCLBTargets(
 			}
 			return changed, fmt.Errorf("%s: target %s: %w", op, node.Name, err)
 		}
-		if err := WatchAction(ctx, l.ActionClient, a); err != nil {
+		if err := l.ActionClient.WaitFor(ctx, a); err != nil {
 			return changed, fmt.Errorf("%s: target %s: %w", op, node.Name, err)
 		}
 		changed = true
@@ -780,7 +780,7 @@ func (l *LoadBalancerOps) ReconcileHCLBTargets(
 				}
 				return changed, fmt.Errorf("%s: target %s: %w", op, node, err)
 			}
-			if err := WatchAction(ctx, l.ActionClient, a); err != nil {
+			if err := l.ActionClient.WaitFor(ctx, a); err != nil {
 				return changed, fmt.Errorf("%s: target %s: %w", op, node, err)
 			}
 			changed = true
@@ -870,7 +870,7 @@ func (l *LoadBalancerOps) ReconcileHCLBServices(
 			}
 		}
 
-		if err = WatchAction(ctx, l.ActionClient, action); err != nil {
+		if err = l.ActionClient.WaitFor(ctx, action); err != nil {
 			return changed, fmt.Errorf("%s: %w", op, err)
 		}
 		changed = true
@@ -883,7 +883,7 @@ func (l *LoadBalancerOps) ReconcileHCLBServices(
 		if err != nil {
 			return changed, fmt.Errorf("%s: port %d: %w", op, p, err)
 		}
-		err = WatchAction(ctx, l.ActionClient, a)
+		err = l.ActionClient.WaitFor(ctx, a)
 		if err != nil {
 			return changed, fmt.Errorf("%s: port: %d: %w", op, p, err)
 		}
diff --git a/internal/mocks/action.go b/internal/mocks/action.go
index ef6d71bf..9f609970 100644
--- a/internal/mocks/action.go
+++ b/internal/mocks/action.go
@@ -12,18 +12,8 @@ type ActionClient struct {
 	mock.Mock
 }
 
-func (m *ActionClient) WatchProgress(ctx context.Context, a *hcloud.Action) (<-chan int, <-chan error) {
-	args := m.Called(ctx, a)
-	return getIntChan(args, 0), getErrChan(args, 1)
-}
-
-func (m *ActionClient) MockWatchProgress(ctx context.Context, a *hcloud.Action, err error) {
-	resC := make(chan int)
-	errC := make(chan error, 1)
-	if err != nil {
-		errC <- err
-	}
-	close(resC)
-	close(errC)
-	m.On("WatchProgress", ctx, a).Return(resC, errC)
+func (m *ActionClient) WaitFor(ctx context.Context, actions ...*hcloud.Action) error {
+	// The mock library does not support variadic arguments, ignore for now
+	args := m.Called(ctx, mock.Anything)
+	return args.Error(0)
 }
diff --git a/internal/mocks/casts.go b/internal/mocks/casts.go
index 4a88c650..95cc2add 100644
--- a/internal/mocks/casts.go
+++ b/internal/mocks/casts.go
@@ -55,22 +55,6 @@ func getNetworkPtr(args mock.Arguments, i int) *hcloud.Network {
 	return v.(*hcloud.Network)
 }
 
-func getIntChan(args mock.Arguments, i int) chan int {
-	v := args.Get(i)
-	if v == nil {
-		return nil
-	}
-	return v.(chan int)
-}
-
-func getErrChan(args mock.Arguments, i int) chan error {
-	v := args.Get(i)
-	if v == nil {
-		return nil
-	}
-	return v.(chan error)
-}
-
 func getCertificatePtr(args mock.Arguments, i int) *hcloud.Certificate {
 	v := args.Get(i)
 	if v == nil {


Test patch:
diff --git a/hcloud/cloud_test.go b/hcloud/cloud_test.go
index d597db99..b7bca712 100644
--- a/hcloud/cloud_test.go
+++ b/hcloud/cloud_test.go
@@ -59,7 +59,8 @@ func newTestEnv() testEnv {
 	client := hcloud.NewClient(
 		hcloud.WithEndpoint(server.URL),
 		hcloud.WithToken("jr5g7ZHpPptyhJzZyHw2Pqu4g9gTqDvEceYpngPf79jNZXCeTYQ4uArypFM3nh75"),
-		hcloud.WithBackoffFunc(func(_ int) time.Duration { return 0 }),
+		hcloud.WithPollOpts(hcloud.PollOpts{BackoffFunc: hcloud.ConstantBackoff(0)}),
+		hcloud.WithRetryOpts(hcloud.RetryOpts{BackoffFunc: hcloud.ConstantBackoff(0)}),
 		hcloud.WithDebugWriter(os.Stdout),
 	)
 	robotClient := hrobot.NewBasicAuthClient("", "")
diff --git a/hcloud/routes_test.go b/hcloud/routes_test.go
index d884590b..22620351 100644
--- a/hcloud/routes_test.go
+++ b/hcloud/routes_test.go
@@ -40,12 +40,14 @@ func TestRoutes_CreateRoute(t *testing.T) {
 			},
 		})
 	})
-	env.Mux.HandleFunc("/actions/1", func(w http.ResponseWriter, _ *http.Request) {
-		json.NewEncoder(w).Encode(schema.NetworkActionAddRouteResponse{
-			Action: schema.Action{
-				ID:       1,
-				Status:   string(hcloud.ActionStatusSuccess),
-				Progress: 100,
+	env.Mux.HandleFunc("/actions", func(w http.ResponseWriter, _ *http.Request) {
+		json.NewEncoder(w).Encode(schema.ActionListResponse{
+			Actions: []schema.Action{
+				{
+					ID:       1,
+					Status:   string(hcloud.ActionStatusSuccess),
+					Progress: 100,
+				},
 			},
 		})
 	})
@@ -155,12 +157,14 @@ func TestRoutes_DeleteRoute(t *testing.T) {
 			},
 		})
 	})
-	env.Mux.HandleFunc("/actions/1", func(w http.ResponseWriter, _ *http.Request) {
-		json.NewEncoder(w).Encode(schema.NetworkActionAddRouteResponse{
-			Action: schema.Action{
-				ID:       1,
-				Status:   string(hcloud.ActionStatusSuccess),
-				Progress: 100,
+	env.Mux.HandleFunc("/actions", func(w http.ResponseWriter, _ *http.Request) {
+		json.NewEncoder(w).Encode(schema.ActionListResponse{
+			Actions: []schema.Action{
+				{
+					ID:       1,
+					Status:   string(hcloud.ActionStatusSuccess),
+					Progress: 100,
+				},
 			},
 		})
 	})
diff --git a/internal/hcops/load_balancer_test.go b/internal/hcops/load_balancer_test.go
index cd3f815d..687d7531 100644
--- a/internal/hcops/load_balancer_test.go
+++ b/internal/hcops/load_balancer_test.go
@@ -462,7 +462,7 @@ func TestLoadBalancerOps_Create(t *testing.T) {
 					Return(tt.createOpts.Network, nil, nil)
 				action := fx.MockCreate(tt.createOpts, tt.lb, nil)
 				fx.MockGetByID(tt.lb, nil)
-				fx.MockWatchProgress(action, nil)
+				fx.ActionClient.On("WaitFor", fx.Ctx, action).Return(nil)
 			},
 			lb: &hcloud.LoadBalancer{ID: 5},
 		},
@@ -514,7 +514,7 @@ func TestLoadBalancerOps_Create(t *testing.T) {
 
 				action := fx.MockCreate(tt.createOpts, tt.lb, nil)
 				fx.MockGetByID(tt.lb, nil)
-				fx.MockWatchProgress(action, nil)
+				fx.ActionClient.On("WaitFor", fx.Ctx, action).Return(nil)
 			},
 			lb: &hcloud.LoadBalancer{ID: 6},
 		},
@@ -534,7 +534,7 @@ func TestLoadBalancerOps_Create(t *testing.T) {
 					}
 					action := fx.MockCreate(tt.createOpts, tt.lb, nil)
 					fx.MockGetByID(tt.lb, nil)
-					fx.MockWatchProgress(action, nil)
+					fx.ActionClient.On("WaitFor", fx.Ctx, action).Return(nil)
 				}
 			}
 			tt.mock(t, &tt, fx)
@@ -662,7 +662,7 @@ func TestLoadBalancerOps_ReconcileHCLB(t *testing.T) {
 				tt.fx.LBClient.
 					On("ChangeAlgorithm", tt.fx.Ctx, tt.initialLB, opts).
 					Return(action, nil, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 			},
 			perform: func(t *testing.T, tt *LBReconcilementTestCase) {
 				changed, err := tt.fx.LBOps.ReconcileHCLB(tt.fx.Ctx, tt.initialLB, tt.service)
@@ -725,7 +725,7 @@ func TestLoadBalancerOps_ReconcileHCLB(t *testing.T) {
 				tt.fx.LBClient.
 					On("ChangeType", tt.fx.Ctx, tt.initialLB, opts).
 					Return(action, nil, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 			},
 			perform: func(t *testing.T, tt *LBReconcilementTestCase) {
 				changed, err := tt.fx.LBOps.ReconcileHCLB(tt.fx.Ctx, tt.initialLB, tt.service)
@@ -789,7 +789,7 @@ func TestLoadBalancerOps_ReconcileHCLB(t *testing.T) {
 				action := &hcloud.Action{ID: rand.Int63()}
 				newRDNS := "new-name-lb.example.com"
 				tt.fx.LBClient.On("ChangeDNSPtr", tt.fx.Ctx, tt.initialLB, net.ParseIP("1.2.3.4").String(), &newRDNS).Return(action, nil, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 			},
 			perform: func(t *testing.T, tt *LBReconcilementTestCase) {
 				changed, err := tt.fx.LBOps.ReconcileHCLB(tt.fx.Ctx, tt.initialLB, tt.service)
@@ -836,7 +836,7 @@ func TestLoadBalancerOps_ReconcileHCLB(t *testing.T) {
 				action := &hcloud.Action{ID: rand.Int63()}
 				newRDNS := "new-name-lb.example.com"
 				tt.fx.LBClient.On("ChangeDNSPtr", tt.fx.Ctx, tt.initialLB, net.ParseIP("fe80::1").String(), &newRDNS).Return(action, nil, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 			},
 			perform: func(t *testing.T, tt *LBReconcilementTestCase) {
 				changed, err := tt.fx.LBOps.ReconcileHCLB(tt.fx.Ctx, tt.initialLB, tt.service)
@@ -860,7 +860,7 @@ func TestLoadBalancerOps_ReconcileHCLB(t *testing.T) {
 				}
 				action := &hcloud.Action{ID: rand.Int63()}
 				tt.fx.LBClient.On("DetachFromNetwork", tt.fx.Ctx, tt.initialLB, opts).Return(action, nil, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 			},
 			perform: func(t *testing.T, tt *LBReconcilementTestCase) {
 				changed, err := tt.fx.LBOps.ReconcileHCLB(tt.fx.Ctx, tt.initialLB, tt.service)
@@ -899,7 +899,7 @@ func TestLoadBalancerOps_ReconcileHCLB(t *testing.T) {
 				opts := hcloud.LoadBalancerAttachToNetworkOpts{Network: nw}
 				action := &hcloud.Action{ID: rand.Int63()}
 				tt.fx.LBClient.On("AttachToNetwork", tt.fx.Ctx, tt.initialLB, opts).Return(action, nil, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 			},
 			perform: func(t *testing.T, tt *LBReconcilementTestCase) {
 				changed, err := tt.fx.LBOps.ReconcileHCLB(tt.fx.Ctx, tt.initialLB, tt.service)
@@ -928,7 +928,7 @@ func TestLoadBalancerOps_ReconcileHCLB(t *testing.T) {
 					Return(action, nil, nil).
 					Once()
 
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 			},
 			perform: func(t *testing.T, tt *LBReconcilementTestCase) {
 				changed, err := tt.fx.LBOps.ReconcileHCLB(tt.fx.Ctx, tt.initialLB, tt.service)
@@ -957,7 +957,7 @@ func TestLoadBalancerOps_ReconcileHCLB(t *testing.T) {
 					Return(action, nil, nil).
 					Once()
 
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 			},
 			perform: func(t *testing.T, tt *LBReconcilementTestCase) {
 				changed, err := tt.fx.LBOps.ReconcileHCLB(tt.fx.Ctx, tt.initialLB, tt.service)
@@ -1000,7 +1000,7 @@ func TestLoadBalancerOps_ReconcileHCLB(t *testing.T) {
 				tt.fx.LBClient.
 					On("DisablePublicInterface", tt.fx.Ctx, tt.initialLB).
 					Return(action, nil, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 			},
 			perform: func(t *testing.T, tt *LBReconcilementTestCase) {
 				changed, err := tt.fx.LBOps.ReconcileHCLB(tt.fx.Ctx, tt.initialLB, tt.service)
@@ -1041,7 +1041,7 @@ func TestLoadBalancerOps_ReconcileHCLB(t *testing.T) {
 				tt.fx.LBClient.
 					On("EnablePublicInterface", tt.fx.Ctx, tt.initialLB).
 					Return(action, nil, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 			},
 			perform: func(t *testing.T, tt *LBReconcilementTestCase) {
 				changed, err := tt.fx.LBOps.ReconcileHCLB(tt.fx.Ctx, tt.initialLB, tt.service)
@@ -1168,19 +1168,19 @@ func TestLoadBalancerOps_ReconcileHCLBTargets(t *testing.T) {
 			mock: func(_ *testing.T, tt *LBReconcilementTestCase) {
 				opts := hcloud.LoadBalancerAddServerTargetOpts{Server: &hcloud.Server{ID: 1}, UsePrivateIP: hcloud.Ptr(false)}
 				action := tt.fx.MockAddServerTarget(tt.initialLB, opts, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 
 				opts = hcloud.LoadBalancerAddServerTargetOpts{Server: &hcloud.Server{ID: 2}, UsePrivateIP: hcloud.Ptr(false)}
 				action = tt.fx.MockAddServerTarget(tt.initialLB, opts, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 
 				optsIP := hcloud.LoadBalancerAddIPTargetOpts{IP: net.ParseIP("1.2.3.4")}
 				action = tt.fx.MockAddIPTarget(tt.initialLB, optsIP, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 
 				optsIP = hcloud.LoadBalancerAddIPTargetOpts{IP: net.ParseIP("1.2.3.5")}
 				action = tt.fx.MockAddIPTarget(tt.initialLB, optsIP, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 
 				tt.fx.MockListRobotServers(tt.robotServers, nil)
 			},
@@ -1239,13 +1239,13 @@ func TestLoadBalancerOps_ReconcileHCLBTargets(t *testing.T) {
 			},
 			mock: func(_ *testing.T, tt *LBReconcilementTestCase) {
 				action := tt.fx.MockRemoveServerTarget(tt.initialLB, &hcloud.Server{ID: 3}, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 
 				action = tt.fx.MockRemoveServerTarget(tt.initialLB, &hcloud.Server{ID: 4}, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 
 				action = tt.fx.MockRemoveIPTarget(tt.initialLB, net.ParseIP("1.2.3.4"), nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 
 				tt.fx.MockListRobotServers(tt.robotServers, nil)
 			},
@@ -1309,11 +1309,11 @@ func TestLoadBalancerOps_ReconcileHCLBTargets(t *testing.T) {
 
 				opts := hcloud.LoadBalancerAddServerTargetOpts{Server: &hcloud.Server{ID: 1}, UsePrivateIP: hcloud.Ptr(true)}
 				action := tt.fx.MockAddServerTarget(tt.initialLB, opts, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 
 				opts = hcloud.LoadBalancerAddServerTargetOpts{Server: &hcloud.Server{ID: 2}, UsePrivateIP: hcloud.Ptr(true)}
 				action = tt.fx.MockAddServerTarget(tt.initialLB, opts, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 
 				tt.fx.MockListRobotServers(nil, nil)
 			},
@@ -1350,11 +1350,11 @@ func TestLoadBalancerOps_ReconcileHCLBTargets(t *testing.T) {
 
 				opts := hcloud.LoadBalancerAddServerTargetOpts{Server: &hcloud.Server{ID: 1}, UsePrivateIP: hcloud.Ptr(true)}
 				action := tt.fx.MockAddServerTarget(tt.initialLB, opts, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 
 				opts = hcloud.LoadBalancerAddServerTargetOpts{Server: &hcloud.Server{ID: 2}, UsePrivateIP: hcloud.Ptr(true)}
 				action = tt.fx.MockAddServerTarget(tt.initialLB, opts, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 
 				tt.fx.MockListRobotServers(nil, nil)
 			},
@@ -1394,14 +1394,14 @@ func TestLoadBalancerOps_ReconcileHCLBTargets(t *testing.T) {
 			},
 			mock: func(_ *testing.T, tt *LBReconcilementTestCase) {
 				action := tt.fx.MockRemoveServerTarget(tt.initialLB, &hcloud.Server{ID: 1}, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 
 				opts := hcloud.LoadBalancerAddServerTargetOpts{
 					Server:       &hcloud.Server{ID: 1},
 					UsePrivateIP: hcloud.Ptr(false),
 				}
 				action = tt.fx.MockAddServerTarget(tt.initialLB, opts, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 
 				tt.fx.MockListRobotServers(nil, nil)
 			},
@@ -1444,7 +1444,7 @@ func TestLoadBalancerOps_ReconcileHCLBServices(t *testing.T) {
 					},
 				}
 				action := tt.fx.MockAddService(opts, tt.initialLB, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 
 				opts = hcloud.LoadBalancerAddServiceOpts{
 					Protocol:        hcloud.LoadBalancerServiceProtocolTCP,
@@ -1456,7 +1456,7 @@ func TestLoadBalancerOps_ReconcileHCLBServices(t *testing.T) {
 					},
 				}
 				action = tt.fx.MockAddService(opts, tt.initialLB, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 			},
 			perform: func(t *testing.T, tt *LBReconcilementTestCase) {
 				changed, err := tt.fx.LBOps.ReconcileHCLBServices(tt.fx.Ctx, tt.initialLB, tt.service)
@@ -1494,7 +1494,7 @@ func TestLoadBalancerOps_ReconcileHCLBServices(t *testing.T) {
 					},
 				}
 				action := tt.fx.MockAddService(opts, tt.initialLB, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 			},
 			perform: func(t *testing.T, tt *LBReconcilementTestCase) {
 				changed, err := tt.fx.LBOps.ReconcileHCLBServices(tt.fx.Ctx, tt.initialLB, tt.service)
@@ -1535,7 +1535,7 @@ func TestLoadBalancerOps_ReconcileHCLBServices(t *testing.T) {
 					On("Get", mock.Anything, "some-cert").
 					Return(cert, nil, nil)
 				action := tt.fx.MockAddService(opts, tt.initialLB, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 			},
 			perform: func(t *testing.T, tt *LBReconcilementTestCase) {
 				changed, err := tt.fx.LBOps.ReconcileHCLBServices(tt.fx.Ctx, tt.initialLB, tt.service)
@@ -1588,7 +1588,7 @@ func TestLoadBalancerOps_ReconcileHCLBServices(t *testing.T) {
 					},
 				}
 				action := tt.fx.MockAddService(opts, tt.initialLB, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 			},
 			perform: func(t *testing.T, tt *LBReconcilementTestCase) {
 				changed, err := tt.fx.LBOps.ReconcileHCLBServices(tt.fx.Ctx, tt.initialLB, tt.service)
@@ -1623,7 +1623,7 @@ func TestLoadBalancerOps_ReconcileHCLBServices(t *testing.T) {
 					},
 				}
 				action := tt.fx.MockAddService(opts, tt.initialLB, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 
 				opts = hcloud.LoadBalancerAddServiceOpts{
 					Protocol:        hcloud.LoadBalancerServiceProtocolHTTP,
@@ -1635,12 +1635,12 @@ func TestLoadBalancerOps_ReconcileHCLBServices(t *testing.T) {
 					},
 				}
 				action = tt.fx.MockAddService(opts, tt.initialLB, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 
 				action = tt.fx.MockDeleteService(tt.initialLB, 80, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 				action = tt.fx.MockDeleteService(tt.initialLB, 443, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 			},
 			perform: func(t *testing.T, tt *LBReconcilementTestCase) {
 				changed, err := tt.fx.LBOps.ReconcileHCLBServices(tt.fx.Ctx, tt.initialLB, tt.service)
@@ -1671,7 +1671,7 @@ func TestLoadBalancerOps_ReconcileHCLBServices(t *testing.T) {
 					},
 				}
 				action := tt.fx.MockUpdateService(opts, tt.initialLB, 80, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 
 				opts = hcloud.LoadBalancerUpdateServiceOpts{
 					Protocol:        hcloud.LoadBalancerServiceProtocolTCP,
@@ -1682,7 +1682,7 @@ func TestLoadBalancerOps_ReconcileHCLBServices(t *testing.T) {
 					},
 				}
 				action = tt.fx.MockUpdateService(opts, tt.initialLB, 443, nil)
-				tt.fx.MockWatchProgress(action, nil)
+				tt.fx.ActionClient.On("WaitFor", tt.fx.Ctx, action).Return(nil)
 			},
 			perform: func(t *testing.T, tt *LBReconcilementTestCase) {
 				changed, err := tt.fx.LBOps.ReconcileHCLBServices(tt.fx.Ctx, tt.initialLB, tt.service)
diff --git a/internal/hcops/testing.go b/internal/hcops/testing.go
index 4c2ee22a..1e8756ab 100644
--- a/internal/hcops/testing.go
+++ b/internal/hcops/testing.go
@@ -129,10 +129,6 @@ func (fx *LoadBalancerOpsFixture) MockListRobotServers(
 	fx.RobotClient.On("ServerGetList").Return(serverList, err)
 }
 
-func (fx *LoadBalancerOpsFixture) MockWatchProgress(a *hcloud.Action, err error) {
-	fx.ActionClient.MockWatchProgress(fx.Ctx, a, err)
-}
-
 func (fx *LoadBalancerOpsFixture) AssertExpectations() {
 	fx.ActionClient.AssertExpectations(fx.T)
 	fx.LBClient.AssertExpectations(fx.T)
diff --git a/tests/e2e/cloud_test.go b/tests/e2e/cloud_test.go
index b9bfdcb3..c805b9a2 100644
--- a/tests/e2e/cloud_test.go
+++ b/tests/e2e/cloud_test.go
@@ -251,7 +251,7 @@ func TestRouteDeleteCorrectRoutes(t *testing.T) {
 			if err != nil {
 				t.Fatal(err)
 			}
-			if err := hcops.WatchAction(ctx, &testCluster.hcloud.Action, action); err != nil {
+			if err := testCluster.hcloud.Action.WaitFor(ctx, action); err != nil {
 				t.Fatal(err)
 			}
 		}
@@ -264,7 +264,7 @@ func TestRouteDeleteCorrectRoutes(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	if err := hcops.WatchAction(ctx, &testCluster.hcloud.Action, action); err != nil {
+	if err := testCluster.hcloud.Action.WaitFor(ctx, action); err != nil {
 		t.Fatal(err)
 	}
 
@@ -275,7 +275,7 @@ func TestRouteDeleteCorrectRoutes(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	if err := hcops.WatchAction(ctx, &testCluster.hcloud.Action, action); err != nil {
+	if err := testCluster.hcloud.Action.WaitFor(ctx, action); err != nil {
 		t.Fatal(err)
 	}
 
diff --git a/tests/e2e/testing.go b/tests/e2e/testing.go
index d51046c7..4f58a3de 100644
--- a/tests/e2e/testing.go
+++ b/tests/e2e/testing.go
@@ -52,7 +52,13 @@ func (tc *TestCluster) Start() error {
 	opts := []hcloud.ClientOption{
 		hcloud.WithToken(token),
 		hcloud.WithApplication("hcloud-ccm-testsuite", "1.0"),
-		hcloud.WithPollBackoffFunc(hcloud.ExponentialBackoff(2, 1*time.Second)),
+		hcloud.WithPollOpts(hcloud.PollOpts{
+			BackoffFunc: hcloud.ExponentialBackoffWithOpts(hcloud.ExponentialBackoffOpts{
+				Base:       time.Second,
+				Multiplier: 2,
+				Cap:        10 * time.Second,
+			}),
+		}),
 	}
 	tc.hcloud = hcloud.NewClient(opts...)