Skip to content

Releases: pidginhost/phctl

phctl v0.20.1

Choose a tag to compare

@andreeapid andreeapid released this 17 Oct 19:08

Fixed

  • kubernetes node delete no longer fails on every call. Give the node's name with --confirm-hostname and add --accept-local-data-loss.
  • kubernetes pool delete can remove a pool that still has nodes. Add --accept-local-data-loss.

phctl v0.20.0

Choose a tag to compare

@andreeapid andreeapid released this 17 Oct 08:45

Fixed

  • domain check reports whether the domain is available, taken or not supported, instead of failing on every call.
  • storage bucket list and storage bucket get no longer fail for large buckets.
  • freedns record list no 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 json includes the invoice type and, where it applies, its amounts in your local currency.

phctl v0.19.0

Choose a tag to compare

@andreeapid andreeapid released this 03 Oct 07:29

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 get preserves 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 list and dedicated server get work at all. The API sends ips as a list of address objects and server_status as 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 json keeps the full address objects.
  • compute server snapshot list and compute server boot-isos work 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 create no 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 json it prints the API's answer.
  • compute volume attach no longer reports an error after attaching. The answer was decoded as the request body. The command now checks the attached flag it reports, fails if it is false, and prints the answer under -o json.
  • kubernetes node delete no 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-go v0.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": 0 or 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 --slug is 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 list and create no longer patch the API's answer. Personal tokens omit account and membership_status while multi-user IAM is off, and the SDK now treats them as optional. Under -o json the 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> in kubernetes pool get, node get and node list.

phctl v0.18.0

Choose a tag to compare

@andreeapid andreeapid released this 19 Aug 08:47

Added

  • phctl kubernetes lb-firewall: list, get, create, update, delete for the firewall in front of a cluster's load balancer. update is 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 answer 200 to. 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. --direction and --action are 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 answers 200 regardless 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.features is generated with omitempty, so an empty list is dropped from the request body and the server would keep the current features while answering 200. 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 --retry for 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 answers 200 with a non-OK status when it declines the work.

  • phctl kubernetes cluster toggle-vm-access and cluster 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 get and pool resize: pool details with its nodes, and a node-count change. resize confirms because growing a pool provisions billable VMs and shrinking one destroys them, and it requires --size — the API treats a body without new_size as a no-op and still answers 200. The work is asynchronous, so the command reports the size the pool still has rather than claiming the new one.

  • phctl kubernetes node get and node rrd: a single node's details, and its recorded metric series. rrd renders every field the API sends, disk I/O included, and prints the numbers as digits — JSON numbers decode to float64, which %v would have shown as 1.7555e+09 for a Unix timestamp.

  • phctl kubernetes {http,tcp,udp}-route get and update: route details, and a partial update. update is 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 than NewPatched*Route(): that constructor seeds the schema defaults for backend_namespace, path_prefix and enable_tls, which a PATCH would then send as though the caller had asked for them.

  • --backend-namespace on {http,tcp,udp}-route create and update: the backend service's namespace could not be set from the CLI, so a backend outside default was 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-firewall governs what may reach it. update is a PATCH that sends only what you pass and confirms the change landed, --protocol is validated against the schema enum before the request, and delete confirms 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 to hour instead of erroring.

Fixed

  • {http,tcp,udp}-route list works 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 through cmdutil.FetchAll against the generated Paginated*List models.
  • {http,tcp,udp}-route create no longer panics on an empty response body. A 201 with no body decodes to a nil route, which the success message then dereferenced.

Changed

  • Requires sdk-go v0.13.0. Every gap this section previously listed was an API schema defect, now fixed in phclient and regenerated:

    • Port forwards are reachable. All six routes had no request body and no response schema, so the SDK generated POST/PUT methods that send nothing and no model to decode into. ClusterBackendMixin.cluster read self.kwargs["cluster_id"], which drf-spectacular cannot supply, and the KeyError made it abandon the whole viewset. The three Gateway route viewsets lost their GET schemas the same way.
    • node metrics can be decoded. NodeMetricsResponse declared its byte counters as bare IntegerField, which generates as int32; a node with 2 GiB of RAM reports one past that. They are int64 now, and disk/maxdisk are documented — the endpoint always returned them.
    • ClusterDetail carries the types it sends. dual_stack and talos_upgrade_available are booleans, storage_quota_gb and last_pool_used_bytes integers, price_per_hour a number. All five were declared as strings, which made the whole cluster object undecodable — so cluster update could not have worked against a real cluster.
    • The RRD timeframe parameter is declared, so a window other than the default is reachable.

    The regeneration also removed the ...Retrieve2 operations, which existed only because the list and detail reads collided on one operation id while the schema was incomplete. The reads are now ...List and ...Retrieve.

phctl v0.17.0

Choose a tag to compare

@andreeapid andreeapid released this 18 Aug 15:01

Added

  • compute server operations: usage, activity, boot-isos, rescue enter / rescue exit, retry-provision, and public-interface get / set / delete. rescue enter and rescue exit reboot the server, so they confirm first; public-interface delete costs the server its public connectivity, so it does too.

    rescue enter, rescue exit and retry-provision answer 200 with {"queued": false} or {"retry": false} when they decline the work, so each checks the flag rather than the status code. boot-isos is scoped to a server rather than listed globally because compatibility depends on that server's disk and RAM, and it pages through cmdutil.FetchAll; its min_ram is a decimal the API sends as a string, rendered as one.

    public-interface set is a POST that the SDK calls Create, but it updates firewall settings on an interface that already exists — the addresses and interface name are read-only server-side. Only --firewall, --policy-in and --policy-out are 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, and credentials reveal / credentials rotate. It is a top-level group rather than compute bucket because compute volume is block storage attached to a server and the two are easy to confuse.

    create and resize confirm first because they change what the account is billed; creating a public bucket also warns that uploaded objects will be internet-readable. delete confirms because it takes the objects with it; visibility --public confirms because it exposes every object to the internet, while --private only removes access and does not. resize and visibility check the bucket actually came back changed — the routes answer 200 with 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 reveal prints an access key and secret, so it asks before doing so unless -f is 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 answer 200 with the secret in the body, and a body that fails to decode would otherwise put live credentials into an error string — and, for rotate, 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 list is deliberately absent: StorageProduct.price is a decimal, and pricing endpoints still go through internal/client's Raw* types.

  • compute server detach-ipv6: attach-ipv6 has existed for a while and detach-ipv4 was 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 -f is passed.

Fixed

  • billing deposit create no longer mangles the amount it reports. The API sends decimals as JSON strings, and sdk-go mapped them to float64 up 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 %.2f it was rendered with printed %!f(string=12): the .2 precision silently truncated 125.00 to 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, kubernetes and support wrapped the SDK error with fmt.Errorf, which discards the body — the only part that says what was actually wrong. A rejected ticket used to report creating ticket: 400 Bad Request; it now reports creating ticket: 400 Bad Request: department=This field is required. compute had already been converted; the rest had not. A contract test in cmd/apierror_contract_test.go keeps it that way.
  • -f/--force is now resolved the same way -o/--output is. It was read through Flags().GetBool, which only sees a root's persistent flags after Cobra merges them during Execute, so it returned false for any command driven directly through RunE.

phctl v0.16.0

Choose a tag to compare

@cristi-boboc cristi-boboc released this 17 Aug 21:35

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 --reboot and attach-ipv6 --reboot: restart the server as part of the attach so the guest OS picks the address up.

Changed

  • Requires sdk-go v0.11.0. The API split the server attach routes into distinct request and response components, so AttachIPv4/AttachIPv6 became AttachIPv4Request/AttachIPv6Request, and six enum components were renamed off content-derived names (for example StatusA57Enum → ResourceStatusEnum).
  • support ticket get renders 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-ipv6 no 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 --reboot or compute 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-ipv6 checks the result. It discarded the response entirely and always claimed success; it now fails when the backend reports the address was not attached, matching attach-ipv4.

  • -o json / -o yaml now work on commands that return a resource. compute server create printed Server created (ID: 123) through cmd.Printf and ignored the global --output flag entirely, so -o json silently 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 through internal/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 in cmd/ fails the build if a command prints API response fields with cmd.Print* again.

  • auth status honours --output and never emits the raw API token — machine-readable output carries the same masked form the table view shows.

  • cmdutil.OutputFormat no longer panics with a nil dereference when a command is exercised outside the root command tree (as RunE-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 400 rendered its fields in a different order each time; they are now sorted. This covers the newer attach-ipv4 / attach-ipv6 400 bodies, e.g. ipv4=This field is required.

phctl v0.15.0

Choose a tag to compare

@cristi-boboc cristi-boboc released this 22 Jun 15:29

Added

  • phctl domain glue (alias personal-dns): manage glue / personal-DNS records — child nameserver hosts registered at the registry. list <domain>, set <domain> --name ns1 --ip <ip> [--ip2 <ip>], and delete <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

Choose a tag to compare

@cristi-boboc cristi-boboc released this 22 Jun 08:33

Fixed

  • domain list and domain get no longer crash with json: cannot unmarshal bool into Go struct field RawDomain.idna of type string. The API's idna field is a boolean flag, not a string; the struct now decodes it as bool and exposes the punycode FQDN via the separate idna_name field (shown as "IDNA Name:" in domain get).

phctl v0.14.2

Choose a tag to compare

@cristi-boboc cristi-boboc released this 22 May 14:37

Fixed

  • compute server get Floating IPs table: header column ID was 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-ack help 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 backend no_network_acknowledged semantics.

Changed

  • Internal refactor: extracted the inline table-rendering closures in compute floating-ip list, compute package list, and compute server get into 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 GENERATIONS column rendering, floating-IP REVERSE_DNS column rendering, compute server get Floating IPs section, and compute server create --no-public-ipv4-ack sending no_network_acknowledged=true without allocating new_ipv4.

phctl v0.14.1

Choose a tag to compare

@cristi-boboc cristi-boboc released this 21 May 20:43

Added

  • compute server create --no-public-ipv4-ack: explicitly acknowledge the VM will have no public network, satisfying the backend no_network_acknowledged guard when none of --new-ipv4, --public-ip, --new-ipv6, --public-ipv6 is set on packages that would otherwise reject the create.