Skip to content

feat: Add volume usage progress bar - #423

Open
khatibomar wants to merge 12 commits into
unikraft-cloud:prod-stagingfrom
khatibomar:feat/volume-usage-progress
Open

feat: Add volume usage progress bar#423
khatibomar wants to merge 12 commits into
unikraft-cloud:prod-stagingfrom
khatibomar:feat/volume-usage-progress

Conversation

@khatibomar

@khatibomar khatibomar commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Fixes #373
Closes #373

image

@craciunoiuc craciunoiuc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Some comments

Comment thread internal/types/quota.go Outdated
emptyColor := ansi.NewStyle().ForegroundColor(ansi.IndexedColor(238)).String()
reset := ansi.NewStyle().ForegroundColor(nil).String()

dots := []rune{'⣀', '⣄', '⣤', '⣦', '⣶', '⣷', '⣿'}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wonder if this format can be found somewhere in bubbletea or smth else? I've seen it before

@nderjung nderjung Jul 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

All these vars should also be defined as const / private internal var.

Comment thread internal/cmd/volumes.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds colored volume-usage sparklines to volume listings.

Changes:

  • Introduces a generic sparkline usage type.
  • Derives volume usage from size and free capacity.
  • Updates volume output and help golden files.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
internal/types/quota.go Implements usage ratio and sparkline rendering.
internal/cmd/volumes.go Adds and populates the volume usage field.
internal/cmd/testdata/TestOutput/volumes Updates expected volume output.
cmd/unikraft/testdata/TestHelp/volumes Adds usage to documented fields.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/types/quota.go Outdated
Comment thread internal/types/quota.go Outdated
Comment thread internal/types/quota.go Outdated
Comment thread internal/cmd/volumes.go
@khatibomar

Copy link
Copy Markdown
Contributor Author
image

Added a custom compare for custom types like sparkling line.

It would be nice to support >,<,>=,<= operators but I seen that it already have an issue

#374

@khatibomar
khatibomar force-pushed the feat/volume-usage-progress branch 2 times, most recently from 1b17f34 to da055c5 Compare July 24, 2026 11:51
Signed-off-by: ayn <ayn.khatib@gmail.com>
@khatibomar
khatibomar force-pushed the feat/volume-usage-progress branch from a729717 to 00346ab Compare July 24, 2026 14:07
@craciunoiuc

Copy link
Copy Markdown
Contributor

@khatibomar can you close the copilot comments that you resolved/ignored so they don't get in the way 🙏

@craciunoiuc craciunoiuc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Some sanity comments my side. Feel free to comment or ignore

I think it looks fine already 🙏

I think after we can get @jedevc also in here for a final opinion

Comment thread internal/types/quota.go Outdated
Comment on lines +91 to +97
case ratio <= 0.5:
color = ansi.NewStyle().ForegroundColor(colors.Success).String()
case ratio <= 0.8:
color = ansi.NewStyle().ForegroundColor(colors.Warning).String()
default:
color = ansi.NewStyle().ForegroundColor(colors.Error).String()
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Just checking, did you compare these colors with the quota bars in the quota command? Same for the percentages where these change

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think the quota have only a blue bar, it doesn't have a color based on percentage.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah sorry, it means we removed that in the end, all good I think, but I'll tag maybe also @jedevc

Do we want percentage colouring or do we remove colors here also?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hm, we should have quotas coloring 🎉

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

var (
  emptyColor = // slate
  safeColor = // green 
  warningColor = // amber > 60%
  dangerColor = // red-rose > 80%

Also note light-dark theme variations, e.g.: https://github.com/unikraft/cli/blob/prod-staging/internal/tui/selector/colors.go#L15-L17

Comment thread internal/types/quota.go Outdated
Comment thread internal/types/quota.go Outdated
Comment on lines +87 to +88
const width = 10

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

could the width be easily made variable? Aka, min 5, max 10 width, like the quotas

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

if you mean metro_quotas.go they are set to be depending on the screen size, I think same thing can be done in here and limited between [5,10] range

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yes exactly, I'm thinking if it would make sense here also to make it variable, so smaller terminals also work

state: available
size: 50MiB
free: 10MiB
usage: ⣿⣿⣿⣿⣿⣿⣿⣿⣀⣀ 40MiB/50MiB

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: I wonder if maybe we should show the number first, then the number first?

Suggested change
usage: ⣿⣿⣿⣿⣿⣿⣿⣿⣀⣀ 40MiB/50MiB
usage: 40MiB/50MiB ⣿⣿⣿⣿⣿⣿⣿⣿⣀⣀

Or maybe even as a percentage?

Suggested change
usage: ⣿⣿⣿⣿⣿⣿⣿⣿⣀⣀ 40MiB/50MiB
usage: 80% ⣿⣿⣿⣿⣿⣿⣿⣿⣀⣀

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

hmmm prefer other way - since the bar is fixed width and looks more aligned in a table

@jedevc jedevc Jul 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Fair point. We could always " " pad it if it was a percentage.

I think I more care about the percentage than the side it's on, though I would argue that having the most significant info (the number) first is useful.

@nderjung nderjung Jul 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That works, i think separating % and actual usage with the bar in the middle makes it easier to read:

METRO  NAME        STATE      FREE   USAGE                           CREATED
fra    my-volume1  available  10MiB   50% ⣿⣿⣿⣿⣿⣀⣀⣀⣀⣀ 50MiB/100MiB   just now
fra    my-volume2  available  10MiB  100% ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ 100MiB/100MiB  just now

in contrast together feels noisy:

METRO  NAME        STATE      FREE   USAGE                           CREATED
fra    my-volume1  available  10MiB  ⣿⣿⣿⣿⣿⣀⣀⣀⣀⣀  50% 50MiB/100MiB   just now
fra    my-volume2  available  10MiB  ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ 100% 100MiB/100MiB  just now

Edit: would argue we can reduce it further by removing duplicate MiB and removing the FREE column:

METRO  NAME        STATE      USAGE                        CREATED
fra    my-volume1  available   50% ⣿⣿⣿⣿⣿⣀⣀⣀⣀⣀ 50/100MiB   just now
fra    my-volume2  available  100% ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ 100/100MiB  just now

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

imo, i would keep usage as just percent + bar. while the free + size appear as columns after it. makes it more obvious that they can be selected.

@khatibomar khatibomar Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

that empty space in FREE kinda when disk full annoys me a bit

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oh that is super odd. Is that because IsEmpty is applying…? We should fix that. We should display 0 here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

But yes, that is generally correct and what I was hoping for!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Let's hope that none of the users relying on output of this CLI column shape/ordering, their scripts will 🍳

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yeah, that's okay 🙂 users who care about this should be using -ojson.

state: available
size: 50MiB
free: 10MiB
usage: ⣿⣿⣿⣿⣿⣿⣿⣿⣀⣀ 40MiB/50MiB

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

❯ unikraft volume inspect foo -oquiet -f usage
⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀ 0B/10MiB

Quiet mode should show the direct underlying value, ideally as a percentage, see above.

Comment thread internal/resource/value/compare.go Outdated
Comment on lines +23 to +26
// Comparer is an interface for types that provide a custom comparable value.
type Comparer interface {
CompareValue() any
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think we should do this interface. Things should implement cmp.Compare to do the comparison.

Somewhere else I had the idea that we should have a custom Render implementation for rendering to the screen, but for filtering, we would just use .String() or .MarshalText or whatever instead of doing the weird post-ASCII filtering.

@khatibomar khatibomar Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

cmp.Compare doesn't have an interface unfortunately it's func Compare[T Ordered](x, y T) int where Ordered is

type Ordered interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
		~float32 | ~float64 |
		~string
}

Altho, I did implemente compare-like method on sparkline and it did work!

I believe part of the reflection code catches this method and use it for sorting.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Well it's actually just sorting as string, it just happen to be true, as soon I added percentage at first it broke XD

Comment thread internal/types/quota.go
func (u SparklineUsage[T]) String() string {
if u.Total == 0 {
return "N/A"
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Essentially, as mention above, this method would just become Render, while String would be implemented to be just the raw percentage.

This would be a bit of a refactor in a few other places, but the code would then feel much neater I think 🎉

Comment thread internal/types/quota.go Outdated
Comment on lines +91 to +97
case ratio <= 0.5:
color = ansi.NewStyle().ForegroundColor(colors.Success).String()
case ratio <= 0.8:
color = ansi.NewStyle().ForegroundColor(colors.Warning).String()
default:
color = ansi.NewStyle().ForegroundColor(colors.Error).String()
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hm, we should have quotas coloring 🎉

@craciunoiuc

Copy link
Copy Markdown
Contributor

What do you mean quotas coloring you told me to remove the colors some time ago 😭 😭 😭 😭

@jedevc

jedevc commented Jul 27, 2026

Copy link
Copy Markdown
Member

What do you mean quotas coloring you told me to remove the colors some time ago 😭 😭 😭 😭

I seem to remember suggesting making the color bars have reasonable boundaries, there were some very magic numbers in there as I remember it 🤔 #337 (comment)

I don't remember suggesting removing them entirely, but maybe I did - if so, woops.

@craciunoiuc

Copy link
Copy Markdown
Contributor

Ah, maybe I got it wrong, eh too late now :D

Comment thread internal/types/quota.go Outdated
}

emptyColor := ansi.NewStyle().ForegroundColor(colors.Slate500).String()
reset := ansi.NewStyle().ForegroundColor(nil).String()

@nderjung nderjung Jul 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

when using lipgloss we should not need to do any resets if strings are correctly wrapped (Rendered) since it handles it for us.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Aha one would think so. I think there's a reason to not do this though - lipgloss sends resets for everything, not just the styles you set. I seem to remember that this does super annoying things in combo with the diff viewer when doing edits for example.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I am not sure that I understand this one, I haven't worked on bubbletea before, I think I have to toy around with it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

So bubbletea's colors are different from using ansi - we do want to actually use ansi directly. lipgloss also has colors, but the way it does colors interacts badly with our custom diff view that shows on edit. You can give it a go, and see what happens 🙂

Comment thread internal/types/quota.go Outdated
Comment on lines +61 to +65
// SparklineUsage represents a used/total pair for quotas, rendered as a colored sparkline.
type SparklineUsage[T numeric] struct {
Used T `field:",long"`
Total T `field:",long"`
}

@nderjung nderjung Jul 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested internal package internal/tui/sparkline -> can then be used in other places?

Also actually this is technically not a sparkline (my mistake for calling this in the original issue - sparklines are timeseries data), better to call it "meter" or "guage" or "usagebar" ?

Signed-off-by: ayn <ayn.khatib@gmail.com>
Signed-off-by: ayn <ayn.khatib@gmail.com>
Signed-off-by: ayn <ayn.khatib@gmail.com>
Signed-off-by: ayn <ayn.khatib@gmail.com>
Signed-off-by: ayn <ayn.khatib@gmail.com>
Signed-off-by: ayn <ayn.khatib@gmail.com>
Signed-off-by: ayn <ayn.khatib@gmail.com>
Signed-off-by: ayn <ayn.khatib@gmail.com>
Signed-off-by: ayn <ayn.khatib@gmail.com>
Signed-off-by: ayn <ayn.khatib@gmail.com>
Signed-off-by: ayn <ayn.khatib@gmail.com>
@khatibomar

khatibomar commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author
image Screenshot_20260730_191809 Screenshot_20260730_191911 image image

@jedevc I addressed the comments, I think it's ready for re-review.

@jedevc
jedevc self-requested a review July 30, 2026 22:30
@craciunoiuc

Copy link
Copy Markdown
Contributor

I mean, I like it, so unless someone has an objection, I think we should merge? 👀

jedevc commented Jul 31, 2026

Copy link
Copy Markdown
Member

Will review again on Monday - but yes, we can aim to merge soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add new sparkline fields

5 participants