mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2024-01-19 02:48:24 +00:00
minFreeSpace refactored
This commit is contained in:
parent
31f1cdeac2
commit
cf552417a7
|
@ -52,7 +52,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func genFile(grpcDialOption grpc.DialOption, i int) (*operation.AssignResult, string) {
|
func genFile(grpcDialOption grpc.DialOption, i int) (*operation.AssignResult, string) {
|
||||||
assignResult, err := operation.Assign(*master, grpcDialOption, &operation.VolumeAssignRequest{
|
assignResult, err := operation.Assign(func() string { return *master }, grpcDialOption, &operation.VolumeAssignRequest{
|
||||||
Count: 1,
|
Count: 1,
|
||||||
Replication: *replication,
|
Replication: *replication,
|
||||||
})
|
})
|
||||||
|
|
|
@ -58,7 +58,8 @@ var (
|
||||||
serverDisableHttp = cmdServer.Flag.Bool("disableHttp", false, "disable http requests, only gRPC operations are allowed.")
|
serverDisableHttp = cmdServer.Flag.Bool("disableHttp", false, "disable http requests, only gRPC operations are allowed.")
|
||||||
volumeDataFolders = cmdServer.Flag.String("dir", os.TempDir(), "directories to store data files. dir[,dir]...")
|
volumeDataFolders = cmdServer.Flag.String("dir", os.TempDir(), "directories to store data files. dir[,dir]...")
|
||||||
volumeMaxDataVolumeCounts = cmdServer.Flag.String("volume.max", "8", "maximum numbers of volumes, count[,count]... If set to zero, the limit will be auto configured.")
|
volumeMaxDataVolumeCounts = cmdServer.Flag.String("volume.max", "8", "maximum numbers of volumes, count[,count]... If set to zero, the limit will be auto configured.")
|
||||||
volumeMinFreeSpacePercent = cmdServer.Flag.String("volume.minFreeSpacePercent", "1", "minimum free disk space (default to 1%). Low disk space will mark all volumes as ReadOnly.")
|
volumeMinFreeSpacePercent = cmdServer.Flag.String("volume.minFreeSpacePercent", "1", "minimum free disk space (default to 1%). Low disk space will mark all volumes as ReadOnly (deprecated, use minFreeSpace instead).")
|
||||||
|
volumeMinFreeSpace = cmdServer.Flag.String("volume.minFreeSpace", "", "min free disk space (value<=100 as percentage like 1, other as human readable bytes, like 10GiB). Low disk space will mark all volumes as ReadOnly.")
|
||||||
serverMetricsHttpPort = cmdServer.Flag.Int("metricsPort", 0, "Prometheus metrics listen port")
|
serverMetricsHttpPort = cmdServer.Flag.Int("metricsPort", 0, "Prometheus metrics listen port")
|
||||||
|
|
||||||
// pulseSeconds = cmdServer.Flag.Int("pulseSeconds", 5, "number of seconds between heartbeats")
|
// pulseSeconds = cmdServer.Flag.Int("pulseSeconds", 5, "number of seconds between heartbeats")
|
||||||
|
@ -244,8 +245,8 @@ func runServer(cmd *Command, args []string) bool {
|
||||||
|
|
||||||
// start volume server
|
// start volume server
|
||||||
if *isStartingVolumeServer {
|
if *isStartingVolumeServer {
|
||||||
go serverOptions.v.startVolumeServer(*volumeDataFolders, *volumeMaxDataVolumeCounts, *serverWhiteListOption, *volumeMinFreeSpacePercent)
|
minFreeSpaces := util.MustParseMinFreeSpace(*minFreeSpace, *minFreeSpacePercent)
|
||||||
|
go serverOptions.v.startVolumeServer(*volumeDataFolders, *volumeMaxDataVolumeCounts, *serverWhiteListOption, minFreeSpaces)
|
||||||
}
|
}
|
||||||
|
|
||||||
if *isStartingMasterServer {
|
if *isStartingMasterServer {
|
||||||
|
@ -253,6 +254,4 @@ func runServer(cmd *Command, args []string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
select {}
|
select {}
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,6 @@ type VolumeServerOptions struct {
|
||||||
compactionMBPerSecond *int
|
compactionMBPerSecond *int
|
||||||
fileSizeLimitMB *int
|
fileSizeLimitMB *int
|
||||||
concurrentUploadLimitMB *int
|
concurrentUploadLimitMB *int
|
||||||
minFreeSpaces []float32
|
|
||||||
pprof *bool
|
pprof *bool
|
||||||
preStopSeconds *int
|
preStopSeconds *int
|
||||||
metricsHttpPort *int
|
metricsHttpPort *int
|
||||||
|
@ -121,13 +120,13 @@ func runVolume(cmd *Command, args []string) bool {
|
||||||
|
|
||||||
go stats_collect.StartMetricsServer(*v.metricsHttpPort)
|
go stats_collect.StartMetricsServer(*v.metricsHttpPort)
|
||||||
|
|
||||||
v.startVolumeServer(*volumeFolders, *maxVolumeCounts, *volumeWhiteListOption,
|
minFreeSpaces := util.MustParseMinFreeSpace(*minFreeSpace, *minFreeSpacePercent)
|
||||||
util.EmptyTo(*minFreeSpace, *minFreeSpacePercent))
|
v.startVolumeServer(*volumeFolders, *maxVolumeCounts, *volumeWhiteListOption, minFreeSpaces)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v VolumeServerOptions) startVolumeServer(volumeFolders, maxVolumeCounts, volumeWhiteListOption, minFreeSpace string) {
|
func (v VolumeServerOptions) startVolumeServer(volumeFolders, maxVolumeCounts, volumeWhiteListOption string, minFreeSpaces []util.MinFreeSpace) {
|
||||||
|
|
||||||
// Set multiple folders and each folder's max volume count limit'
|
// Set multiple folders and each folder's max volume count limit'
|
||||||
v.folders = strings.Split(volumeFolders, ",")
|
v.folders = strings.Split(volumeFolders, ",")
|
||||||
|
@ -155,22 +154,13 @@ func (v VolumeServerOptions) startVolumeServer(volumeFolders, maxVolumeCounts, v
|
||||||
glog.Fatalf("%d directories by -dir, but only %d max is set by -max", len(v.folders), len(v.folderMaxLimits))
|
glog.Fatalf("%d directories by -dir, but only %d max is set by -max", len(v.folders), len(v.folderMaxLimits))
|
||||||
}
|
}
|
||||||
|
|
||||||
// set minFreeSpacePercent
|
if len(minFreeSpaces) == 1 && len(v.folders) > 1 {
|
||||||
minFreeSpaceStrings := strings.Split(minFreeSpace, ",")
|
|
||||||
for _, freeString := range minFreeSpaceStrings {
|
|
||||||
if vv, e := util.ParseMinFreeSpace(freeString); e == nil {
|
|
||||||
v.minFreeSpaces = append(v.minFreeSpaces, vv)
|
|
||||||
} else {
|
|
||||||
glog.Fatalf("The value specified in -minFreeSpace not a valid value %s", freeString)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(v.minFreeSpaces) == 1 && len(v.folders) > 1 {
|
|
||||||
for i := 0; i < len(v.folders)-1; i++ {
|
for i := 0; i < len(v.folders)-1; i++ {
|
||||||
v.minFreeSpaces = append(v.minFreeSpaces, v.minFreeSpaces[0])
|
minFreeSpaces = append(minFreeSpaces, minFreeSpaces[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(v.folders) != len(v.minFreeSpaces) {
|
if len(v.folders) != len(minFreeSpaces) {
|
||||||
glog.Fatalf("%d directories by -dir, but only %d minFreeSpacePercent is set by -minFreeSpacePercent", len(v.folders), len(v.minFreeSpaces))
|
glog.Fatalf("%d directories by -dir, but only %d minFreeSpacePercent is set by -minFreeSpacePercent", len(v.folders), len(minFreeSpaces))
|
||||||
}
|
}
|
||||||
|
|
||||||
// set disk types
|
// set disk types
|
||||||
|
@ -233,7 +223,7 @@ func (v VolumeServerOptions) startVolumeServer(volumeFolders, maxVolumeCounts, v
|
||||||
|
|
||||||
volumeServer := weed_server.NewVolumeServer(volumeMux, publicVolumeMux,
|
volumeServer := weed_server.NewVolumeServer(volumeMux, publicVolumeMux,
|
||||||
*v.ip, *v.port, *v.publicUrl,
|
*v.ip, *v.port, *v.publicUrl,
|
||||||
v.folders, v.folderMaxLimits, v.minFreeSpaces, diskTypes,
|
v.folders, v.folderMaxLimits, minFreeSpaces, diskTypes,
|
||||||
*v.idxFolder,
|
*v.idxFolder,
|
||||||
volumeNeedleMapKind,
|
volumeNeedleMapKind,
|
||||||
strings.Split(masters, ","), 5, *v.dataCenter, *v.rack,
|
strings.Split(masters, ","), 5, *v.dataCenter, *v.rack,
|
||||||
|
|
|
@ -43,7 +43,7 @@ type VolumeServer struct {
|
||||||
|
|
||||||
func NewVolumeServer(adminMux, publicMux *http.ServeMux, ip string,
|
func NewVolumeServer(adminMux, publicMux *http.ServeMux, ip string,
|
||||||
port int, publicUrl string,
|
port int, publicUrl string,
|
||||||
folders []string, maxCounts []int, minFreeSpaces []float32, diskTypes []types.DiskType,
|
folders []string, maxCounts []int, minFreeSpaces []util.MinFreeSpace, diskTypes []types.DiskType,
|
||||||
idxFolder string,
|
idxFolder string,
|
||||||
needleMapKind storage.NeedleMapKind,
|
needleMapKind storage.NeedleMapKind,
|
||||||
masterNodes []string, pulseSeconds int,
|
masterNodes []string, pulseSeconds int,
|
||||||
|
|
|
@ -23,10 +23,9 @@ type DiskLocation struct {
|
||||||
DiskType types.DiskType
|
DiskType types.DiskType
|
||||||
MaxVolumeCount int
|
MaxVolumeCount int
|
||||||
OriginalMaxVolumeCount int
|
OriginalMaxVolumeCount int
|
||||||
// MinFreeSpace limits the minimum free space (<=100 as percentage, > 100 as bytes)
|
MinFreeSpace util.MinFreeSpace
|
||||||
MinFreeSpace float32
|
volumes map[needle.VolumeId]*Volume
|
||||||
volumes map[needle.VolumeId]*Volume
|
volumesLock sync.RWMutex
|
||||||
volumesLock sync.RWMutex
|
|
||||||
|
|
||||||
// erasure coding
|
// erasure coding
|
||||||
ecVolumes map[needle.VolumeId]*erasure_coding.EcVolume
|
ecVolumes map[needle.VolumeId]*erasure_coding.EcVolume
|
||||||
|
@ -35,7 +34,7 @@ type DiskLocation struct {
|
||||||
isDiskSpaceLow bool
|
isDiskSpaceLow bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDiskLocation(dir string, maxVolumeCount int, minFreeSpace float32, idxDir string, diskType types.DiskType) *DiskLocation {
|
func NewDiskLocation(dir string, maxVolumeCount int, minFreeSpace util.MinFreeSpace, idxDir string, diskType types.DiskType) *DiskLocation {
|
||||||
dir = util.ResolvePath(dir)
|
dir = util.ResolvePath(dir)
|
||||||
if idxDir == "" {
|
if idxDir == "" {
|
||||||
idxDir = dir
|
idxDir = dir
|
||||||
|
@ -363,7 +362,7 @@ func (l *DiskLocation) CheckDiskSpace() {
|
||||||
stats.VolumeServerResourceGauge.WithLabelValues(l.Directory, "used").Set(float64(s.Used))
|
stats.VolumeServerResourceGauge.WithLabelValues(l.Directory, "used").Set(float64(s.Used))
|
||||||
stats.VolumeServerResourceGauge.WithLabelValues(l.Directory, "free").Set(float64(s.Free))
|
stats.VolumeServerResourceGauge.WithLabelValues(l.Directory, "free").Set(float64(s.Free))
|
||||||
|
|
||||||
isLow := l.MinFreeSpace < 100 && s.PercentFree < l.MinFreeSpace || s.Free < uint64(l.MinFreeSpace)
|
isLow, desc := l.MinFreeSpace.IsLow(s.Free, s.PercentFree)
|
||||||
if isLow != l.isDiskSpaceLow {
|
if isLow != l.isDiskSpaceLow {
|
||||||
l.isDiskSpaceLow = !l.isDiskSpaceLow
|
l.isDiskSpaceLow = !l.isDiskSpaceLow
|
||||||
}
|
}
|
||||||
|
@ -373,8 +372,7 @@ func (l *DiskLocation) CheckDiskSpace() {
|
||||||
logLevel = glog.Level(0)
|
logLevel = glog.Level(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
glog.V(logLevel).Infof("dir %s freePercent %.2f%% < min %.2f%%, isLowDiskSpace: %v",
|
glog.V(logLevel).Infof("dir %s %s", dir, desc)
|
||||||
dir, s.PercentFree, l.MinFreeSpace, l.isDiskSpaceLow)
|
|
||||||
}
|
}
|
||||||
time.Sleep(time.Minute)
|
time.Sleep(time.Minute)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/chrislusf/seaweedfs/weed/util"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
@ -52,7 +53,8 @@ func (s *Store) String() (str string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStore(grpcDialOption grpc.DialOption, port int, ip, publicUrl string, dirnames []string, maxVolumeCounts []int, minFreeSpaces []float32, idxFolder string, needleMapKind NeedleMapKind, diskTypes []DiskType) (s *Store) {
|
func NewStore(grpcDialOption grpc.DialOption, port int, ip, publicUrl string, dirnames []string, maxVolumeCounts []int,
|
||||||
|
minFreeSpaces []util.MinFreeSpace, idxFolder string, needleMapKind NeedleMapKind, diskTypes []DiskType) (s *Store) {
|
||||||
s = &Store{grpcDialOption: grpcDialOption, Port: port, Ip: ip, PublicUrl: publicUrl, NeedleMapKind: needleMapKind}
|
s = &Store{grpcDialOption: grpcDialOption, Port: port, Ip: ip, PublicUrl: publicUrl, NeedleMapKind: needleMapKind}
|
||||||
s.Locations = make([]*DiskLocation, 0)
|
s.Locations = make([]*DiskLocation, 0)
|
||||||
for i := 0; i < len(dirnames); i++ {
|
for i := 0; i < len(dirnames); i++ {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
|
@ -176,23 +175,12 @@ func EmptyTo(s, to string) string {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrMinFreeSpaceBadValue = errors.New("minFreeSpace is invalid")
|
// IfElse works like b ? this : that.
|
||||||
|
func IfElse(b bool, this, that string) string {
|
||||||
// ParseMinFreeSpace parses min free space expression s as percentage like 1,10 or human readable size like 10G
|
if b {
|
||||||
func ParseMinFreeSpace(s string) (float32, error) {
|
return this
|
||||||
if value, e := strconv.ParseFloat(s, 32); e == nil {
|
|
||||||
if value < 0 || value > 100 {
|
|
||||||
return 0, ErrMinFreeSpaceBadValue
|
|
||||||
}
|
|
||||||
return float32(value), nil
|
|
||||||
} else if directSize, e2 := ParseBytes(s); e2 == nil {
|
|
||||||
if directSize <= 100 {
|
|
||||||
return 0, ErrMinFreeSpaceBadValue
|
|
||||||
}
|
|
||||||
return float32(directSize), nil
|
|
||||||
}
|
}
|
||||||
|
return that
|
||||||
return 0, ErrMinFreeSpaceBadValue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseBytes parses a string representation of bytes into the number
|
// ParseBytes parses a string representation of bytes into the number
|
||||||
|
|
|
@ -2,32 +2,6 @@ package util
|
||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestParseMinFreeSpace(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
in string
|
|
||||||
ok bool
|
|
||||||
value float32
|
|
||||||
}{
|
|
||||||
{in: "42", ok: true, value: 42},
|
|
||||||
{in: "-1", ok: false, value: 0},
|
|
||||||
{in: "101", ok: false, value: 0},
|
|
||||||
{in: "100B", ok: false, value: 0},
|
|
||||||
{in: "100Ki", ok: true, value: 100 * 1024},
|
|
||||||
{in: "100GiB", ok: true, value: 100 * 1024 * 1024 * 1024},
|
|
||||||
{in: "42M", ok: true, value: 42 * 1000 * 1000},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, p := range tests {
|
|
||||||
got, err := ParseMinFreeSpace(p.in)
|
|
||||||
if p.ok != (err == nil) {
|
|
||||||
t.Errorf("failed to test %v", p.in)
|
|
||||||
}
|
|
||||||
if p.ok && err == nil && got != p.value {
|
|
||||||
t.Errorf("failed to test %v", p.in)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestByteParsing(t *testing.T) {
|
func TestByteParsing(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
in string
|
in string
|
||||||
|
|
90
weed/util/minfreespace.go
Normal file
90
weed/util/minfreespace.go
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/chrislusf/seaweedfs/weed/glog"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MinFreeSpaceType is the type of MinFreeSpace.
|
||||||
|
type MinFreeSpaceType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AsPercent set the MinFreeSpaceType to a percentage value from 0 to 100.
|
||||||
|
AsPercent MinFreeSpaceType = iota
|
||||||
|
// AsBytes set the MinFreeSpaceType to a absolute value bytes.
|
||||||
|
AsBytes
|
||||||
|
)
|
||||||
|
|
||||||
|
// MinFreeSpace is type that defines the limit for the minimum free space.
|
||||||
|
type MinFreeSpace struct {
|
||||||
|
Type MinFreeSpaceType
|
||||||
|
Bytes uint64
|
||||||
|
Percent float32
|
||||||
|
Raw string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLow tells whether the free space is low or not.
|
||||||
|
func (s MinFreeSpace) IsLow(freeBytes uint64, freePercent float32) (yes bool, desc string) {
|
||||||
|
switch s.Type {
|
||||||
|
case AsPercent:
|
||||||
|
yes = freePercent < s.Percent
|
||||||
|
op := IfElse(yes, "<", ">=")
|
||||||
|
return yes, fmt.Sprintf("disk free %.2f%% %s required %.2f%%", freePercent, op, s.Percent)
|
||||||
|
case AsBytes:
|
||||||
|
yes = freeBytes < s.Bytes
|
||||||
|
op := IfElse(yes, "<", ">=")
|
||||||
|
return yes, fmt.Sprintf("disk free %s %s required %s",
|
||||||
|
BytesToHumanReadable(freeBytes), op, BytesToHumanReadable(s.Bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of MinFreeSpace.
|
||||||
|
func (s MinFreeSpace) String() string {
|
||||||
|
switch s.Type {
|
||||||
|
case AsPercent:
|
||||||
|
return fmt.Sprintf("%.2f%%", s.Percent)
|
||||||
|
default:
|
||||||
|
return s.Raw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustParseMinFreeSpace parses comma-separated argument for min free space setting.
|
||||||
|
// minFreeSpace has the high priority than minFreeSpacePercent if it is set.
|
||||||
|
func MustParseMinFreeSpace(minFreeSpace string, minFreeSpacePercent string) (spaces []MinFreeSpace) {
|
||||||
|
ss := strings.Split(EmptyTo(minFreeSpace, minFreeSpacePercent), ",")
|
||||||
|
for _, freeString := range ss {
|
||||||
|
if vv, e := ParseMinFreeSpace(freeString); e == nil {
|
||||||
|
spaces = append(spaces, *vv)
|
||||||
|
} else {
|
||||||
|
glog.Fatalf("The value specified in -minFreeSpace not a valid value %s", freeString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return spaces
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrMinFreeSpaceBadValue = errors.New("minFreeSpace is invalid")
|
||||||
|
|
||||||
|
// ParseMinFreeSpace parses min free space expression s as percentage like 1,10 or human readable size like 10G
|
||||||
|
func ParseMinFreeSpace(s string) (*MinFreeSpace, error) {
|
||||||
|
if percent, e := strconv.ParseFloat(s, 32); e == nil {
|
||||||
|
if percent < 0 || percent > 100 {
|
||||||
|
return nil, ErrMinFreeSpaceBadValue
|
||||||
|
}
|
||||||
|
return &MinFreeSpace{Type: AsPercent, Percent: float32(percent), Raw: s}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if directSize, e := ParseBytes(s); e == nil {
|
||||||
|
if directSize <= 100 {
|
||||||
|
return nil, ErrMinFreeSpaceBadValue
|
||||||
|
}
|
||||||
|
return &MinFreeSpace{Type: AsBytes, Bytes: directSize, Raw: s}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrMinFreeSpaceBadValue
|
||||||
|
}
|
29
weed/util/minfreespace_test.go
Normal file
29
weed/util/minfreespace_test.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestParseMinFreeSpace(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
in string
|
||||||
|
ok bool
|
||||||
|
value *MinFreeSpace
|
||||||
|
}{
|
||||||
|
{in: "42", ok: true, value: &MinFreeSpace{Type: AsPercent, Percent: 42, Raw: "42"}},
|
||||||
|
{in: "-1", ok: false, value: nil},
|
||||||
|
{in: "101", ok: false, value: nil},
|
||||||
|
{in: "100B", ok: false, value: nil},
|
||||||
|
{in: "100Ki", ok: true, value: &MinFreeSpace{Type: AsBytes, Bytes: 100 * 1024, Raw: "100Ki"}},
|
||||||
|
{in: "100GiB", ok: true, value: &MinFreeSpace{Type: AsBytes, Bytes: 100 * 1024 * 1024 * 1024, Raw: "100GiB"}},
|
||||||
|
{in: "42M", ok: true, value: &MinFreeSpace{Type: AsBytes, Bytes: 42 * 1000 * 1000, Raw: "42M"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range tests {
|
||||||
|
got, err := ParseMinFreeSpace(p.in)
|
||||||
|
if p.ok != (err == nil) {
|
||||||
|
t.Errorf("failed to test %v", p.in)
|
||||||
|
}
|
||||||
|
if p.ok && err == nil && *got != *p.value {
|
||||||
|
t.Errorf("failed to test %v", p.in)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue