Repository navigation
Releases: pidginhost/phctl
Release list
phctl v0.20.1
Fixed
kubernetes node deleteno longer fails on every call. Give the node's name with--confirm-hostnameand add--accept-local-data-loss.kubernetes pool deletecan remove a pool that still has nodes. Add--accept-local-data-loss.
phctl v0.20.0
Fixed
domain checkreports whether the domain is available, taken or not supported, instead of failing on every call.storage bucket listandstorage bucket getno longer fail for large buckets.freedns record listno longer fails on zones with records that have no address, such as TXT records.
Added
- Server, volume, bucket and private network listings and details include pricing and billing cycles in JSON output when available.
- Dedicated server listings and details include billing information and hardware in JSON output when available.
billing invoice get -o jsonincludes the invoice type and, where it applies, its amounts in your local currency.
phctl v0.19.0
Fixed
- Commands that read SDK response fields now reject empty or null responses with an error instead of panicking or returning successful null output, including paginated lists.
billing invoice getpreserves service details and the other invoice fields from the API. Decimal amounts retain their exact text in tables and remain strings in JSON.- IP and volume detach commands and private-network add/remove-server commands fail when the API reports that the operation did not happen.
- FreeDNS record commands require
--source, matching the API, instead of accepting its omission and failing later in the SDK. dedicated server listanddedicated server getwork at all. The API sendsipsas a list of address objects andserver_statusas an object, but the commands decoded both as strings, so every call failed. They now use the generated model: the table shows the addresses comma-separated and the provider's status text, and-o jsonkeeps the full address objects.compute server snapshot listandcompute server boot-isoswork at all. Both routes answer a plain JSON array, and the commands expected a paginated envelope, so every call failed to decode.compute server snapshot createno longer reports an error after queuing the snapshot. The answer was decoded as a snapshot list, which it is not. The command now checks that the API queued the snapshot and fails if it did not; under-o jsonit prints the API's answer.compute volume attachno longer reports an error after attaching. The answer was decoded as the request body. The command now checks theattachedflag it reports, fails if it is false, and prints the answer under-o json.kubernetes node deleteno longer claims the node is gone. The API starts a safe removal (cordon, drain, detach) and answers with the operation it queued. The command now reports the operation ID and status, and prints the operation under-o json.
Changed
- Requires
sdk-gov0.14.0. Request bodies have their own models without the fields the server assigns, so create and update commands no longer send placeholder values such as"id": 0or empty strings for read-only fields. This covers firewall rule sets and rules, private networks, SSH keys, public interface settings, API tokens, load balancer firewall rules, port forwards and Gateway routes. compute network create --slugis deprecated. The server assigns the slug and never applied the value passed. The flag is still accepted, prints a deprecation warning, and is no longer required.account api-token listandcreateno longer patch the API's answer. Personal tokens omitaccountandmembership_statuswhile multi-user IAM is off, and the SDK now treats them as optional. Under-o jsonthe two fields are left out when the API does not send them, instead of being printed as null.- A Kubernetes node without an address shows
<none>inkubernetes pool get,node getandnode list.
phctl v0.18.0
Added
-
phctl kubernetes lb-firewall:list,get,create,update,deletefor the firewall in front of a cluster's load balancer.updateis a PATCH that sends only the flags you actually pass, so it cannot blank a field you did not mention, and it refuses an empty change rather than making a round trip the API would answer200to. Each field it did send is read back out of the response, so a rule the API reports unchanged fails instead of being announced as updated.--directionand--actionare validated against the schema's enums before the request, because a typo would otherwise reach a live load balancer as a rejected write. -
phctl kubernetes cluster update: rename a cluster, set delete protection, or replace its feature set. Changing features confirms first — features the new set omits are uninstalled and their workloads restart. Every setting is read back out of the response: the route answers200regardless of whether it applied anything, so a rename or a protection change that did not take now fails instead of reporting success.Removing every feature is rejected up front rather than attempted.
PatchedClusterDetail.featuresis generated withomitempty, so an empty list is dropped from the request body and the server would keep the current features while answering200. A repeated feature is rejected too: the server stores a set, so the response would come back one element shorter than the request and read as an unapplied change. -
phctl kubernetes cluster upgrade-feature: upgrade a cluster feature (cert-manager and friends) to the latest version compatible with the cluster's Kubernetes release, with--retryfor a failed install. It confirms first because the feature's workloads restart, and it checks the reported status rather than the HTTP code — the route answers200with a non-OKstatus when it declines the work. -
phctl kubernetes cluster toggle-vm-accessandcluster eligible-vms: turn cloud VM access to the cluster's private network on or off, and list the VMs that could then be connected. The toggle flips the current setting rather than setting it, so it confirms first and reports the state the cluster ended up in; running it twice returns to where you started. -
phctl kubernetes cluster kubeconfig --regenerate: issue a fresh kubeconfig. It confirms first, because every copy of the previous one stops working. The command also now rejects an empty or whitespace-only response instead of writing out a file that looks like a kubeconfig but configures nothing — including through--merge. -
phctl kubernetes pool getandpool resize: pool details with its nodes, and a node-count change.resizeconfirms because growing a pool provisions billable VMs and shrinking one destroys them, and it requires--size— the API treats a body withoutnew_sizeas a no-op and still answers200. The work is asynchronous, so the command reports the size the pool still has rather than claiming the new one. -
phctl kubernetes node getandnode rrd: a single node's details, and its recorded metric series.rrdrenders every field the API sends, disk I/O included, and prints the numbers as digits — JSON numbers decode tofloat64, which%vwould have shown as1.7555e+09for a Unix timestamp. -
phctl kubernetes {http,tcp,udp}-route getandupdate: route details, and a partial update.updateis a PATCH that sends only the flags you pass, so an omitted field keeps its current value, and it reads each one back out of the response. It deliberately builds the body from the zero value rather thanNewPatched*Route(): that constructor seeds the schema defaults forbackend_namespace,path_prefixandenable_tls, which a PATCH would then send as though the caller had asked for them. -
--backend-namespaceon{http,tcp,udp}-route createandupdate: the backend service's namespace could not be set from the CLI, so a backend outsidedefaultwas unreachable. -
phctl kubernetes port-forward:list,get,create,update,delete. A port forward publishes an address inside the cluster on the load balancer's public IP;phctl kubernetes lb-firewallgoverns what may reach it.updateis a PATCH that sends only what you pass and confirms the change landed,--protocolis validated against the schema enum before the request, anddeleteconfirms because traffic reaching the service through it stops. -
phctl kubernetes node metrics: current CPU, memory, disk and network counters for a node. -
phctl kubernetes node rrd --timeframe: select the window (hour,day,week,month,year). An unknown value is rejected rather than passed on, because the API silently falls back tohourinstead of erroring.
Fixed
{http,tcp,udp}-route listworks at all. These endpoints paginate, but the commands decoded a bare JSON array, so every call failed against the real API. The schema did not describe the response, and the commands had only structural tests, so nothing caught it. They now page throughcmdutil.FetchAllagainst the generatedPaginated*Listmodels.{http,tcp,udp}-route createno longer panics on an empty response body. A201with no body decodes to a nil route, which the success message then dereferenced.
Changed
-
Requires
sdk-gov0.13.0. Every gap this section previously listed was an API schema defect, now fixed inphclientand regenerated:- Port forwards are reachable. All six routes had no request body and no response schema, so the SDK generated
POST/PUTmethods that send nothing and no model to decode into.ClusterBackendMixin.clusterreadself.kwargs["cluster_id"], which drf-spectacular cannot supply, and theKeyErrormade it abandon the whole viewset. The three Gateway route viewsets lost theirGETschemas the same way. node metricscan be decoded.NodeMetricsResponsedeclared its byte counters as bareIntegerField, which generates asint32; a node with 2 GiB of RAM reports one past that. They areint64now, anddisk/maxdiskare documented — the endpoint always returned them.ClusterDetailcarries the types it sends.dual_stackandtalos_upgrade_availableare booleans,storage_quota_gbandlast_pool_used_bytesintegers,price_per_houra number. All five were declared as strings, which made the whole cluster object undecodable — socluster updatecould not have worked against a real cluster.- The RRD
timeframeparameter is declared, so a window other than the default is reachable.
The regeneration also removed the
...Retrieve2operations, which existed only because the list and detail reads collided on one operation id while the schema was incomplete. The reads are now...Listand...Retrieve. - Port forwards are reachable. All six routes had no request body and no response schema, so the SDK generated
phctl v0.17.0
Added
-
compute serveroperations:usage,activity,boot-isos,rescue enter/rescue exit,retry-provision, andpublic-interface get/set/delete.rescue enterandrescue exitreboot the server, so they confirm first;public-interface deletecosts the server its public connectivity, so it does too.rescue enter,rescue exitandretry-provisionanswer200with{"queued": false}or{"retry": false}when they decline the work, so each checks the flag rather than the status code.boot-isosis scoped to a server rather than listed globally because compatibility depends on that server's disk and RAM, and it pages throughcmdutil.FetchAll; itsmin_ramis a decimal the API sends as a string, rendered as one.public-interface setis a POST that the SDK callsCreate, but it updates firewall settings on an interface that already exists — the addresses and interface name are read-only server-side. Only--firewall,--policy-inand--policy-outare writable, an unknown policy is rejected before the request rather than silently ignored, and calling it with no flags is an error rather than a no-op round trip. -
compute ipv6 reverse-dns: the IPv4 equivalent already existed; IPv6 PTR records could only be read or set from the panel. -
phctl storage bucket: object storage had no CLI surface at all.list,get,create,delete,resize,visibility, andcredentials reveal/credentials rotate. It is a top-level group rather thancompute bucketbecausecompute volumeis block storage attached to a server and the two are easy to confuse.createandresizeconfirm first because they change what the account is billed; creating a public bucket also warns that uploaded objects will be internet-readable.deleteconfirms because it takes the objects with it;visibility --publicconfirms because it exposes every object to the internet, while--privateonly removes access and does not.resizeandvisibilitycheck the bucket actually came back changed — the routes answer200with the current state, so trusting the status code alone would report a resize that never happened. Asking for the value a bucket already has is a no-op that succeeds, so a script can converge on a desired state without reading it first.credentials revealprints an access key and secret, so it asks before doing so unless-fis passed. Both credential commands wrap failures with a redacting error wrapper that also removes SDK errors from the unwrap chain and distrusts response reason phrases. Those routes answer200with the secret in the body, and a body that fails to decode would otherwise put live credentials into an error string — and, forrotate, the new secret, after the old pair is already dead. A failed rotation tells the operator to reveal the current pair instead of retrying the non-idempotent operation.storage product listis deliberately absent:StorageProduct.priceis a decimal, and pricing endpoints still go throughinternal/client'sRaw*types. -
compute server detach-ipv6:attach-ipv6has existed for a while anddetach-ipv4was already there, but an IPv6 could only be attached, never removed, from the CLI. -
compute server resize --package <id|slug>: change a server's package. Downgrades are only accepted between packages with the same disk size, and the server restarts, so it confirms first unless-fis passed.
Fixed
billing deposit createno longer mangles the amount it reports. The API sends decimals as JSON strings, andsdk-gomapped them tofloat64up to v0.11.0 — so the command failed to decode the response after the deposit was created. On v0.12.2 the field is a string, and the%.2fit was rendered with printed%!f(string=12): the.2precision silently truncated125.00to two characters. It renders as sent now.- API errors now keep the response body across every command. 85 call sites in
account,billing,dedicated,domain,freedns,hosting,kubernetesandsupportwrapped the SDK error withfmt.Errorf, which discards the body — the only part that says what was actually wrong. A rejected ticket used to reportcreating ticket: 400 Bad Request; it now reportscreating ticket: 400 Bad Request: department=This field is required.computehad already been converted; the rest had not. A contract test incmd/apierror_contract_test.gokeeps it that way. -f/--forceis now resolved the same way-o/--outputis. It was read throughFlags().GetBool, which only sees a root's persistent flags after Cobra merges them duringExecute, so it returnedfalsefor any command driven directly throughRunE.
phctl v0.16.0
Added
compute server create --public-ip: attach an IPv4 you already own (by ID or address) at create time, instead of only being able to allocate a new one. Mutually exclusive with--new-ipv4.compute server create --new-ipv6/--public-ipv6: the IPv6 counterparts — allocate a new address, or attach one you already own. Mutually exclusive with each other.compute server attach-ipv4 --rebootandattach-ipv6 --reboot: restart the server as part of the attach so the guest OS picks the address up.
Changed
- Requires
sdk-gov0.11.0. The API split the server attach routes into distinct request and response components, soAttachIPv4/AttachIPv6becameAttachIPv4Request/AttachIPv6Request, and six enum components were renamed off content-derived names (for exampleStatusA57Enum→ResourceStatusEnum). support ticket getrenders the message thread. The API used to return the conversation as one preformatted blob; it is now a list, so each message is printed with its author, date, and attachment name.
Fixed
-
compute server attach-ipv4/attach-ipv6no longer report an unreachable server as done. A public address is written to the machine config and the guest OS only reads it while booting, so on a running server the address was inert:IPv4 x attached to server N.described a machine that answered neither ping nor SSH on it. The command now reports whether a restart is still owed and points at--rebootorcompute server power N --action reboot, and says so when the restart was already issued. A stopped server is never rebooted just to pick the address up. -
compute server attach-ipv6checks the result. It discarded the response entirely and always claimed success; it now fails when the backend reports the address was not attached, matchingattach-ipv4. -
-o json/-o yamlnow work on commands that return a resource.compute server createprintedServer created (ID: 123)throughcmd.Printfand ignored the global--outputflag entirely, so-o jsonsilently produced prose; roughly 35 other commands across compute, kubernetes, domain, billing, account, support, freedns, hosting and dedicated had the same bug. All of them now render throughinternal/output.The rule is now explicit: anything reporting a resource returned by the API goes through
output.Print/output.Result; side-effect acknowledgements (delete,power, snapshot queueing), interactive prompts, and progress narration keep plain text. An AST guard incmd/fails the build if a command prints API response fields withcmd.Print*again. -
auth statushonours--outputand never emits the raw API token — machine-readable output carries the same masked form the table view shows. -
cmdutil.OutputFormatno longer panics with a nil dereference when a command is exercised outside the root command tree (asRunE-level unit tests do). -
API error messages are stable between runs. Multi-field validation errors were flattened by ranging a Go map, so the same
400rendered its fields in a different order each time; they are now sorted. This covers the newerattach-ipv4/attach-ipv6400bodies, e.g.ipv4=This field is required.
phctl v0.15.0
Added
phctl domain glue(aliaspersonal-dns): manage glue / personal-DNS records — child nameserver hosts registered at the registry.list <domain>,set <domain> --name ns1 --ip <ip> [--ip2 <ip>], anddelete <domain> --name ns1. Register a nameserver host (e.g.ns1.example.ro) before delegating another domain's nameservers to it. Requires the corresponding/api/domain/domain/{domain}/dns/endpoint on the backend.
phctl v0.14.3
Fixed
domain listanddomain getno longer crash withjson: cannot unmarshal bool into Go struct field RawDomain.idna of type string. The API'sidnafield is a boolean flag, not a string; the struct now decodes it asbooland exposes the punycode FQDN via the separateidna_namefield (shown as "IDNA Name:" indomain get).
phctl v0.14.2
Fixed
compute server getFloating IPs table: header columnIDwas indented two spaces while data rows were flush-left, throwing tabwriter alignment off for the entire section. Header now matches the row layout.compute server create --no-public-ipv4-ackhelp text: previously described the flag as "no public network" gated on four specific flags; reworded to make clear it acknowledges no public IPv4 or IPv6, matching the actual backendno_network_acknowledgedsemantics.
Changed
- Internal refactor: extracted the inline table-rendering closures in
compute floating-ip list,compute package list, andcompute server getinto named functions (printFloatingIPv{4,6}ListTable,printPackageListTable,printServerDetailsTable) so they can be exercised directly from tests. No behavior change. - Test coverage: added regression tests covering package
GENERATIONScolumn rendering, floating-IPREVERSE_DNScolumn rendering,compute server getFloating IPs section, andcompute server create --no-public-ipv4-acksendingno_network_acknowledged=truewithout allocatingnew_ipv4.
phctl v0.14.1
Added
compute server create --no-public-ipv4-ack: explicitly acknowledge the VM will have no public network, satisfying the backendno_network_acknowledgedguard when none of--new-ipv4,--public-ip,--new-ipv6,--public-ipv6is set on packages that would otherwise reject the create.