Skip to content

Commit c95e423

Browse files
committed
Add a client for Virtual Network management
This commit adds entities to generate the (unavailable) XML schema for virtual network specification, and a client with Get and Set operations for the currently active subscription. The pattern for using the client is the same as the powershell version - get the running config, merge in any desired changes and set the configuration to the new state. There is no documentation on what the concurrency constraints are with the underlying Azure REST API, I suspect it is not safe to call concurrently though can't verify this.
1 parent af6985d commit c95e423

File tree

3 files changed

+166
-18
lines changed

3 files changed

+166
-18
lines changed

clients/vnetClient/entities.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package vnetClient
2+
3+
import (
4+
"encoding/xml"
5+
)
6+
7+
const xmlNamespace = "http://schemas.microsoft.com/ServiceHosting/2011/07/NetworkConfiguration"
8+
const xmlNamespaceXsd = "http://www.w3.org/2001/XMLSchema"
9+
const xmlNamespaceXsi = "http://www.w3.org/2001/XMLSchema-instance"
10+
11+
//NetworkConfiguration represents the network configuration for an entire Azure
12+
//subscription. TODO: Nicer builder methods for these that abstract away the
13+
//underlying structure
14+
type NetworkConfiguration struct {
15+
XMLName xml.Name `xml:"NetworkConfiguration"`
16+
XmlNamespaceXsd string `xml:"xmlns:xsd,attr"`
17+
XmlNamespaceXsi string `xml:"xmlns:xsi,attr"`
18+
Xmlns string `xml:"xmlns,attr"`
19+
Configuration VirtualNetworkConfiguration `xml:"VirtualNetworkConfiguration"`
20+
}
21+
22+
//NewNetworkConfiguration creates a new empty NetworkConfiguration structure for
23+
//further configuration. The XML namespaces are set correctly.
24+
func NewNetworkConfiguration() NetworkConfiguration {
25+
networkConfiguration := NetworkConfiguration{}
26+
networkConfiguration.setXmlNamespaces()
27+
return networkConfiguration
28+
}
29+
30+
//setXmlNamespaces ensure that all of the required namespaces are set. It should
31+
//be called prior to marshalling the structure to XML for use with the Azure REST
32+
//endpoint. It is used internally prior to submitting requests, but since it is
33+
//idempotent there is no harm in repeat calls.
34+
func (self *NetworkConfiguration) setXmlNamespaces() {
35+
self.XmlNamespaceXsd = xmlNamespaceXsd
36+
self.XmlNamespaceXsi = xmlNamespaceXsi
37+
self.Xmlns = xmlNamespace
38+
}
39+
40+
type VirtualNetworkConfiguration struct {
41+
Dns Dns `xml:"Dns,omitempty"`
42+
LocalNetworkSites []LocalNetworkSite `xml:"LocalNetworkSites>LocalNetworkSite"`
43+
VirtualNetworkSites []VirtualNetworkSite `xml:"VirtualNetworkSites>VirtualNetworkSite"`
44+
}
45+
46+
type Dns struct {
47+
DnsServers []DnsServer `xml:"DnsServers,omitempty>DnsServer,omitempty"`
48+
}
49+
50+
type DnsServer struct {
51+
XMLName xml.Name `xml:"DnsServer"`
52+
Name string `xml:"name,attr"`
53+
IPAddress string `xml:"IPAddress,attr"`
54+
}
55+
56+
type DnsServerRef struct {
57+
Name string `xml:"name,attr"`
58+
}
59+
60+
type VirtualNetworkSite struct {
61+
Name string `xml:"name,attr"`
62+
Location string `xml:"Location,attr"`
63+
AddressSpace AddressSpace `xml:"AddressSpace"`
64+
Subnets []Subnet `xml:"Subnets>Subnet"`
65+
DnsServersRef []DnsServerRef `xml:"DnsServersRef,omitempty>DnsServerRef"`
66+
}
67+
68+
type LocalNetworkSite struct {
69+
Name string `xml:"name,attr"`
70+
VPNGatewayAddress string
71+
AddressSpace AddressSpace
72+
}
73+
74+
type AddressSpace struct {
75+
AddressPrefix []string
76+
}
77+
78+
type Subnet struct {
79+
Name string `xml:"name,attr"`
80+
AddressPrefix string
81+
}

clients/vnetClient/vnetClient.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package vnetClient
2+
3+
import (
4+
"encoding/xml"
5+
azure "github.com/MSOpenTech/azure-sdk-for-go"
6+
)
7+
8+
const (
9+
azureNetworkConfigurationURL = "services/networking/media"
10+
)
11+
12+
//GetVirtualNetworkConfiguration retreives the current virtual network
13+
//configuration for the currently active subscription. Note that the
14+
//underlying Azure API means that network related operations are not safe
15+
//for running concurrently.
16+
func GetVirtualNetworkConfiguration() (NetworkConfiguration, error) {
17+
networkConfiguration := NewNetworkConfiguration()
18+
response, err := azure.SendAzureGetRequest(azureNetworkConfigurationURL)
19+
if err != nil {
20+
return networkConfiguration, err
21+
}
22+
23+
err = xml.Unmarshal(response, &networkConfiguration)
24+
if err != nil {
25+
return networkConfiguration, err
26+
}
27+
28+
return networkConfiguration, nil
29+
}
30+
31+
//SetVirtualNetworkConfiguration configures the virtual networks for the
32+
//currently active subscription according to the NetworkConfiguration given.
33+
//Note that the underlying Azure API means that network related operations
34+
//are not safe for running concurrently.
35+
func SetVirtualNetworkConfiguration(networkConfiguration NetworkConfiguration) error {
36+
networkConfiguration.setXmlNamespaces()
37+
networkConfigurationBytes, err := xml.Marshal(networkConfiguration)
38+
if err != nil {
39+
return err
40+
}
41+
42+
requestId, err := azure.SendAzurePutRequest(azureNetworkConfigurationURL, "text/plain", networkConfigurationBytes)
43+
if err != nil {
44+
return err
45+
}
46+
47+
err = azure.WaitAsyncOperation(requestId)
48+
return err
49+
}

common.go

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ import (
1717
const (
1818
paramNotSpecifiedError = "Parameter %s is not specified."
1919

20-
azureManagementDnsName = "https://management.core.windows.net"
21-
msVersionHeader = "x-ms-version"
22-
msVersionHeaderValue = "2014-05-01"
23-
contentHeader = "Content-Type"
24-
contentHeaderValue = "application/xml"
25-
requestIdHeader = "X-Ms-Request-Id"
20+
azureManagementDnsName = "https://management.core.windows.net"
21+
msVersionHeader = "x-ms-version"
22+
msVersionHeaderValue = "2014-05-01"
23+
contentHeader = "Content-Type"
24+
defaultContentHeaderValue = "application/xml"
25+
requestIdHeader = "X-Ms-Request-Id"
2626
)
2727

2828
//Region public methods starts
@@ -32,7 +32,7 @@ func SendAzureGetRequest(url string) ([]byte, error) {
3232
return nil, fmt.Errorf(paramNotSpecifiedError, "url")
3333
}
3434

35-
response, err := SendAzureRequest(url, "GET", nil)
35+
response, err := SendAzureRequest(url, "GET", "", nil)
3636
if err != nil {
3737
return nil, err
3838
}
@@ -46,7 +46,21 @@ func SendAzurePostRequest(url string, data []byte) (string, error) {
4646
return "", fmt.Errorf(paramNotSpecifiedError, "url")
4747
}
4848

49-
response, err := SendAzureRequest(url, "POST", data)
49+
response, err := SendAzureRequest(url, "POST", "", data)
50+
if err != nil {
51+
return "", err
52+
}
53+
54+
requestId := response.Header[requestIdHeader]
55+
return requestId[0], nil
56+
}
57+
58+
func SendAzurePutRequest(url string, contentType string, data []byte) (string, error) {
59+
if len(url) == 0 {
60+
return "", fmt.Errorf(paramNotSpecifiedError, contentType, "url")
61+
}
62+
63+
response, err := SendAzureRequest(url, "PUT", contentType, data)
5064
if err != nil {
5165
return "", err
5266
}
@@ -60,7 +74,7 @@ func SendAzureDeleteRequest(url string) (string, error) {
6074
return "", fmt.Errorf(paramNotSpecifiedError, "url")
6175
}
6276

63-
response, err := SendAzureRequest(url, "DELETE", nil)
77+
response, err := SendAzureRequest(url, "DELETE", "", nil)
6478
if err != nil {
6579
return "", err
6680
}
@@ -69,7 +83,7 @@ func SendAzureDeleteRequest(url string) (string, error) {
6983
return requestId[0], nil
7084
}
7185

72-
func SendAzureRequest(url string, requestType string, data []byte) (*http.Response, error) {
86+
func SendAzureRequest(url string, requestType string, contentType string, data []byte) (*http.Response, error) {
7387
if len(url) == 0 {
7488
return nil, fmt.Errorf(paramNotSpecifiedError, "url")
7589
}
@@ -79,7 +93,7 @@ func SendAzureRequest(url string, requestType string, data []byte) (*http.Respon
7993

8094
client := createHttpClient()
8195

82-
response, err := sendRequest(client, url, requestType, data, 7)
96+
response, err := sendRequest(client, url, requestType, contentType, data, 7)
8397
if err != nil {
8498
return nil, err
8599
}
@@ -159,7 +173,7 @@ func CheckStringParams(url string) ([]byte, error) {
159173
return nil, fmt.Errorf(paramNotSpecifiedError, "url")
160174
}
161175

162-
response, err := SendAzureRequest(url, "GET", nil)
176+
response, err := SendAzureRequest(url, "GET", "", nil)
163177
if err != nil {
164178
return nil, err
165179
}
@@ -188,8 +202,8 @@ func NewUUID() (string, error) {
188202

189203
//Region private methods starts
190204

191-
func sendRequest(client *http.Client, url string, requestType string, data []byte, numberOfRetries int) (*http.Response, error) {
192-
request, reqErr := createAzureRequest(url, requestType, data)
205+
func sendRequest(client *http.Client, url string, requestType string, contentType string, data []byte, numberOfRetries int) (*http.Response, error) {
206+
request, reqErr := createAzureRequest(url, requestType, contentType, data)
193207
if reqErr != nil {
194208
return nil, reqErr
195209
}
@@ -200,7 +214,7 @@ func sendRequest(client *http.Client, url string, requestType string, data []byt
200214
return nil, err
201215
}
202216

203-
return sendRequest(client, url, requestType, data, numberOfRetries-1)
217+
return sendRequest(client, url, requestType, contentType, data, numberOfRetries-1)
204218
}
205219

206220
if response.StatusCode > 299 {
@@ -211,7 +225,7 @@ func sendRequest(client *http.Client, url string, requestType string, data []byt
211225
return nil, azureErr
212226
}
213227

214-
return sendRequest(client, url, requestType, data, numberOfRetries-1)
228+
return sendRequest(client, url, requestType, contentType, data, numberOfRetries-1)
215229
}
216230
}
217231

@@ -228,7 +242,7 @@ func getAzureError(responseBody []byte) error {
228242
return error
229243
}
230244

231-
func createAzureRequest(url string, requestType string, data []byte) (*http.Request, error) {
245+
func createAzureRequest(url string, requestType string, contentType string, data []byte) (*http.Request, error) {
232246
var request *http.Request
233247
var err error
234248

@@ -245,7 +259,11 @@ func createAzureRequest(url string, requestType string, data []byte) (*http.Requ
245259
}
246260

247261
request.Header.Add(msVersionHeader, msVersionHeaderValue)
248-
request.Header.Add(contentHeader, contentHeaderValue)
262+
if len(contentType) > 0 {
263+
request.Header.Add(contentHeader, contentType)
264+
} else {
265+
request.Header.Add(contentHeader, defaultContentHeaderValue)
266+
}
249267

250268
return request, nil
251269
}

0 commit comments

Comments
 (0)