Repository: hetznercloud/hcloud-go
Pull request #277 (2023-06-21 10:23:15)

feat(network): add field expose_routes_to_vswitch
Feature was released today.

Marked as draft until the other Go integrations are ready.


Patch:
diff --git a/hcloud/network.go b/hcloud/network.go
index 5a19681..cf85a55 100644
--- a/hcloud/network.go
+++ b/hcloud/network.go
@@ -45,6 +45,9 @@ type Network struct {
 	Servers    []*Server
 	Protection NetworkProtection
 	Labels     map[string]string
+
+	// ExposeRoutesToVSwitch indicates if the routes from this network should be exposed to the vSwitch connection.
+	ExposeRoutesToVSwitch bool
 }
 
 // NetworkSubnet represents a subnet of a network in the Hetzner Cloud.
@@ -190,6 +193,9 @@ func (c *NetworkClient) Delete(ctx context.Context, network *Network) (*Response
 type NetworkUpdateOpts struct {
 	Name   string
 	Labels map[string]string
+	// ExposeRoutesToVSwitch indicates if the routes from this network should be exposed to the vSwitch connection.
+	// The exposing only takes effect if a vSwitch connection is active.
+	ExposeRoutesToVSwitch *bool
 }
 
 // Update updates a network.
@@ -200,6 +206,10 @@ func (c *NetworkClient) Update(ctx context.Context, network *Network, opts Netwo
 	if opts.Labels != nil {
 		reqBody.Labels = &opts.Labels
 	}
+	if opts.ExposeRoutesToVSwitch != nil {
+		reqBody.ExposeRoutesToVSwitch = opts.ExposeRoutesToVSwitch
+	}
+
 	reqBodyData, err := json.Marshal(reqBody)
 	if err != nil {
 		return nil, nil, err
@@ -226,6 +236,9 @@ type NetworkCreateOpts struct {
 	Subnets []NetworkSubnet
 	Routes  []NetworkRoute
 	Labels  map[string]string
+	// ExposeRoutesToVSwitch indicates if the routes from this network should be exposed to the vSwitch connection.
+	// The exposing only takes effect if a vSwitch connection is active.
+	ExposeRoutesToVSwitch bool
 }
 
 // Validate checks if options are valid.
@@ -245,8 +258,9 @@ func (c *NetworkClient) Create(ctx context.Context, opts NetworkCreateOpts) (*Ne
 		return nil, nil, err
 	}
 	reqBody := schema.NetworkCreateRequest{
-		Name:    opts.Name,
-		IPRange: opts.IPRange.String(),
+		Name:                  opts.Name,
+		IPRange:               opts.IPRange.String(),
+		ExposeRoutesToVSwitch: opts.ExposeRoutesToVSwitch,
 	}
 	for _, subnet := range opts.Subnets {
 		s := schema.NetworkSubnet{
diff --git a/hcloud/schema.go b/hcloud/schema.go
index c76d7de..f4a9366 100644
--- a/hcloud/schema.go
+++ b/hcloud/schema.go
@@ -403,7 +403,8 @@ func NetworkFromSchema(s schema.Network) *Network {
 		Protection: NetworkProtection{
 			Delete: s.Protection.Delete,
 		},
-		Labels: map[string]string{},
+		Labels:                map[string]string{},
+		ExposeRoutesToVSwitch: s.ExposeRoutesToVSwitch,
 	}
 
 	_, n.IPRange, _ = net.ParseCIDR(s.IPRange)
diff --git a/hcloud/schema/network.go b/hcloud/schema/network.go
index 87e184d..56bcbea 100644
--- a/hcloud/schema/network.go
+++ b/hcloud/schema/network.go
@@ -4,15 +4,16 @@ import "time"
 
 // Network defines the schema of a network.
 type Network struct {
-	ID         int               `json:"id"`
-	Name       string            `json:"name"`
-	Created    time.Time         `json:"created"`
-	IPRange    string            `json:"ip_range"`
-	Subnets    []NetworkSubnet   `json:"subnets"`
-	Routes     []NetworkRoute    `json:"routes"`
-	Servers    []int             `json:"servers"`
-	Protection NetworkProtection `json:"protection"`
-	Labels     map[string]string `json:"labels"`
+	ID                    int               `json:"id"`
+	Name                  string            `json:"name"`
+	Created               time.Time         `json:"created"`
+	IPRange               string            `json:"ip_range"`
+	Subnets               []NetworkSubnet   `json:"subnets"`
+	Routes                []NetworkRoute    `json:"routes"`
+	Servers               []int             `json:"servers"`
+	Protection            NetworkProtection `json:"protection"`
+	Labels                map[string]string `json:"labels"`
+	ExposeRoutesToVSwitch bool              `json:"expose_routes_to_vswitch"`
 }
 
 // NetworkSubnet represents a subnet of a network.
@@ -37,8 +38,9 @@ type NetworkProtection struct {
 
 // NetworkUpdateRequest defines the schema of the request to update a network.
 type NetworkUpdateRequest struct {
-	Name   string             `json:"name,omitempty"`
-	Labels *map[string]string `json:"labels,omitempty"`
+	Name                  string             `json:"name,omitempty"`
+	Labels                *map[string]string `json:"labels,omitempty"`
+	ExposeRoutesToVSwitch *bool              `json:"expose_routes_to_vswitch,omitempty"`
 }
 
 // NetworkUpdateResponse defines the schema of the response when updating a network.
@@ -60,11 +62,12 @@ type NetworkGetResponse struct {
 
 // NetworkCreateRequest defines the schema of the request to create a network.
 type NetworkCreateRequest struct {
-	Name    string             `json:"name"`
-	IPRange string             `json:"ip_range"`
-	Subnets []NetworkSubnet    `json:"subnets,omitempty"`
-	Routes  []NetworkRoute     `json:"routes,omitempty"`
-	Labels  *map[string]string `json:"labels,omitempty"`
+	Name                  string             `json:"name"`
+	IPRange               string             `json:"ip_range"`
+	Subnets               []NetworkSubnet    `json:"subnets,omitempty"`
+	Routes                []NetworkRoute     `json:"routes,omitempty"`
+	Labels                *map[string]string `json:"labels,omitempty"`
+	ExposeRoutesToVSwitch bool               `json:"expose_routes_to_vswitch"`
 }
 
 // NetworkCreateResponse defines the schema of the response when


Test patch:
diff --git a/hcloud/network_test.go b/hcloud/network_test.go
index d5f5d00..b06f334 100644
--- a/hcloud/network_test.go
+++ b/hcloud/network_test.go
@@ -213,6 +213,36 @@ func TestNetworkCreate(t *testing.T) {
 			t.Fatal(err)
 		}
 	})
+
+	t.Run("optional fields", func(t *testing.T) {
+		env := newTestEnv()
+		defer env.Teardown()
+
+		env.Mux.HandleFunc("/networks", func(w http.ResponseWriter, r *http.Request) {
+			var reqBody schema.NetworkCreateRequest
+			if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil {
+				t.Fatal(err)
+			}
+			if reqBody.ExposeRoutesToVSwitch != true {
+				t.Errorf("unexpected ExposeRoutesToVSwitch: %v", reqBody.ExposeRoutesToVSwitch)
+			}
+
+			json.NewEncoder(w).Encode(schema.NetworkCreateResponse{
+				Network: schema.Network{
+					ID: 1,
+				},
+			})
+		})
+		opts := NetworkCreateOpts{
+			Name:                  "my-network",
+			IPRange:               ipRange,
+			ExposeRoutesToVSwitch: true,
+		}
+		_, _, err := env.Client.Network.Create(ctx, opts)
+		if err != nil {
+			t.Fatal(err)
+		}
+	})
 }
 
 func TestNetworkDelete(t *testing.T) {
@@ -245,7 +275,7 @@ func TestNetworkClientUpdate(t *testing.T) {
 			if r.Method != "PUT" {
 				t.Error("expected PUT")
 			}
-			var reqBody schema.ServerUpdateRequest
+			var reqBody schema.NetworkUpdateRequest
 			if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil {
 				t.Fatal(err)
 			}
@@ -280,7 +310,7 @@ func TestNetworkClientUpdate(t *testing.T) {
 			if r.Method != "PUT" {
 				t.Error("expected PUT")
 			}
-			var reqBody schema.ServerUpdateRequest
+			var reqBody schema.NetworkUpdateRequest
 			if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil {
 				t.Fatal(err)
 			}
@@ -307,6 +337,41 @@ func TestNetworkClientUpdate(t *testing.T) {
 		}
 	})
 
+	t.Run("update expose routes to vswitch", func(t *testing.T) {
+		env := newTestEnv()
+		defer env.Teardown()
+
+		env.Mux.HandleFunc("/networks/1", func(w http.ResponseWriter, r *http.Request) {
+			if r.Method != "PUT" {
+				t.Error("expected PUT")
+			}
+			var reqBody schema.NetworkUpdateRequest
+			if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil {
+				t.Fatal(err)
+			}
+			if reqBody.ExposeRoutesToVSwitch == nil || *reqBody.ExposeRoutesToVSwitch != true {
+				t.Errorf("unexpected field in request: %v", reqBody.ExposeRoutesToVSwitch)
+			}
+			json.NewEncoder(w).Encode(schema.NetworkUpdateResponse{
+				Network: schema.Network{
+					ID: 1,
+				},
+			})
+		})
+
+		opts := NetworkUpdateOpts{
+			ExposeRoutesToVSwitch: Ptr(true),
+		}
+		updatedNetwork, _, err := env.Client.Network.Update(ctx, network, opts)
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		if updatedNetwork.ID != 1 {
+			t.Errorf("unexpected network ID: %v", updatedNetwork.ID)
+		}
+	})
+
 	t.Run("no updates", func(t *testing.T) {
 		env := newTestEnv()
 		defer env.Teardown()
@@ -315,7 +380,7 @@ func TestNetworkClientUpdate(t *testing.T) {
 			if r.Method != "PUT" {
 				t.Error("expected PUT")
 			}
-			var reqBody schema.ServerUpdateRequest
+			var reqBody schema.NetworkUpdateRequest
 			if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil {
 				t.Fatal(err)
 			}