1package vps
2
3import (
4	"fmt"
5	"github.com/transip/gotransip/v6"
6	"github.com/transip/gotransip/v6/ipaddress"
7	"github.com/transip/gotransip/v6/product"
8	"github.com/transip/gotransip/v6/repository"
9	"github.com/transip/gotransip/v6/rest"
10	"net"
11	"net/url"
12	"strings"
13	"time"
14)
15
16// Repository is the vps repository
17// this repository allows you to manage all VPS services for your TransIP account
18type Repository repository.RestRepository
19
20// GetAll returns a list of all your VPSs
21func (r *Repository) GetAll() ([]Vps, error) {
22	var response vpssWrapper
23	restRequest := rest.Request{Endpoint: "/vps"}
24	err := r.Client.Get(restRequest, &response)
25
26	return response.Vpss, err
27}
28
29// GetAllByTags returns a list of all VPSs that match the tags provided
30func (r *Repository) GetAllByTags(tags []string) ([]Vps, error) {
31	var response vpssWrapper
32	restRequest := rest.Request{Endpoint: "/vps", Parameters: url.Values{"tags": tags}}
33	err := r.Client.Get(restRequest, &response)
34
35	return response.Vpss, err
36}
37
38// GetSelection returns a limited list of VPSs,
39// specify how many and which page/chunk of VPSs you want to retrieve
40func (r *Repository) GetSelection(page int, itemsPerPage int) ([]Vps, error) {
41	var response vpssWrapper
42	params := url.Values{
43		"pageSize": []string{fmt.Sprintf("%d", itemsPerPage)},
44		"page":     []string{fmt.Sprintf("%d", page)},
45	}
46
47	restRequest := rest.Request{Endpoint: "/vps", Parameters: params}
48	err := r.Client.Get(restRequest, &response)
49
50	return response.Vpss, err
51}
52
53// GetByName returns information on a specific VPS by name
54func (r *Repository) GetByName(vpsName string) (Vps, error) {
55	var response vpsWrapper
56	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s", vpsName)}
57	err := r.Client.Get(restRequest, &response)
58
59	return response.Vps, err
60}
61
62// Order allows you to order a new VPS
63func (r *Repository) Order(vpsOrder Order) error {
64	restRequest := rest.Request{Endpoint: "/vps", Body: &vpsOrder}
65
66	return r.Client.Post(restRequest)
67}
68
69// OrderMultiple allows you to order multiple vpses at the same time
70func (r *Repository) OrderMultiple(orders []Order) error {
71	requestBody := vpssOrderWrapper{Orders: orders}
72	restRequest := rest.Request{Endpoint: "/vps", Body: &requestBody}
73
74	return r.Client.Post(restRequest)
75}
76
77// Clone allows you to clone an existing VPS
78// There are a few things to take into account when you want to clone an existing VPS to a new VPS:
79//
80// - If the original VPS (which you’re going to clone) is currently locked, the clone will fail;
81//
82// - Cloned control panels can be used on the VPS, but as the IP address changes, this does require you to synchronise
83//   the new license on the new VPS (licenses are often IP-based);
84//
85// - Possibly, your VPS has its network interface(s) configured using (a) static IP(‘s) rather than a dynamic allocation
86//   using DHCP. If this is the case, you have to configure the new IP(‘s) on the new VPS.
87//   Do note that this is not the case with our pre-installed control panel images;
88//
89// - VPS add-ons such as Big Storage aren’t affected by cloning - these will stay attached to the original VPS and can’t
90//   be swapped automatically
91func (r *Repository) Clone(vpsName string) error {
92	requestBody := cloneRequest{VpsName: vpsName}
93	restRequest := rest.Request{Endpoint: "/vps", Body: &requestBody}
94
95	return r.Client.Post(restRequest)
96}
97
98// CloneToAvailabilityZone allows you to clone a vps to a specific availability zone, identified by name
99func (r *Repository) CloneToAvailabilityZone(vpsName string, availabilityZone string) error {
100	requestBody := cloneRequest{VpsName: vpsName, AvailabilityZone: availabilityZone}
101	restRequest := rest.Request{Endpoint: "/vps", Body: &requestBody}
102
103	return r.Client.Post(restRequest)
104}
105
106// Update allows you to lock/unlock a VPS, update a VPS description, and add/remove tags.
107//
108//   For locking the VPS, set isCustomerLocked to true. Set the value to false for unlocking the VPS
109//   You can change your VPS description by simply changing the description attribute
110//   To add/remove tags, you must update the tags attribute
111func (r *Repository) Update(vps Vps) error {
112	requestBody := vpsWrapper{Vps: vps}
113	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s", vps.Name), Body: &requestBody}
114
115	return r.Client.Put(restRequest)
116}
117
118// Start allows you to start a VPS, given that it’s currently in a stopped state
119func (r *Repository) Start(vpsName string) error {
120	requestBody := actionWrapper{Action: "start"}
121	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s", vpsName), Body: &requestBody}
122
123	return r.Client.Patch(restRequest)
124}
125
126// Stop allows you to stop a VPS
127func (r *Repository) Stop(vpsName string) error {
128	requestBody := actionWrapper{Action: "stop"}
129	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s", vpsName), Body: &requestBody}
130
131	return r.Client.Patch(restRequest)
132}
133
134// Reset allows you to reset a VPS, a reset is essentially the stop and start command combined into one
135func (r *Repository) Reset(vpsName string) error {
136	requestBody := actionWrapper{Action: "reset"}
137	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s", vpsName), Body: &requestBody}
138
139	return r.Client.Patch(restRequest)
140}
141
142// Handover will handover a VPS to another TransIP Account. This call will initiate the handover process.
143// The actual handover will be done when the target customer accepts the handover
144func (r *Repository) Handover(vpsName string, targetCustomerName string) error {
145	requestBody := handoverRequest{Action: "handover", TargetCustomerName: targetCustomerName}
146	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s", vpsName), Body: &requestBody}
147
148	return r.Client.Patch(restRequest)
149}
150
151// Cancel will cancel the VPS, thus deleting it
152func (r *Repository) Cancel(vpsName string, endTime gotransip.CancellationTime) error {
153	requestBody := gotransip.CancellationRequest{EndTime: endTime}
154	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s", vpsName), Body: &requestBody}
155
156	return r.Client.Delete(restRequest)
157}
158
159// GetUsage will allow you to request your vps usage for a specified period and usage type,
160// for convenience you can also use the GetUsages or GetUsagesLast24Hours
161func (r *Repository) GetUsage(vpsName string, usageTypes []UsageType, period UsagePeriod) (Usage, error) {
162	var response usageWrapper
163	var types []string
164	for _, usageType := range usageTypes {
165		types = append(types, string(usageType))
166	}
167
168	parameters := url.Values{
169		"dateTimeStart": []string{fmt.Sprintf("%d", period.TimeStart)},
170		"dateTimeEnd":   []string{fmt.Sprintf("%d", period.TimeEnd)},
171		"types":         []string{strings.Join(types, ",")},
172	}
173
174	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/usage", vpsName), Parameters: parameters}
175	err := r.Client.Get(restRequest, &response)
176
177	return response.Usage, err
178}
179
180// GetAllUsage returns a Usage struct filled with all usage data for the given UsagePeriod.
181// UsagePeriod is struct containing a start and end unix timestamp
182func (r *Repository) GetAllUsage(vpsName string, period UsagePeriod) (Usage, error) {
183	return r.GetUsage(
184		vpsName,
185		[]UsageType{UsageTypeCPU, UsageTypeDisk, UsageTypeNetwork},
186		period,
187	)
188}
189
190// GetAllUsage24Hours returns all usage data for a given Vps within the last 24 hours
191func (r *Repository) GetAllUsage24Hours(vpsName string) (Usage, error) {
192	// always define a period body, this way we don't have to depend on the empty body logic on the api server
193	period := UsagePeriod{TimeStart: time.Now().Add(-24 * time.Hour).Unix(), TimeEnd: time.Now().Unix()}
194
195	return r.GetAllUsage(vpsName, period)
196}
197
198// GetVNCData will return VncData about your vps.
199// It allows you to get the location, token and password in order to connect directly to the VNC console of your VPS.
200func (r *Repository) GetVNCData(vpsName string) (VncData, error) {
201	var response vncDataWrapper
202	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/vnc-data", vpsName)}
203	err := r.Client.Get(restRequest, &response)
204
205	return response.VncData, err
206}
207
208// RegenerateVNCToken allows you to regenerate the VNC credentials for a VPS
209func (r *Repository) RegenerateVNCToken(vpsName string) error {
210	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/vnc-data", vpsName)}
211
212	return r.Client.Patch(restRequest)
213}
214
215// GetAddons returns a struct with 'cancellable', 'available' and 'active' addons in it for the given VPS
216func (r *Repository) GetAddons(vpsName string) (Addons, error) {
217	var response addonsWrapper
218	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/addons", vpsName)}
219	err := r.Client.Get(restRequest, &response)
220
221	return response.Addons, err
222}
223
224// OrderAddons allows you to expand VPS specs with a given list of addons to order
225func (r *Repository) OrderAddons(vpsName string, addons []string) error {
226	response := addonOrderRequest{Addons: addons}
227	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/addons", vpsName), Body: &response}
228
229	return r.Client.Post(restRequest)
230}
231
232// CancelAddon allows you to cancel an add-on by name, specifying the VPS name as well.
233// Due to technical restrictions (possible dataloss) storage add-ons cannot be cancelled.
234func (r *Repository) CancelAddon(vpsName string, addon string) error {
235	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/addons/%s", vpsName, addon)}
236
237	return r.Client.Delete(restRequest)
238}
239
240// GetUpgrades returns all available product upgrades for a VPS
241func (r *Repository) GetUpgrades(vpsName string) ([]product.Product, error) {
242	var response upgradesWrapper
243	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/upgrades", vpsName)}
244	err := r.Client.Get(restRequest, &response)
245
246	return response.Upgrades, err
247}
248
249// Upgrade allows you to upgrade a VPS by name and productName
250func (r *Repository) Upgrade(vpsName string, productName string) error {
251	requestBody := upgradeRequest{ProductName: productName}
252	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/upgrades", vpsName), Body: &requestBody}
253
254	return r.Client.Post(restRequest)
255}
256
257// GetOperatingSystems returns a list of operating systems that you can install on a vps
258func (r *Repository) GetOperatingSystems(vpsName string) ([]OperatingSystem, error) {
259	var response operatingSystemsWrapper
260	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/operating-systems", vpsName)}
261	err := r.Client.Get(restRequest, &response)
262
263	return response.OperatingSystems, err
264}
265
266// InstallOperatingSystem allows you to install an operating system to a Vps,
267// optionally you can specify a hostname and a base64InstallText,
268// which would be the automatic installation configuration of your Vps
269// for more information, see: https://api.transip.nl/rest/docs.html#vps-operatingsystems-post
270func (r *Repository) InstallOperatingSystem(vpsName string, operatingSystemName string, hostname string, base64InstallText string) error {
271	requestBody := installRequest{OperatingSystemName: operatingSystemName, Hostname: hostname, Base64InstallText: base64InstallText}
272	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/operating-systems", vpsName), Body: &requestBody}
273
274	return r.Client.Post(restRequest)
275}
276
277// InstallOperatingSystemWithOptions allows you to install an operating system to a Vps,
278// in the options you can specify hostname, username, ssh keys, and base64InstallText,
279// which would be the automatic installation configuration of your Vps
280// for more information, see: https://api.transip.nl/rest/docs.html#vps-operatingsystems-post
281func (r *Repository) InstallOperatingSystemWithOptions(vpsName string, options InstallOptions) error {
282	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/operating-systems", vpsName), Body: &options}
283
284	return r.Client.Post(restRequest)
285}
286
287// GetIPAddresses returns all IPv4 and IPv6 addresses attached to the VPS
288func (r *Repository) GetIPAddresses(vpsName string) ([]ipaddress.IPAddress, error) {
289	var response ipaddress.IPAddressesWrapper
290	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/ip-addresses", vpsName)}
291	err := r.Client.Get(restRequest, &response)
292
293	return response.IPAddresses, err
294}
295
296// GetIPAddressByAddress returns network information for the specified IP address
297func (r *Repository) GetIPAddressByAddress(vpsName string, address net.IP) (ipaddress.IPAddress, error) {
298	var response ipAddressWrapper
299	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/ip-addresses/%s", vpsName, address.String())}
300	err := r.Client.Get(restRequest, &response)
301
302	return response.IPAddress, err
303}
304
305// AddIPv6Address allows you to add an IPv6 address to your VPS.
306// After adding an IPv6 address, you can set the reverse DNS for this address using the UpdateReverseDNS function.
307func (r *Repository) AddIPv6Address(vpsName string, address net.IP) error {
308	requestBody := addIPRequest{IPAddress: address}
309	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/ip-addresses", vpsName), Body: &requestBody}
310
311	return r.Client.Post(restRequest)
312}
313
314// UpdateReverseDNS allows you to update the reverse dns for IPv4 addresses as wal as IPv6 addresses
315func (r *Repository) UpdateReverseDNS(vpsName string, ip ipaddress.IPAddress) error {
316	requestBody := ipAddressWrapper{IPAddress: ip}
317	restRequest := rest.Request{
318		Endpoint: fmt.Sprintf("/vps/%s/ip-addresses/%s", vpsName, ip.Address.String()),
319		Body:     &requestBody,
320	}
321
322	return r.Client.Put(restRequest)
323}
324
325// RemoveIPv6Address allows you to remove an IPv6 address from the registered list of IPv6 address within your VPS's `/64` range.
326func (r *Repository) RemoveIPv6Address(vpsName string, address net.IP) error {
327	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/ip-addresses/%s", vpsName, address.String())}
328
329	return r.Client.Delete(restRequest)
330}
331
332// GetSnapshots returns a list of Snapshots for a given VPS
333func (r *Repository) GetSnapshots(vpsName string) ([]Snapshot, error) {
334	var response snapshotsWrapper
335	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/snapshots", vpsName)}
336	err := r.Client.Get(restRequest, &response)
337
338	return response.Snapshots, err
339}
340
341// GetSnapshotByName returns a Snapshot for a VPS given its snapshotName and vpsName
342func (r *Repository) GetSnapshotByName(vpsName string, snapshotName string) (Snapshot, error) {
343	var response snapshotWrapper
344	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/snapshots/%s", vpsName, snapshotName)}
345	err := r.Client.Get(restRequest, &response)
346
347	return response.Snapshot, err
348}
349
350// CreateSnapshot allows you to create a snapshot for restoring it at a later time or restoring it to another VPS.
351// See the function RevertSnapshot for this.
352func (r *Repository) CreateSnapshot(vpsName string, description string, shouldStartVps bool) error {
353	requestBody := createSnapshotRequest{Description: description, ShouldStartVps: shouldStartVps}
354	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/snapshots", vpsName), Body: &requestBody}
355
356	return r.Client.Post(restRequest)
357}
358
359// RevertSnapshot allows you to revert a snapshot of a vps,
360// if you want to revert a snapshot to a different vps you can use the RevertSnapshotToOtherVps method
361func (r *Repository) RevertSnapshot(vpsName string, snapshotName string) error {
362	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/snapshots/%s", vpsName, snapshotName)}
363
364	return r.Client.Patch(restRequest)
365}
366
367// RevertSnapshotToOtherVps allows you to revert a snapshot to a different vps
368func (r *Repository) RevertSnapshotToOtherVps(vpsName string, snapshotName string, destinationVps string) error {
369	requestBody := revertSnapshotRequest{DestinationVpsName: destinationVps}
370	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/snapshots/%s", vpsName, snapshotName), Body: &requestBody}
371
372	return r.Client.Patch(restRequest)
373}
374
375// RemoveSnapshot allows you to remove a snapshot from a given VPS
376func (r *Repository) RemoveSnapshot(vpsName string, snapshotName string) error {
377	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/snapshots/%s", vpsName, snapshotName)}
378
379	return r.Client.Delete(restRequest)
380}
381
382// GetBackups allows you to get a list of backups for a given VPS which you can use to revert or convert to snapshot
383func (r *Repository) GetBackups(vpsName string) ([]Backup, error) {
384	var response backupsWrapper
385	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/backups", vpsName)}
386	err := r.Client.Get(restRequest, &response)
387
388	return response.Backups, err
389}
390
391// RevertBackup allows you to revert a backup
392func (r *Repository) RevertBackup(vpsName string, backupID int64) error {
393	requestBody := actionWrapper{Action: "revert"}
394	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/backups/%d", vpsName, backupID), Body: &requestBody}
395
396	return r.Client.Patch(restRequest)
397}
398
399// ConvertBackupToSnapshot allows you to convert a backup to a snapshot
400func (r *Repository) ConvertBackupToSnapshot(vpsName string, backupID int64, snapshotDescription string) error {
401	requestBody := convertBackupRequest{SnapshotDescription: snapshotDescription, Action: "convert"}
402	restRequest := rest.Request{Endpoint: fmt.Sprintf("/vps/%s/backups/%d", vpsName, backupID), Body: &requestBody}
403
404	return r.Client.Patch(restRequest)
405}
406