Skip to content

Commit 8ac1c0d

Browse files
rgarciacursoragent
andcommitted
Add complete API coverage for all Leaseweb resources
Extend existing resource commands (dedicated-servers, instances, load-balancers, domains, ips, invoices, private-networks) with all missing operations, and add 18 new resource commands: abuse-reports, acronis-backup, aggregation-packs, api-keys, cdn, colocations, datacenter-access, dedicated-racks, dedicated-network-equipment, emails, floating-ips, private-clouds, remote-management, storage, traffic-policy, virtual-servers, vps, and webhosting. Also adds PatchJSON and DeleteWithBody methods to the HTTP client. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 10467a3 commit 8ac1c0d

28 files changed

Lines changed: 7476 additions & 23 deletions

README.md

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# lw — CLI for the Leaseweb API
22

3-
A command-line interface for managing [Leaseweb](https://www.leaseweb.com/) infrastructure: dedicated servers, public cloud instances, IP addresses, invoices, DNS, and more.
3+
A command-line interface for managing [Leaseweb](https://www.leaseweb.com/) infrastructure: dedicated servers, public cloud instances, VPS, virtual servers, private clouds, CDN, DNS, email, and more.
44

55
## Installation
66

@@ -80,23 +80,44 @@ NAME:
8080
USAGE:
8181
lw [global options] [command [command options]]
8282

83+
VERSION:
84+
dev
85+
8386
COMMANDS:
84-
config Manage CLI configuration
85-
dedicated-servers, ds Manage dedicated servers
86-
domains Manage hosting domains
87-
instances, i Manage public cloud instances
88-
invoices Manage invoices
89-
ips Manage IP addresses
90-
load-balancers, lb Manage public cloud load balancers
91-
private-networks, pn Manage private networks
92-
services Manage services
87+
abuse-reports, abuse Manage abuse reports
88+
acronis-backup, backup Manage Acronis backup
89+
aggregation-packs, ap Manage aggregation packs
90+
api-keys, keys Manage API keys
91+
cdn, c Manage CDN resources
92+
colocations, colo Manage colocations
93+
config Manage CLI configuration
94+
datacenter-access, dca Manage datacenter access requests
95+
dedicated-racks, dr Manage dedicated racks
96+
dedicated-servers, ds Manage dedicated servers
97+
domains Manage hosting domains
98+
emails, email Manage email services
99+
floating-ips, fip Manage floating IPs
100+
instances, i Manage public cloud instances
101+
invoices Manage invoices
102+
ips Manage IP addresses
103+
load-balancers, lb Manage public cloud load balancers
104+
network-equipment, ne Manage dedicated network equipment
105+
private-clouds, pc Manage private clouds
106+
private-networks, pn Manage private networks
107+
remote-management, rm Manage OpenVPN remote management
108+
services Manage services
109+
storage Manage storage
110+
traffic-policy, tp Manage traffic policies
111+
virtual-servers, vs Manage virtual servers
112+
vps, v Manage VPS instances
113+
webhosting, wh Manage webhosting packages
93114

94115
GLOBAL OPTIONS:
95116
--profile string, -p string Config profile to use
96117
--api-key string Leaseweb API key (overrides profile)
97118
--base-url string Override the base URL for API requests
98119
--debug Enable debug logging of HTTP requests
99-
--format string Output format: auto, json, pretty, raw, yaml (default: "auto")
120+
--format string Output format (one of: auto, json, pretty, raw, yaml) (default: "auto")
100121
--transform string GJSON expression to transform output
101122
--help, -h show help
102123
--version, -v print the version
@@ -169,17 +190,35 @@ lw ds list --format raw --transform "servers.#.id"
169190

170191
Use `lw <command> --help` for details on any subcommand. Here's a summary:
171192

172-
| Command | Alias | Subcommands |
193+
| Command | Alias | Description |
173194
|---------|-------|-------------|
174-
| `dedicated-servers` | `ds` | list, get, update, power-on/off/cycle, power-status, rescue, install, credentials, jobs, hardware-info, metrics, network-interfaces |
175-
| `instances` | `i` | list, get, launch, terminate, start, stop, reboot, update, console, credentials, ips, snapshots, metrics, regions, types, images |
176-
| `ips` | | list, get, update, null-route, remove-null-route, null-route-history, reverse-lookup |
177-
| `invoices` | | list, get, pdf, proforma |
178-
| `services` | | list, get, update, cancel, uncancel, cancellation-reasons |
179-
| `domains` | | list, get, dns, dns-get, dns-create, dns-delete |
180-
| `load-balancers` | `lb` | list, get, create, update, delete, listeners |
181-
| `private-networks` | `pn` | list, get, create, update, delete, servers |
195+
| `abuse-reports` | `abuse` | List, get, resolve abuse reports, manage messages and attachments |
196+
| `acronis-backup` | `backup` | List backup items, get details, view metrics |
197+
| `aggregation-packs` | `ap` | List and get aggregation packs |
198+
| `api-keys` | `keys` | CRUD API keys, validate keys, list capabilities |
199+
| `cdn` | `c` | Distributions, origins, cache, SSL, WAF, geo-restrictions, metrics |
200+
| `colocations` | `colo` | CRUD colocations, credentials, IPs, metrics, notifications |
182201
| `config` | | init, show |
202+
| `datacenter-access` | `dca` | Access requests, datacenters, contacts, visitors |
203+
| `dedicated-racks` | `dr` | CRUD racks, credentials, IPs, metrics, notifications |
204+
| `dedicated-servers` | `ds` | Full server lifecycle, credentials, IPs, jobs, metrics, DHCP, notifications |
205+
| `domains` | | DNS records, DNSSEC, nameservers, contacts, locks, zone import/export |
206+
| `emails` | `email` | Domains, mailboxes, forwards, aliases, spam filter, auto-reply |
207+
| `floating-ips` | `fip` | CRUD floating IP ranges, definitions, assign/unassign |
208+
| `instances` | `i` | Full instance lifecycle, credentials, IPs, snapshots, ISOs, security groups |
209+
| `invoices` | | List, get, PDF download, proforma, CSV export |
210+
| `ips` | | List, get, update, null route, reverse lookup (IPv4 + IPv6) |
211+
| `load-balancers` | `lb` | CRUD, listeners, IPs, metrics, monitoring |
212+
| `network-equipment` | `ne` | CRUD equipment, credentials, IPs, power, null routes |
213+
| `private-clouds` | `pc` | CRUD private clouds, credentials, metrics |
214+
| `private-networks` | `pn` | CRUD networks, servers, DHCP reservations |
215+
| `remote-management` | `rm` | OpenVPN profiles, credentials |
216+
| `services` | | List, get, update, cancel/uncancel |
217+
| `storage` | | List storage, VMs, volumes, grow volumes |
218+
| `traffic-policy` | `tp` | List, get, update policies, history, reset |
219+
| `virtual-servers` | `vs` | CRUD servers, credentials, metrics, snapshots, templates |
220+
| `vps` | `v` | Full VPS lifecycle, credentials, IPs, snapshots, monitoring, notifications |
221+
| `webhosting` | `wh` | Packages, usernames, domain aliases, catch-all |
183222

184223
## Development
185224

pkg/cmd/abuse_reports.go

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"os"
8+
9+
"github.com/urfave/cli/v3"
10+
)
11+
12+
var abuseReportsCmd = cli.Command{
13+
Name: "abuse-reports",
14+
Aliases: []string{"abuse"},
15+
Usage: "Manage abuse reports",
16+
Commands: []*cli.Command{
17+
&abuseListCmd, &abuseGetCmd, &abuseResolveCmd,
18+
&abuseMessagesCmd, &abuseMessageCreateCmd,
19+
&abuseAttachmentsCmd, &abuseAttachmentGetCmd, &abuseResolutionOptionsCmd,
20+
},
21+
HideHelpCommand: true,
22+
}
23+
24+
func abusePath(id string) string { return "/abuse/v1/reports/" + id }
25+
26+
var abuseListCmd = cli.Command{Name: "list", Usage: "List abuse reports", Flags: append(PaginationFlags, &cli.StringFlag{Name: "status"}, &cli.StringFlag{Name: "sort-by"}), Action: func(ctx context.Context, cmd *cli.Command) error {
27+
client, err := NewClient(cmd)
28+
if err != nil {
29+
return err
30+
}
31+
q := PaginationQuery(cmd)
32+
if s := cmd.String("status"); s != "" {
33+
q += "&status=" + s
34+
}
35+
if s := cmd.String("sort-by"); s != "" {
36+
q += "&sortBy=" + s
37+
}
38+
res, err := client.Get(ctx, "/abuse/v1/reports?"+q)
39+
if err != nil {
40+
return err
41+
}
42+
return ShowResult(os.Stdout, res, cmd.Root().String("format"), cmd.Root().String("transform"))
43+
}, HideHelpCommand: true}
44+
45+
var abuseGetCmd = cli.Command{Name: "get", ArgsUsage: "<id>", Action: func(ctx context.Context, cmd *cli.Command) error {
46+
args := cmd.Args().Slice()
47+
if len(args) < 1 {
48+
return fmt.Errorf("report ID required")
49+
}
50+
client, err := NewClient(cmd)
51+
if err != nil {
52+
return err
53+
}
54+
res, err := client.Get(ctx, abusePath(args[0]))
55+
if err != nil {
56+
return err
57+
}
58+
return ShowResult(os.Stdout, res, cmd.Root().String("format"), cmd.Root().String("transform"))
59+
}, HideHelpCommand: true}
60+
61+
var abuseResolveCmd = cli.Command{Name: "resolve", Usage: "Resolve abuse report", ArgsUsage: "<id>", Flags: []cli.Flag{&cli.StringSliceFlag{Name: "resolution", Required: true}}, Action: func(ctx context.Context, cmd *cli.Command) error {
62+
args := cmd.Args().Slice()
63+
if len(args) < 1 {
64+
return fmt.Errorf("report ID required")
65+
}
66+
client, err := NewClient(cmd)
67+
if err != nil {
68+
return err
69+
}
70+
body, _ := json.Marshal(map[string][]string{"resolutions": cmd.StringSlice("resolution")})
71+
_, err = client.PostJSON(ctx, abusePath(args[0])+"/resolve", body)
72+
if err != nil {
73+
return err
74+
}
75+
fmt.Fprintf(os.Stderr, "Resolved report %s\n", args[0])
76+
return nil
77+
}, HideHelpCommand: true}
78+
79+
var abuseMessagesCmd = cli.Command{Name: "messages", ArgsUsage: "<id>", Action: func(ctx context.Context, cmd *cli.Command) error {
80+
args := cmd.Args().Slice()
81+
if len(args) < 1 {
82+
return fmt.Errorf("report ID required")
83+
}
84+
client, err := NewClient(cmd)
85+
if err != nil {
86+
return err
87+
}
88+
res, err := client.Get(ctx, abusePath(args[0])+"/messages")
89+
if err != nil {
90+
return err
91+
}
92+
return ShowResult(os.Stdout, res, cmd.Root().String("format"), cmd.Root().String("transform"))
93+
}, HideHelpCommand: true}
94+
95+
var abuseMessageCreateCmd = cli.Command{Name: "message-create", ArgsUsage: "<id>", Flags: []cli.Flag{&cli.StringFlag{Name: "body", Required: true}}, Action: func(ctx context.Context, cmd *cli.Command) error {
96+
args := cmd.Args().Slice()
97+
if len(args) < 1 {
98+
return fmt.Errorf("report ID required")
99+
}
100+
client, err := NewClient(cmd)
101+
if err != nil {
102+
return err
103+
}
104+
body, _ := json.Marshal(map[string]string{"body": cmd.String("body")})
105+
res, err := client.PostJSON(ctx, abusePath(args[0])+"/messages", body)
106+
if err != nil {
107+
return err
108+
}
109+
return ShowResult(os.Stdout, res, cmd.Root().String("format"), cmd.Root().String("transform"))
110+
}, HideHelpCommand: true}
111+
112+
var abuseAttachmentsCmd = cli.Command{Name: "attachments", ArgsUsage: "<id>", Action: func(ctx context.Context, cmd *cli.Command) error {
113+
args := cmd.Args().Slice()
114+
if len(args) < 1 {
115+
return fmt.Errorf("report ID required")
116+
}
117+
client, err := NewClient(cmd)
118+
if err != nil {
119+
return err
120+
}
121+
res, err := client.Get(ctx, abusePath(args[0])+"/attachments")
122+
if err != nil {
123+
return err
124+
}
125+
return ShowResult(os.Stdout, res, cmd.Root().String("format"), cmd.Root().String("transform"))
126+
}, HideHelpCommand: true}
127+
128+
var abuseAttachmentGetCmd = cli.Command{Name: "attachment-get", ArgsUsage: "<id> <attachment-id>", Action: func(ctx context.Context, cmd *cli.Command) error {
129+
args := cmd.Args().Slice()
130+
if len(args) < 2 {
131+
return fmt.Errorf("report ID and attachment ID required")
132+
}
133+
client, err := NewClient(cmd)
134+
if err != nil {
135+
return err
136+
}
137+
res, err := client.Get(ctx, fmt.Sprintf("%s/attachments/%s", abusePath(args[0]), args[1]))
138+
if err != nil {
139+
return err
140+
}
141+
return ShowResult(os.Stdout, res, cmd.Root().String("format"), cmd.Root().String("transform"))
142+
}, HideHelpCommand: true}
143+
144+
var abuseResolutionOptionsCmd = cli.Command{Name: "resolution-options", ArgsUsage: "<id>", Action: func(ctx context.Context, cmd *cli.Command) error {
145+
args := cmd.Args().Slice()
146+
if len(args) < 1 {
147+
return fmt.Errorf("report ID required")
148+
}
149+
client, err := NewClient(cmd)
150+
if err != nil {
151+
return err
152+
}
153+
res, err := client.Get(ctx, abusePath(args[0])+"/resolutions")
154+
if err != nil {
155+
return err
156+
}
157+
return ShowResult(os.Stdout, res, cmd.Root().String("format"), cmd.Root().String("transform"))
158+
}, HideHelpCommand: true}

pkg/cmd/acronis_backup.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
8+
"github.com/urfave/cli/v3"
9+
)
10+
11+
var acronisBackupCmd = cli.Command{
12+
Name: "acronis-backup",
13+
Aliases: []string{"backup"},
14+
Usage: "Manage Acronis backup",
15+
Commands: []*cli.Command{
16+
&backupListCmd,
17+
&backupGetCmd,
18+
&backupMetricsCmd,
19+
},
20+
HideHelpCommand: true,
21+
}
22+
23+
var backupListCmd = cli.Command{
24+
Name: "list",
25+
Usage: "List backup items",
26+
Action: handleBackupList,
27+
HideHelpCommand: true,
28+
}
29+
30+
func handleBackupList(ctx context.Context, cmd *cli.Command) error {
31+
client, err := NewClient(cmd)
32+
if err != nil {
33+
return err
34+
}
35+
res, err := client.Get(ctx, "/backup/v1/backup")
36+
if err != nil {
37+
return err
38+
}
39+
return ShowResult(os.Stdout, res, cmd.Root().String("format"), cmd.Root().String("transform"))
40+
}
41+
42+
var backupGetCmd = cli.Command{
43+
Name: "get",
44+
Usage: "Inspect a backup item",
45+
ArgsUsage: "<equipment-id>",
46+
Action: handleBackupGet,
47+
HideHelpCommand: true,
48+
}
49+
50+
func handleBackupGet(ctx context.Context, cmd *cli.Command) error {
51+
args := cmd.Args().Slice()
52+
if len(args) < 1 {
53+
return fmt.Errorf("equipment ID required")
54+
}
55+
client, err := NewClient(cmd)
56+
if err != nil {
57+
return err
58+
}
59+
res, err := client.Get(ctx, "/backup/v1/backup/"+args[0])
60+
if err != nil {
61+
return err
62+
}
63+
return ShowResult(os.Stdout, res, cmd.Root().String("format"), cmd.Root().String("transform"))
64+
}
65+
66+
var backupMetricsCmd = cli.Command{
67+
Name: "metrics",
68+
Usage: "Get storage usage metrics",
69+
Action: handleBackupMetrics,
70+
HideHelpCommand: true,
71+
}
72+
73+
func handleBackupMetrics(ctx context.Context, cmd *cli.Command) error {
74+
client, err := NewClient(cmd)
75+
if err != nil {
76+
return err
77+
}
78+
res, err := client.Get(ctx, "/backup/v1/metrics/storage")
79+
if err != nil {
80+
return err
81+
}
82+
return ShowResult(os.Stdout, res, cmd.Root().String("format"), cmd.Root().String("transform"))
83+
}

0 commit comments

Comments
 (0)