Skip to content

Conversation

@mgajda
Copy link
Contributor

@mgajda mgajda commented Dec 15, 2025

Improves 'upctl account billing' command to view billing information:

Account billing summary

  • Flexible period specification: --period "last month", --period "3months", --period "2024-Q3"
  • Smart defaults: Works without flags (defaults to current month)
  • Resource names: Shows human-readable names with --detailed flag
  • Advanced filtering: Filter by name pattern (--match) or category (--category)

Related to #339

@mgajda mgajda requested a review from a team as a code owner December 15, 2025 07:35
@kangasta
Copy link
Member

Thank you for the PR! Seems that this is at least partly duplicate of #606. We'll handle that one first as it was created earlier and we have already started review work there. Let's see after that how we can further improve the billing logs support.

@mgajda
Copy link
Contributor Author

mgajda commented Dec 29, 2025

@kangasta Ideally the PR would include features both #606 and #632, since budget management is a major selling point for UpCloud over traditional cloud solutions.
I opened a separate request in an attempt to revive the issue, and broaden the scope.
It depended on UpCloudLtd/upcloud-go-api#456 which is now merged.

mgajda added 2 commits January 4, 2026 17:14
Implements 'upctl billing' commands to view billing information:

## billing summary
View cost summary by category with flexible period options:
- Named periods: 'month', 'day', 'quarter', 'year', 'last month'
- Relative periods: '3months' (3 months ago), '2weeks' (2 weeks ago)
- Relative from date: '2months from 2024-06', '+3months from 2024-01'
- Direct format: 'YYYY-MM'
- Optional --resource filter for specific resource UUID
- Optional --detailed flag for full breakdown

## billing list
List detailed billing with resource names:
- Fetches and displays resource names (servers, storage)
- Same flexible --period options as summary
- --match flag for name filtering (case-insensitive)
- --category flag to filter by resource type
- Shows both UUID and name for identification

Both commands:
- Default to current month if period not specified
- Support JSON/YAML output for scripting
- Use existing GetBillingSummary API endpoint

Addresses UpCloudLtd#339
- Add trailing newlines to files
- Fix struct field alignment in resourceInfo
- Fix string concatenation spacing
@mgajda mgajda force-pushed the feat/billing-summary branch from 93c3b64 to a5402bf Compare January 4, 2026 19:36
mgajda added 2 commits January 4, 2026 21:12
…fication

- Add --period flag for intuitive date specification (month, last month, 3months, etc)
- Add --detailed flag to show resource names alongside UUIDs
- Add --match and --category flags for resource filtering
- Default to current month when no period specified
- Maintain full backward compatibility with --year/--month flags
- Remove duplicate billing commands, enhance existing account billing instead

Following reviewer guidance to avoid command duplication while preserving
all valuable functionality from the original billing commands.
- Extract getCategories() and getResourceGroups() helper functions
- Reuse helper functions across buildOriginalSections, buildEnhancedSections, and buildResourceRows
- Add comprehensive backward compatibility tests
- Verify year/month flags override period flag when both are specified
- Reduce code duplication by ~45 lines while maintaining all functionality
@mgajda
Copy link
Contributor Author

mgajda commented Jan 5, 2026

The PR is now on top of main and the existing account billing command rather than creating duplicates:

  • All original functionality preserved (--year, --month, --resource-id, --username work exactly as before)
  • Added new capabilities: flexible period parsing, resource name resolution, and advanced filtering
  • Added 300 lines of enhancements

New Features Added to account billing

  • Flexible period specification: --period "last month", --period "3months", --period "2024-Q3"
  • Smart defaults: Works without flags (defaults to current month)
  • Resource names: Shows human-readable names with --detailed flag
  • Advanced filtering: Filter by name pattern (--match) or category (--category)

Backward Compatibility

  • Original --year and --month flags still work exactly as before
  • When both old and new flags are specified, old flags take precedence
  • Comprehensive test suite validates all backward compatibility scenarios

Example Usage

  • Original usage still works

    upctl account billing --year 2024 --month 7
  • Works without flags (current month)

    upctl account billing
  • Natural language periods

    upctl account billing --period "last month"
  • See resource names instead of just UUIDs

    upctl account billing --period "3months" --detailed
  • Filter by name or category

    upctl account billing --match "prod" --category server

@mgajda mgajda requested a review from kangasta January 5, 2026 14:02
Copy link
Member

@kangasta kangasta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR includes quite a lot of different changes, so not sure what would be the best way forward. I would suggest, even though there is no direct dependencies between this and #633, that we put this PR on hold until #633 has been resolved to limit the amount of parallel work 🤔


// parsePeriod converts various period formats into YYYY-MM for the API
// Supports formats like: "month", "last month", "3months", "2024-07", "2months from 2024-06"
func parsePeriod(period string) (string, string, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This argument/function seems quite complex and might cause confusion so maybe it could be omitted for now 🤔

We try to keep the user-interface quite simple and close to the API, so adding a non-standard way for defining the period would be quite different compared to options provided in other commands. I'm sorry that these design principles are not well documented in any public guidelines yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that was some work, but I would like to push for this feature as a first user: depending of the project one wants a quarterly, monthly, or biweekly billing.

}

// fetchResourceNames retrieves names for servers and storage resources
func (s *billingCommand) fetchResourceNames(exec commands.Executor, summary *upcloud.BillingSummary) map[string]string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have some logic already for filtering resources by name in all list and all purge commands, maybe that could be re-used here 🤔 That code would not help finding resources that have already been deleted, though.

@kangasta kangasta added the blocked Can not proceed until something else has been resolved. label Jan 7, 2026
@mgajda mgajda changed the title feat: add billing commands for cost summaries and resource listing feat: improved cost summaries and resource listing in account billing Jan 9, 2026
mgajda added 2 commits January 9, 2026 20:31
- Add concurrent fetching of resource names for better performance
- Support more resource types: load balancers, databases, Kubernetes clusters
- Use sync.WaitGroup for parallel API calls with thread-safe name collection
- Gracefully handle errors to avoid breaking the entire billing display
- Extract common goroutine creation pattern into helper function
- Use defer for mutex unlock to ensure proper cleanup
- Reduce code duplication in resource fetching logic
- Address golangci-lint suggestions for cleaner code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

blocked Can not proceed until something else has been resolved.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants