[Bug]: fractional memory sizes are truncated when a configuration is persisted (`--memory 1.5g` is stored as 1 GiB)
- Dominant language
- Swift
- Stars
- 49.9k
- Forks
- 1.8k
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 22
Description
### I have done the following
- [x] I have searched the existing issues
- [x] If possible, I've reproduced the issue using the 'main' branch of this project
### Steps to reproduce
`container machine create --memory 1.5g` (and `container machine set memory=1.5g`) parses the value into a `MemorySize`, and `MachineConfig` persists it through `Codable`. `MemorySize.encode(to:)` writes `formatted`, which truncates:
```swift
public var formatted: String {
let value = Int64(measurement.value)
let label = Self.unitLabels[measurement.unit] ?? "unknown"
return "\(value)\(label)"
}
```
I don't have a macOS host available to run the CLI on, so I exercised the type itself, taking `Sources/ContainerPersistence/MemorySize.swift` and `Sources/ContainerPersistence/Measurement+Parse.swift` from `main` unchanged and encoding/decoding through `JSONEncoder`/`JSONDecoder` (Swift 6.2):
| input | bytes before encoding | encoded | bytes after decoding |
| --- | --- | --- | --- |
| `1gb` | 1073741824 | `"1gb"` | 1073741824 |
| `2048mb` | 2147483648 | `"2048mb"` | 2147483648 |
| `1.5gb` | 1610612736 | `"1gb"` | 1073741824 |
| `0.5tb` | 549755813888 | `"0tb"` | 0 |
### Problem description
`Measurement.parse` deliberately accepts a decimal point, so `1.5gb` is valid input and `MachineConfig.validate()` accepts it. Persisting it is what loses the fraction, and there is no warning: the number the user typed simply is not the number that is stored.
Two consequences follow:
1. A machine created with `--memory 1.5g` runs with 1 GiB. `MachinesService` sets `config.resources.memoryInBytes` from `bootConfig.memory.toUInt64(unit: .bytes)`, and that boot config comes back from the store.
2. A size that is below 1 in its own unit encodes to `0`. `MachineConfig.init(from:)` calls `validate()`, which rejects anything under 1 GiB, so a machine created with `--memory 0.5t` is persisted as `"0tb"` and its configuration no longer decodes — the entry is stuck.
What I would expect is for the encoded string to parse back to the size that was requested. Rendering the value in the largest unit that still expresses it as a whole number does that (`1.5gb` -> `1536mb`, `0.5tb` -> `512gb`) and leaves whole values untouched, so existing configurations and existing output do not change.
I have a fix ready and will open a pull request against this issue.
### Environment
- OS: not applicable — I reproduced this from the sources with Swift 6.2, not on an installed `container`
- Xcode: not applicable
- Container: `main` @ 33ebc8ae
### Code of Conduct
- [x] I agree to follow this project's Code of Conduct
Contributor guide
Research direction
Start with Sources/ContainerPersistence/MemorySize.swift and Sources/ContainerPersistence/Measurement+Parse.swift, then reproduce the JSONEncoder/JSONDecoder round trip described in the issue. Done means fractional sizes such as 1.5gb and 0.5tb decode to the original byte counts while whole existing values retain their current encoding.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- backend, cli
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100