1// +build go1.7
2
3package sql
4
5// Copyright (c) Microsoft Corporation. All rights reserved.
6// Licensed under the MIT License. See License.txt in the project root for license information.
7
8import (
9	"encoding/xml"
10	"fmt"
11	"time"
12
13	"github.com/Azure/azure-sdk-for-go/services/classic/management"
14)
15
16// Definitions of numerous constants representing API endpoints.
17const (
18	azureCreateDatabaseServerURL = "services/sqlservers/servers"
19	azureListDatabaseServersURL  = "services/sqlservers/servers"
20	azureDeleteDatabaseServerURL = "services/sqlservers/servers/%s"
21
22	azureCreateFirewallRuleURL = "services/sqlservers/servers/%s/firewallrules"
23	azureGetFirewallRuleURL    = "services/sqlservers/servers/%s/firewallrules/%s"
24	azureListFirewallRulesURL  = "services/sqlservers/servers/%s/firewallrules"
25	azureUpdateFirewallRuleURL = "services/sqlservers/servers/%s/firewallrules/%s"
26	azureDeleteFirewallRuleURL = "services/sqlservers/servers/%s/firewallrules/%s"
27
28	azureCreateDatabaseURL = "services/sqlservers/servers/%s/databases"
29	azureGetDatabaseURL    = "services/sqlservers/servers/%s/databases/%s"
30	azureListDatabasesURL  = "services/sqlservers/servers/%s/databases?contentview=generic"
31	azureUpdateDatabaseURL = "services/sqlservers/servers/%s/databases/%s"
32	azureDeleteDatabaseURL = "services/sqlservers/servers/%s/databases/%s"
33
34	errParamNotSpecified = "Parameter %s was not specified."
35
36	DatabaseStateCreating = "Creating"
37)
38
39// SQLDatabaseClient defines various database CRUD operations.
40// It contains a management.Client for making the actual http calls.
41type SQLDatabaseClient struct {
42	mgmtClient management.Client
43}
44
45// NewClient returns a new SQLDatabaseClient struct with the provided
46// management.Client as the underlying client.
47func NewClient(mgmtClient management.Client) SQLDatabaseClient {
48	return SQLDatabaseClient{mgmtClient}
49}
50
51// CreateServer creates a new Azure SQL Database server and return its name.
52//
53// https://msdn.microsoft.com/en-us/library/azure/dn505699.aspx
54func (c SQLDatabaseClient) CreateServer(params DatabaseServerCreateParams) (string, error) {
55	req, err := xml.Marshal(params)
56	if err != nil {
57		return "", err
58	}
59
60	resp, err := c.mgmtClient.SendAzurePostRequestWithReturnedResponse(azureCreateDatabaseServerURL, req)
61	if err != nil {
62		return "", err
63	}
64
65	var name string
66	err = xml.Unmarshal(resp, &name)
67
68	return name, err
69}
70
71// ListServers retrieves the Azure SQL Database servers for this subscription.
72//
73// https://msdn.microsoft.com/en-us/library/azure/dn505702.aspx
74func (c SQLDatabaseClient) ListServers() (ListServersResponse, error) {
75	var resp ListServersResponse
76
77	data, err := c.mgmtClient.SendAzureGetRequest(azureListDatabaseServersURL)
78	if err != nil {
79		return resp, err
80	}
81
82	err = xml.Unmarshal(data, &resp)
83	return resp, err
84}
85
86// DeleteServer deletes an Azure SQL Database server (including all its databases).
87//
88// https://msdn.microsoft.com/en-us/library/azure/dn505695.aspx
89func (c SQLDatabaseClient) DeleteServer(name string) error {
90	if name == "" {
91		return fmt.Errorf(errParamNotSpecified, "name")
92	}
93
94	url := fmt.Sprintf(azureDeleteDatabaseServerURL, name)
95	_, err := c.mgmtClient.SendAzureDeleteRequest(url)
96	return err
97}
98
99// CreateFirewallRule creates an Azure SQL Database server
100// firewall rule.
101//
102// https://msdn.microsoft.com/en-us/library/azure/dn505712.aspx
103func (c SQLDatabaseClient) CreateFirewallRule(server string, params FirewallRuleCreateParams) error {
104	if server == "" {
105		return fmt.Errorf(errParamNotSpecified, "server")
106	}
107
108	req, err := xml.Marshal(params)
109	if err != nil {
110		return err
111	}
112
113	url := fmt.Sprintf(azureCreateFirewallRuleURL, server)
114
115	_, err = c.mgmtClient.SendAzurePostRequest(url, req)
116	return err
117}
118
119// GetFirewallRule gets the details of an Azure SQL Database Server firewall rule.
120//
121// https://msdn.microsoft.com/en-us/library/azure/dn505698.aspx
122func (c SQLDatabaseClient) GetFirewallRule(server, ruleName string) (FirewallRuleResponse, error) {
123	var rule FirewallRuleResponse
124
125	if server == "" {
126		return rule, fmt.Errorf(errParamNotSpecified, "server")
127	}
128	if ruleName == "" {
129		return rule, fmt.Errorf(errParamNotSpecified, "ruleName")
130	}
131
132	url := fmt.Sprintf(azureGetFirewallRuleURL, server, ruleName)
133	resp, err := c.mgmtClient.SendAzureGetRequest(url)
134	if err != nil {
135		return rule, err
136	}
137
138	err = xml.Unmarshal(resp, &rule)
139	return rule, err
140}
141
142// ListFirewallRules retrieves the set of firewall rules for an Azure SQL
143// Database Server.
144//
145// https://msdn.microsoft.com/en-us/library/azure/dn505715.aspx
146func (c SQLDatabaseClient) ListFirewallRules(server string) (ListFirewallRulesResponse, error) {
147	var rules ListFirewallRulesResponse
148
149	if server == "" {
150		return rules, fmt.Errorf(errParamNotSpecified, "server")
151	}
152
153	url := fmt.Sprintf(azureListFirewallRulesURL, server)
154	resp, err := c.mgmtClient.SendAzureGetRequest(url)
155	if err != nil {
156		return rules, err
157	}
158
159	err = xml.Unmarshal(resp, &rules)
160	return rules, err
161}
162
163// UpdateFirewallRule update a firewall rule for an Azure SQL Database server.
164//
165// https://msdn.microsoft.com/en-us/library/azure/dn505707.aspx
166func (c SQLDatabaseClient) UpdateFirewallRule(server, ruleName string, params FirewallRuleUpdateParams) error {
167	if server == "" {
168		return fmt.Errorf(errParamNotSpecified, "server")
169	}
170	if ruleName == "" {
171		return fmt.Errorf(errParamNotSpecified, "ruleName")
172	}
173
174	req, err := xml.Marshal(params)
175	if err != nil {
176		return err
177	}
178
179	url := fmt.Sprintf(azureUpdateFirewallRuleURL, server, ruleName)
180	_, err = c.mgmtClient.SendAzurePutRequest(url, "text/xml", req)
181	return err
182}
183
184// DeleteFirewallRule deletes an Azure SQL Database server firewall rule.
185//
186// https://msdn.microsoft.com/en-us/library/azure/dn505706.aspx
187func (c SQLDatabaseClient) DeleteFirewallRule(server, ruleName string) error {
188	if server == "" {
189		return fmt.Errorf(errParamNotSpecified, "server")
190	}
191	if ruleName == "" {
192		return fmt.Errorf(errParamNotSpecified, "ruleName")
193	}
194
195	url := fmt.Sprintf(azureDeleteFirewallRuleURL, server, ruleName)
196
197	_, err := c.mgmtClient.SendAzureDeleteRequest(url)
198	return err
199}
200
201// CreateDatabase creates a new Microsoft Azure SQL Database on the given database server.
202//
203// https://msdn.microsoft.com/en-us/library/azure/dn505701.aspx
204func (c SQLDatabaseClient) CreateDatabase(server string, params DatabaseCreateParams) error {
205	if server == "" {
206		return fmt.Errorf(errParamNotSpecified, "server")
207	}
208
209	req, err := xml.Marshal(params)
210	if err != nil {
211		return err
212	}
213
214	target := fmt.Sprintf(azureCreateDatabaseURL, server)
215	_, err = c.mgmtClient.SendAzurePostRequest(target, req)
216	return err
217}
218
219// WaitForDatabaseCreation is a helper method which waits
220// for the creation of the database on the given server.
221func (c SQLDatabaseClient) WaitForDatabaseCreation(
222	server, database string,
223	cancel chan struct{}) error {
224	for {
225		stat, err := c.GetDatabase(server, database)
226		if err != nil {
227			return err
228		}
229		if stat.State != DatabaseStateCreating {
230			return nil
231		}
232
233		select {
234		case <-time.After(management.DefaultOperationPollInterval):
235		case <-cancel:
236			return management.ErrOperationCancelled
237		}
238	}
239}
240
241// GetDatabase gets the details for an Azure SQL Database.
242//
243// https://msdn.microsoft.com/en-us/library/azure/dn505708.aspx
244func (c SQLDatabaseClient) GetDatabase(server, database string) (ServiceResource, error) {
245	var db ServiceResource
246
247	if database == "" {
248		return db, fmt.Errorf(errParamNotSpecified, "database")
249	}
250	if server == "" {
251		return db, fmt.Errorf(errParamNotSpecified, "server")
252	}
253
254	url := fmt.Sprintf(azureGetDatabaseURL, server, database)
255	resp, err := c.mgmtClient.SendAzureGetRequest(url)
256	if err != nil {
257		return db, err
258	}
259
260	err = xml.Unmarshal(resp, &db)
261	return db, err
262}
263
264// ListDatabases returns a list of Azure SQL Databases on the given server.
265//
266// https://msdn.microsoft.com/en-us/library/azure/dn505711.aspx
267func (c SQLDatabaseClient) ListDatabases(server string) (ListDatabasesResponse, error) {
268	var databases ListDatabasesResponse
269	if server == "" {
270		return databases, fmt.Errorf(errParamNotSpecified, "server name")
271	}
272
273	url := fmt.Sprintf(azureListDatabasesURL, server)
274	resp, err := c.mgmtClient.SendAzureGetRequest(url)
275	if err != nil {
276		return databases, err
277	}
278
279	err = xml.Unmarshal(resp, &databases)
280	return databases, err
281}
282
283// UpdateDatabase updates the details of the given Database off the given server.
284//
285// https://msdn.microsoft.com/en-us/library/azure/dn505718.aspx
286func (c SQLDatabaseClient) UpdateDatabase(
287	server, database string,
288	params ServiceResourceUpdateParams) (management.OperationID, error) {
289	if database == "" {
290		return "", fmt.Errorf(errParamNotSpecified, "database")
291	}
292	if server == "" {
293		return "", fmt.Errorf(errParamNotSpecified, "server")
294	}
295
296	url := fmt.Sprintf(azureUpdateDatabaseURL, server, database)
297	req, err := xml.Marshal(params)
298	if err != nil {
299		return "", err
300	}
301
302	return c.mgmtClient.SendAzurePutRequest(url, "text/xml", req)
303}
304
305// DeleteDatabase deletes the Azure SQL Database off the given server.
306//
307// https://msdn.microsoft.com/en-us/library/azure/dn505705.aspx
308func (c SQLDatabaseClient) DeleteDatabase(server, database string) error {
309	if database == "" {
310		return fmt.Errorf(errParamNotSpecified, "database")
311	}
312	if server == "" {
313		return fmt.Errorf(errParamNotSpecified, "server")
314	}
315
316	url := fmt.Sprintf(azureDeleteDatabaseURL, server, database)
317
318	_, err := c.mgmtClient.SendAzureDeleteRequest(url)
319
320	return err
321}
322