Merge pull request #4779 from hashicorp/f-attributes-typed
Attribute object that supports units and comparison
This commit is contained in:
commit
6b11abaccd
|
@ -77,6 +77,11 @@ func TimeToPtr(t time.Duration) *time.Duration {
|
|||
return &t
|
||||
}
|
||||
|
||||
// Float64ToPtr returns the pointer to an float64
|
||||
func Float64ToPtr(f float64) *float64 {
|
||||
return &f
|
||||
}
|
||||
|
||||
func IntMin(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
|
|
379
plugins/shared/structs/attribute.go
Normal file
379
plugins/shared/structs/attribute.go
Normal file
|
@ -0,0 +1,379 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/hashicorp/nomad/helper"
|
||||
)
|
||||
|
||||
const (
|
||||
// floatPrecision is the precision used before rounding. It is set to a high
|
||||
// number to give a high chance of correctly returning equality.
|
||||
floatPrecision = uint(256)
|
||||
)
|
||||
|
||||
// BaseUnit is a unique base unit. All units that share the same base unit
|
||||
// should be comparable.
|
||||
type BaseUnit uint16
|
||||
|
||||
const (
|
||||
UnitScalar BaseUnit = iota
|
||||
UnitByte
|
||||
UnitByteRate
|
||||
UnitHertz
|
||||
UnitWatt
|
||||
)
|
||||
|
||||
// Unit describes a unit and its multiplier over the base unit type
|
||||
type Unit struct {
|
||||
// Name is the name of the unit (GiB, MB/s)
|
||||
Name string
|
||||
|
||||
// Base is the base unit for the unit
|
||||
Base BaseUnit
|
||||
|
||||
// Multiplier is the multiplier over the base unit (KiB multiplier is 1024)
|
||||
Multiplier int64
|
||||
|
||||
// InverseMultiplier specifies that the multiplier is an inverse so:
|
||||
// Base / Multiplier. For example a mW is a W/1000.
|
||||
InverseMultiplier bool
|
||||
}
|
||||
|
||||
// Comparable returns if two units are comparable
|
||||
func (u *Unit) Comparable(o *Unit) bool {
|
||||
if u == nil || o == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return u.Base == o.Base
|
||||
}
|
||||
|
||||
// Attribute is used to describe the value of an attribute, optionally
|
||||
// specifying units
|
||||
type Attribute struct {
|
||||
// Float is the float value for the attribute
|
||||
Float *float64
|
||||
|
||||
// Int is the int value for the attribute
|
||||
Int *int64
|
||||
|
||||
// String is the string value for the attribute
|
||||
String *string
|
||||
|
||||
// Bool is the bool value for the attribute
|
||||
Bool *bool
|
||||
|
||||
// Unit is the optional unit for the set int or float value
|
||||
Unit string
|
||||
}
|
||||
|
||||
// GoString returns a string representation of the attribute
|
||||
func (a *Attribute) GoString() string {
|
||||
if a == nil {
|
||||
return "nil attribute"
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
if a.Float != nil {
|
||||
b.WriteString(fmt.Sprintf("%v", *a.Float))
|
||||
} else if a.Int != nil {
|
||||
b.WriteString(fmt.Sprintf("%v", *a.Int))
|
||||
} else if a.Bool != nil {
|
||||
b.WriteString(fmt.Sprintf("%v", *a.Bool))
|
||||
} else if a.String != nil {
|
||||
b.WriteString(*a.String)
|
||||
}
|
||||
|
||||
if a.Unit != "" {
|
||||
b.WriteString(a.Unit)
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// Validate checks if the attribute is valid
|
||||
func (a *Attribute) Validate() error {
|
||||
if a.Unit != "" {
|
||||
if _, ok := UnitIndex[a.Unit]; !ok {
|
||||
return fmt.Errorf("unrecognized unit %q", a.Unit)
|
||||
}
|
||||
|
||||
// Check only int/float set
|
||||
if a.String != nil || a.Bool != nil {
|
||||
return fmt.Errorf("unit can not be specified on a boolean or string attribute")
|
||||
}
|
||||
}
|
||||
|
||||
// Assert only one of the attributes is set
|
||||
set := 0
|
||||
if a.Float != nil {
|
||||
set++
|
||||
}
|
||||
if a.Int != nil {
|
||||
set++
|
||||
}
|
||||
if a.String != nil {
|
||||
set++
|
||||
}
|
||||
if a.Bool != nil {
|
||||
set++
|
||||
}
|
||||
|
||||
if set == 0 {
|
||||
return fmt.Errorf("no attribute value set")
|
||||
} else if set > 1 {
|
||||
return fmt.Errorf("only one attribute value may be set")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Compare compares two attributes. If the returned boolean value is false, it
|
||||
// means the values are not comparable, either because they are of different
|
||||
// types (bool versus int) or the units are incompatible for comparison.
|
||||
// The returned int will be 0 if a==b, -1 if a < b, and +1 if a > b for all
|
||||
// values but bool. For bool it will be 0 if a==b or 1 if a!=b.
|
||||
func (a *Attribute) Compare(b *Attribute) (int, bool) {
|
||||
if !a.Comparable(b) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return a.comparator()(b)
|
||||
}
|
||||
|
||||
// comparator returns the comparator function for the attribute
|
||||
func (a *Attribute) comparator() compareFn {
|
||||
if a.Bool != nil {
|
||||
return a.boolComparator
|
||||
}
|
||||
if a.String != nil {
|
||||
return a.stringComparator
|
||||
}
|
||||
if a.Int != nil || a.Float != nil {
|
||||
return a.numberComparator
|
||||
}
|
||||
|
||||
return nullComparator
|
||||
}
|
||||
|
||||
// boolComparator compares two boolean attributes
|
||||
func (a *Attribute) boolComparator(b *Attribute) (int, bool) {
|
||||
if *a.Bool == *b.Bool {
|
||||
return 0, true
|
||||
}
|
||||
|
||||
return 1, true
|
||||
}
|
||||
|
||||
// stringComparator compares two string attributes
|
||||
func (a *Attribute) stringComparator(b *Attribute) (int, bool) {
|
||||
return strings.Compare(*a.String, *b.String), true
|
||||
}
|
||||
|
||||
// numberComparator compares two number attributes, having either Int or Float
|
||||
// set.
|
||||
func (a *Attribute) numberComparator(b *Attribute) (int, bool) {
|
||||
// If they are both integers we do perfect precision comparisons
|
||||
if a.Int != nil && b.Int != nil {
|
||||
return a.intComparator(b)
|
||||
}
|
||||
|
||||
// Push both into the float space
|
||||
af := a.getBigFloat()
|
||||
bf := b.getBigFloat()
|
||||
if af == nil || bf == nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return af.Cmp(bf), true
|
||||
}
|
||||
|
||||
// intComparator compares two integer attributes.
|
||||
func (a *Attribute) intComparator(b *Attribute) (int, bool) {
|
||||
ai := a.getInt()
|
||||
bi := b.getInt()
|
||||
|
||||
if ai == bi {
|
||||
return 0, true
|
||||
} else if ai < bi {
|
||||
return -1, true
|
||||
} else {
|
||||
return 1, true
|
||||
}
|
||||
}
|
||||
|
||||
// nullComparator always returns false and is used when no comparison function
|
||||
// is possible
|
||||
func nullComparator(*Attribute) (int, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// compareFn is used to compare two attributes. It returns -1, 0, 1 for ordering
|
||||
// and a boolean for if the comparison is possible.
|
||||
type compareFn func(b *Attribute) (int, bool)
|
||||
|
||||
// getBigFloat returns a big.Float representation of the attribute, converting
|
||||
// the value to the base unit if a unit is specified.
|
||||
func (a *Attribute) getBigFloat() *big.Float {
|
||||
f := new(big.Float)
|
||||
f.SetPrec(floatPrecision)
|
||||
if a.Int != nil {
|
||||
f.SetInt64(*a.Int)
|
||||
} else if a.Float != nil {
|
||||
f.SetFloat64(*a.Float)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the unit
|
||||
u := a.getTypedUnit()
|
||||
|
||||
// If there is no unit just return the float
|
||||
if u == nil {
|
||||
return f
|
||||
}
|
||||
|
||||
// Convert to the base unit
|
||||
multiplier := new(big.Float)
|
||||
multiplier.SetPrec(floatPrecision)
|
||||
multiplier.SetInt64(u.Multiplier)
|
||||
if u.InverseMultiplier {
|
||||
base := big.NewFloat(1.0)
|
||||
base.SetPrec(floatPrecision)
|
||||
multiplier = multiplier.Quo(base, multiplier)
|
||||
}
|
||||
|
||||
f.Mul(f, multiplier)
|
||||
return f
|
||||
}
|
||||
|
||||
// getInt returns an int representation of the attribute, converting
|
||||
// the value to the base unit if a unit is specified.
|
||||
func (a *Attribute) getInt() int64 {
|
||||
if a.Int == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
i := *a.Int
|
||||
|
||||
// Get the unit
|
||||
u := a.getTypedUnit()
|
||||
|
||||
// If there is no unit just return the int
|
||||
if u == nil {
|
||||
return i
|
||||
}
|
||||
|
||||
if u.InverseMultiplier {
|
||||
i /= u.Multiplier
|
||||
} else {
|
||||
i *= u.Multiplier
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
// Comparable returns whether they are comparable
|
||||
func (a *Attribute) Comparable(b *Attribute) bool {
|
||||
if a == nil || b == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// First use the units to decide if comparison is possible
|
||||
aUnit := a.getTypedUnit()
|
||||
bUnit := b.getTypedUnit()
|
||||
if aUnit != nil && bUnit != nil {
|
||||
return aUnit.Comparable(bUnit)
|
||||
} else if aUnit != nil && bUnit == nil {
|
||||
return false
|
||||
} else if aUnit == nil && bUnit != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if a.String != nil {
|
||||
if b.String != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
if a.Bool != nil {
|
||||
if b.Bool != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// getTypedUnit returns the Unit for the attribute or nil if no unit exists.
|
||||
func (a *Attribute) getTypedUnit() *Unit {
|
||||
return UnitIndex[a.Unit]
|
||||
}
|
||||
|
||||
// ParseAttribute takes a string and parses it into an attribute, pulling out
|
||||
// units if they are specified as a suffix on a number
|
||||
func ParseAttribute(input string) *Attribute {
|
||||
ll := len(input)
|
||||
if ll == 0 {
|
||||
return &Attribute{String: helper.StringToPtr(input)}
|
||||
}
|
||||
|
||||
// Try to parse as a bool
|
||||
b, err := strconv.ParseBool(input)
|
||||
if err == nil {
|
||||
return &Attribute{Bool: helper.BoolToPtr(b)}
|
||||
}
|
||||
|
||||
// Check if the string is a number ending with potential units
|
||||
if unicode.IsLetter(rune(input[ll-1])) {
|
||||
// Try suffix matching
|
||||
var unit string
|
||||
for _, u := range lengthSortedUnits {
|
||||
if strings.HasSuffix(input, u) {
|
||||
unit = u
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we know about the unit. If we don't we can only treat this
|
||||
// as a string
|
||||
if len(unit) == 0 {
|
||||
return &Attribute{String: helper.StringToPtr(input)}
|
||||
}
|
||||
|
||||
// Grab the numeric
|
||||
numeric := strings.TrimSpace(strings.TrimSuffix(input, unit))
|
||||
|
||||
// Try to parse as an int
|
||||
i, err := strconv.ParseInt(numeric, 10, 64)
|
||||
if err == nil {
|
||||
return &Attribute{Int: helper.Int64ToPtr(i), Unit: unit}
|
||||
}
|
||||
|
||||
// Try to parse as a float
|
||||
f, err := strconv.ParseFloat(numeric, 64)
|
||||
if err == nil {
|
||||
return &Attribute{Float: helper.Float64ToPtr(f), Unit: unit}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to parse as an int
|
||||
i, err := strconv.ParseInt(input, 10, 64)
|
||||
if err == nil {
|
||||
return &Attribute{Int: helper.Int64ToPtr(i)}
|
||||
}
|
||||
|
||||
// Try to parse as a float
|
||||
f, err := strconv.ParseFloat(input, 64)
|
||||
if err == nil {
|
||||
return &Attribute{Float: helper.Float64ToPtr(f)}
|
||||
}
|
||||
|
||||
return &Attribute{String: helper.StringToPtr(input)}
|
||||
}
|
687
plugins/shared/structs/attribute_test.go
Normal file
687
plugins/shared/structs/attribute_test.go
Normal file
|
@ -0,0 +1,687 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/nomad/helper"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAttribute_Validate(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input *Attribute
|
||||
Fail bool
|
||||
}{
|
||||
{
|
||||
Input: &Attribute{
|
||||
Bool: helper.BoolToPtr(true),
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: &Attribute{
|
||||
String: helper.StringToPtr("foo"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: &Attribute{
|
||||
Int: helper.Int64ToPtr(123),
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: &Attribute{
|
||||
Float: helper.Float64ToPtr(123.2),
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: &Attribute{
|
||||
Bool: helper.BoolToPtr(true),
|
||||
Unit: "MB",
|
||||
},
|
||||
Fail: true,
|
||||
},
|
||||
{
|
||||
Input: &Attribute{
|
||||
String: helper.StringToPtr("foo"),
|
||||
Unit: "MB",
|
||||
},
|
||||
Fail: true,
|
||||
},
|
||||
{
|
||||
Input: &Attribute{
|
||||
Int: helper.Int64ToPtr(123),
|
||||
Unit: "lolNO",
|
||||
},
|
||||
Fail: true,
|
||||
},
|
||||
{
|
||||
Input: &Attribute{
|
||||
Float: helper.Float64ToPtr(123.2),
|
||||
Unit: "lolNO",
|
||||
},
|
||||
Fail: true,
|
||||
},
|
||||
{
|
||||
Input: &Attribute{
|
||||
Int: helper.Int64ToPtr(123),
|
||||
Float: helper.Float64ToPtr(123.2),
|
||||
Unit: "mW",
|
||||
},
|
||||
Fail: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.Input.GoString(), func(t *testing.T) {
|
||||
if err := c.Input.Validate(); err != nil && !c.Fail {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type compareTestCase struct {
|
||||
A *Attribute
|
||||
B *Attribute
|
||||
Expected int
|
||||
NotComparable bool
|
||||
}
|
||||
|
||||
func TestAttribute_Compare_Bool(t *testing.T) {
|
||||
cases := []*compareTestCase{
|
||||
{
|
||||
A: &Attribute{
|
||||
Bool: helper.BoolToPtr(true),
|
||||
},
|
||||
B: &Attribute{
|
||||
Bool: helper.BoolToPtr(true),
|
||||
},
|
||||
Expected: 0,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Bool: helper.BoolToPtr(true),
|
||||
},
|
||||
B: &Attribute{
|
||||
Bool: helper.BoolToPtr(false),
|
||||
},
|
||||
Expected: 1,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Bool: helper.BoolToPtr(true),
|
||||
},
|
||||
B: &Attribute{
|
||||
String: helper.StringToPtr("foo"),
|
||||
},
|
||||
NotComparable: true,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Bool: helper.BoolToPtr(true),
|
||||
},
|
||||
B: &Attribute{
|
||||
Int: helper.Int64ToPtr(123),
|
||||
},
|
||||
NotComparable: true,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Bool: helper.BoolToPtr(true),
|
||||
},
|
||||
B: &Attribute{
|
||||
Float: helper.Float64ToPtr(123.2),
|
||||
},
|
||||
NotComparable: true,
|
||||
},
|
||||
}
|
||||
testComparison(t, cases)
|
||||
}
|
||||
|
||||
func TestAttribute_Compare_String(t *testing.T) {
|
||||
cases := []*compareTestCase{
|
||||
{
|
||||
A: &Attribute{
|
||||
String: helper.StringToPtr("a"),
|
||||
},
|
||||
B: &Attribute{
|
||||
String: helper.StringToPtr("b"),
|
||||
},
|
||||
Expected: -1,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
String: helper.StringToPtr("hello"),
|
||||
},
|
||||
B: &Attribute{
|
||||
String: helper.StringToPtr("hello"),
|
||||
},
|
||||
Expected: 0,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
String: helper.StringToPtr("b"),
|
||||
},
|
||||
B: &Attribute{
|
||||
String: helper.StringToPtr("a"),
|
||||
},
|
||||
Expected: 1,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
String: helper.StringToPtr("hello"),
|
||||
},
|
||||
B: &Attribute{
|
||||
Bool: helper.BoolToPtr(true),
|
||||
},
|
||||
NotComparable: true,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
String: helper.StringToPtr("hello"),
|
||||
},
|
||||
B: &Attribute{
|
||||
Int: helper.Int64ToPtr(123),
|
||||
},
|
||||
NotComparable: true,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
String: helper.StringToPtr("hello"),
|
||||
},
|
||||
B: &Attribute{
|
||||
Float: helper.Float64ToPtr(123.2),
|
||||
},
|
||||
NotComparable: true,
|
||||
},
|
||||
}
|
||||
testComparison(t, cases)
|
||||
}
|
||||
|
||||
func TestAttribute_Compare_Float(t *testing.T) {
|
||||
cases := []*compareTestCase{
|
||||
{
|
||||
A: &Attribute{
|
||||
Float: helper.Float64ToPtr(101.5),
|
||||
},
|
||||
B: &Attribute{
|
||||
Float: helper.Float64ToPtr(100001.5),
|
||||
},
|
||||
Expected: -1,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Float: helper.Float64ToPtr(100001.5),
|
||||
},
|
||||
B: &Attribute{
|
||||
Float: helper.Float64ToPtr(100001.5),
|
||||
},
|
||||
Expected: 0,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Float: helper.Float64ToPtr(999999999.5),
|
||||
},
|
||||
B: &Attribute{
|
||||
Float: helper.Float64ToPtr(101.5),
|
||||
},
|
||||
Expected: 1,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Float: helper.Float64ToPtr(101.5),
|
||||
},
|
||||
B: &Attribute{
|
||||
Bool: helper.BoolToPtr(true),
|
||||
},
|
||||
NotComparable: true,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Float: helper.Float64ToPtr(101.5),
|
||||
},
|
||||
B: &Attribute{
|
||||
String: helper.StringToPtr("hello"),
|
||||
},
|
||||
NotComparable: true,
|
||||
},
|
||||
}
|
||||
testComparison(t, cases)
|
||||
}
|
||||
|
||||
func TestAttribute_Compare_Int(t *testing.T) {
|
||||
cases := []*compareTestCase{
|
||||
{
|
||||
A: &Attribute{
|
||||
Int: helper.Int64ToPtr(3),
|
||||
},
|
||||
B: &Attribute{
|
||||
Int: helper.Int64ToPtr(10),
|
||||
},
|
||||
Expected: -1,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Int: helper.Int64ToPtr(10),
|
||||
},
|
||||
B: &Attribute{
|
||||
Int: helper.Int64ToPtr(10),
|
||||
},
|
||||
Expected: 0,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Int: helper.Int64ToPtr(100),
|
||||
},
|
||||
B: &Attribute{
|
||||
Int: helper.Int64ToPtr(10),
|
||||
},
|
||||
Expected: 1,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Int: helper.Int64ToPtr(10),
|
||||
},
|
||||
B: &Attribute{
|
||||
Bool: helper.BoolToPtr(true),
|
||||
},
|
||||
NotComparable: true,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Int: helper.Int64ToPtr(10),
|
||||
},
|
||||
B: &Attribute{
|
||||
String: helper.StringToPtr("hello"),
|
||||
},
|
||||
NotComparable: true,
|
||||
},
|
||||
}
|
||||
testComparison(t, cases)
|
||||
}
|
||||
|
||||
func TestAttribute_Compare_Int_With_Units(t *testing.T) {
|
||||
cases := []*compareTestCase{
|
||||
{
|
||||
A: &Attribute{
|
||||
Int: helper.Int64ToPtr(3),
|
||||
Unit: "MB",
|
||||
},
|
||||
B: &Attribute{
|
||||
Int: helper.Int64ToPtr(10),
|
||||
Unit: "MB",
|
||||
},
|
||||
Expected: -1,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Int: helper.Int64ToPtr(10),
|
||||
Unit: "MB",
|
||||
},
|
||||
B: &Attribute{
|
||||
Int: helper.Int64ToPtr(10),
|
||||
Unit: "MB",
|
||||
},
|
||||
Expected: 0,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Int: helper.Int64ToPtr(100),
|
||||
Unit: "MB",
|
||||
},
|
||||
B: &Attribute{
|
||||
Int: helper.Int64ToPtr(10),
|
||||
Unit: "MB",
|
||||
},
|
||||
Expected: 1,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Int: helper.Int64ToPtr(3),
|
||||
Unit: "GB",
|
||||
},
|
||||
B: &Attribute{
|
||||
Int: helper.Int64ToPtr(3),
|
||||
Unit: "MB",
|
||||
},
|
||||
Expected: 1,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Int: helper.Int64ToPtr(1),
|
||||
Unit: "GiB",
|
||||
},
|
||||
B: &Attribute{
|
||||
Int: helper.Int64ToPtr(1024),
|
||||
Unit: "MiB",
|
||||
},
|
||||
Expected: 0,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Int: helper.Int64ToPtr(1),
|
||||
Unit: "GiB",
|
||||
},
|
||||
B: &Attribute{
|
||||
Int: helper.Int64ToPtr(1025),
|
||||
Unit: "MiB",
|
||||
},
|
||||
Expected: -1,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Int: helper.Int64ToPtr(1000),
|
||||
Unit: "mW",
|
||||
},
|
||||
B: &Attribute{
|
||||
Int: helper.Int64ToPtr(1),
|
||||
Unit: "W",
|
||||
},
|
||||
Expected: 0,
|
||||
},
|
||||
}
|
||||
testComparison(t, cases)
|
||||
}
|
||||
|
||||
func TestAttribute_Compare_Float_With_Units(t *testing.T) {
|
||||
cases := []*compareTestCase{
|
||||
{
|
||||
A: &Attribute{
|
||||
Float: helper.Float64ToPtr(3.0),
|
||||
Unit: "MB",
|
||||
},
|
||||
B: &Attribute{
|
||||
Float: helper.Float64ToPtr(10.0),
|
||||
Unit: "MB",
|
||||
},
|
||||
Expected: -1,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Float: helper.Float64ToPtr(10.0),
|
||||
Unit: "MB",
|
||||
},
|
||||
B: &Attribute{
|
||||
Float: helper.Float64ToPtr(10.0),
|
||||
Unit: "MB",
|
||||
},
|
||||
Expected: 0,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Float: helper.Float64ToPtr(100.0),
|
||||
Unit: "MB",
|
||||
},
|
||||
B: &Attribute{
|
||||
Float: helper.Float64ToPtr(10.0),
|
||||
Unit: "MB",
|
||||
},
|
||||
Expected: 1,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Float: helper.Float64ToPtr(3.0),
|
||||
Unit: "GB",
|
||||
},
|
||||
B: &Attribute{
|
||||
Float: helper.Float64ToPtr(3.0),
|
||||
Unit: "MB",
|
||||
},
|
||||
Expected: 1,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Float: helper.Float64ToPtr(1.0),
|
||||
Unit: "GiB",
|
||||
},
|
||||
B: &Attribute{
|
||||
Float: helper.Float64ToPtr(1024.0),
|
||||
Unit: "MiB",
|
||||
},
|
||||
Expected: 0,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Float: helper.Float64ToPtr(1.0),
|
||||
Unit: "GiB",
|
||||
},
|
||||
B: &Attribute{
|
||||
Float: helper.Float64ToPtr(1025.0),
|
||||
Unit: "MiB",
|
||||
},
|
||||
Expected: -1,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Float: helper.Float64ToPtr(1000.0),
|
||||
Unit: "mW",
|
||||
},
|
||||
B: &Attribute{
|
||||
Float: helper.Float64ToPtr(1.0),
|
||||
Unit: "W",
|
||||
},
|
||||
Expected: 0,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Float: helper.Float64ToPtr(1.5),
|
||||
Unit: "GiB",
|
||||
},
|
||||
B: &Attribute{
|
||||
Float: helper.Float64ToPtr(1400.0),
|
||||
Unit: "MiB",
|
||||
},
|
||||
Expected: 1,
|
||||
},
|
||||
}
|
||||
testComparison(t, cases)
|
||||
}
|
||||
|
||||
func TestAttribute_Compare_IntToFloat(t *testing.T) {
|
||||
cases := []*compareTestCase{
|
||||
{
|
||||
A: &Attribute{
|
||||
Int: helper.Int64ToPtr(3),
|
||||
},
|
||||
B: &Attribute{
|
||||
Float: helper.Float64ToPtr(10.0),
|
||||
},
|
||||
Expected: -1,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Int: helper.Int64ToPtr(10),
|
||||
},
|
||||
B: &Attribute{
|
||||
Float: helper.Float64ToPtr(10.0),
|
||||
},
|
||||
Expected: 0,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Int: helper.Int64ToPtr(10),
|
||||
},
|
||||
B: &Attribute{
|
||||
Float: helper.Float64ToPtr(10.1),
|
||||
},
|
||||
Expected: -1,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Int: helper.Int64ToPtr(100),
|
||||
},
|
||||
B: &Attribute{
|
||||
Float: helper.Float64ToPtr(10.0),
|
||||
},
|
||||
Expected: 1,
|
||||
},
|
||||
{
|
||||
A: &Attribute{
|
||||
Int: helper.Int64ToPtr(100),
|
||||
},
|
||||
B: &Attribute{
|
||||
Float: helper.Float64ToPtr(100.00001),
|
||||
},
|
||||
Expected: -1,
|
||||
},
|
||||
}
|
||||
testComparison(t, cases)
|
||||
}
|
||||
|
||||
func testComparison(t *testing.T, cases []*compareTestCase) {
|
||||
for _, c := range cases {
|
||||
t.Run(fmt.Sprintf("%#v vs %#v", c.A, c.B), func(t *testing.T) {
|
||||
v, ok := c.A.Compare(c.B)
|
||||
if !ok && !c.NotComparable {
|
||||
t.Fatal("should be comparable")
|
||||
} else if ok {
|
||||
require.Equal(t, c.Expected, v)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttribute_ParseAndValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input string
|
||||
Expected *Attribute
|
||||
}{
|
||||
{
|
||||
Input: "true",
|
||||
Expected: &Attribute{
|
||||
Bool: helper.BoolToPtr(true),
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: "false",
|
||||
Expected: &Attribute{
|
||||
Bool: helper.BoolToPtr(false),
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: "100",
|
||||
Expected: &Attribute{
|
||||
Int: helper.Int64ToPtr(100),
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: "-100",
|
||||
Expected: &Attribute{
|
||||
Int: helper.Int64ToPtr(-100),
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: "-1.0",
|
||||
Expected: &Attribute{
|
||||
Float: helper.Float64ToPtr(-1.0),
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: "-100.25",
|
||||
Expected: &Attribute{
|
||||
Float: helper.Float64ToPtr(-100.25),
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: "1.01",
|
||||
Expected: &Attribute{
|
||||
Float: helper.Float64ToPtr(1.01),
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: "100.25",
|
||||
Expected: &Attribute{
|
||||
Float: helper.Float64ToPtr(100.25),
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: "foobar",
|
||||
Expected: &Attribute{
|
||||
String: helper.StringToPtr("foobar"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: "foo123bar",
|
||||
Expected: &Attribute{
|
||||
String: helper.StringToPtr("foo123bar"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: "100MB",
|
||||
Expected: &Attribute{
|
||||
Int: helper.Int64ToPtr(100),
|
||||
Unit: "MB",
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: "-100MHz",
|
||||
Expected: &Attribute{
|
||||
Int: helper.Int64ToPtr(-100),
|
||||
Unit: "MHz",
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: "-1.0MB/s",
|
||||
Expected: &Attribute{
|
||||
Float: helper.Float64ToPtr(-1.0),
|
||||
Unit: "MB/s",
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: "-100.25GiB/s",
|
||||
Expected: &Attribute{
|
||||
Float: helper.Float64ToPtr(-100.25),
|
||||
Unit: "GiB/s",
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: "1.01TB",
|
||||
Expected: &Attribute{
|
||||
Float: helper.Float64ToPtr(1.01),
|
||||
Unit: "TB",
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: "100.25mW",
|
||||
Expected: &Attribute{
|
||||
Float: helper.Float64ToPtr(100.25),
|
||||
Unit: "mW",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.Input, func(t *testing.T) {
|
||||
a := ParseAttribute(c.Input)
|
||||
require.Equal(t, c.Expected, a)
|
||||
require.NoError(t, a.Validate())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParse(b *testing.B) {
|
||||
cases := []string{
|
||||
"true",
|
||||
"false",
|
||||
"100",
|
||||
"-100",
|
||||
"-1.0",
|
||||
"-100.25",
|
||||
"1.01",
|
||||
"100.25",
|
||||
"foobar",
|
||||
"foo123bar",
|
||||
"100MB",
|
||||
"-100MHz",
|
||||
"-1.0MB/s",
|
||||
"-100.25GiB/s",
|
||||
"1.01TB",
|
||||
"100.25mW",
|
||||
}
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
for _, c := range cases {
|
||||
ParseAttribute(c)
|
||||
}
|
||||
}
|
||||
}
|
250
plugins/shared/structs/proto/attribute.pb.go
Normal file
250
plugins/shared/structs/proto/attribute.pb.go
Normal file
|
@ -0,0 +1,250 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: attribute.proto
|
||||
|
||||
package proto
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// Attribute is used to describe the value of an attribute, optionally
|
||||
// specifying units
|
||||
type Attribute struct {
|
||||
// Types that are valid to be assigned to Value:
|
||||
// *Attribute_FloatVal
|
||||
// *Attribute_IntVal
|
||||
// *Attribute_StringVal
|
||||
// *Attribute_BoolVal
|
||||
Value isAttribute_Value `protobuf_oneof:"value"`
|
||||
// unit gives the unit type: MHz, MB, etc.
|
||||
Unit string `protobuf:"bytes,5,opt,name=unit,proto3" json:"unit,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Attribute) Reset() { *m = Attribute{} }
|
||||
func (m *Attribute) String() string { return proto.CompactTextString(m) }
|
||||
func (*Attribute) ProtoMessage() {}
|
||||
func (*Attribute) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_attribute_bfac9d16cf08e8f5, []int{0}
|
||||
}
|
||||
func (m *Attribute) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Attribute.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Attribute) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Attribute.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *Attribute) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Attribute.Merge(dst, src)
|
||||
}
|
||||
func (m *Attribute) XXX_Size() int {
|
||||
return xxx_messageInfo_Attribute.Size(m)
|
||||
}
|
||||
func (m *Attribute) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Attribute.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Attribute proto.InternalMessageInfo
|
||||
|
||||
type isAttribute_Value interface {
|
||||
isAttribute_Value()
|
||||
}
|
||||
|
||||
type Attribute_FloatVal struct {
|
||||
FloatVal float64 `protobuf:"fixed64,1,opt,name=float_val,json=floatVal,proto3,oneof"`
|
||||
}
|
||||
|
||||
type Attribute_IntVal struct {
|
||||
IntVal int64 `protobuf:"varint,2,opt,name=int_val,json=intVal,proto3,oneof"`
|
||||
}
|
||||
|
||||
type Attribute_StringVal struct {
|
||||
StringVal string `protobuf:"bytes,3,opt,name=string_val,json=stringVal,proto3,oneof"`
|
||||
}
|
||||
|
||||
type Attribute_BoolVal struct {
|
||||
BoolVal bool `protobuf:"varint,4,opt,name=bool_val,json=boolVal,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*Attribute_FloatVal) isAttribute_Value() {}
|
||||
|
||||
func (*Attribute_IntVal) isAttribute_Value() {}
|
||||
|
||||
func (*Attribute_StringVal) isAttribute_Value() {}
|
||||
|
||||
func (*Attribute_BoolVal) isAttribute_Value() {}
|
||||
|
||||
func (m *Attribute) GetValue() isAttribute_Value {
|
||||
if m != nil {
|
||||
return m.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Attribute) GetFloatVal() float64 {
|
||||
if x, ok := m.GetValue().(*Attribute_FloatVal); ok {
|
||||
return x.FloatVal
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Attribute) GetIntVal() int64 {
|
||||
if x, ok := m.GetValue().(*Attribute_IntVal); ok {
|
||||
return x.IntVal
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Attribute) GetStringVal() string {
|
||||
if x, ok := m.GetValue().(*Attribute_StringVal); ok {
|
||||
return x.StringVal
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Attribute) GetBoolVal() bool {
|
||||
if x, ok := m.GetValue().(*Attribute_BoolVal); ok {
|
||||
return x.BoolVal
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Attribute) GetUnit() string {
|
||||
if m != nil {
|
||||
return m.Unit
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// XXX_OneofFuncs is for the internal use of the proto package.
|
||||
func (*Attribute) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
|
||||
return _Attribute_OneofMarshaler, _Attribute_OneofUnmarshaler, _Attribute_OneofSizer, []interface{}{
|
||||
(*Attribute_FloatVal)(nil),
|
||||
(*Attribute_IntVal)(nil),
|
||||
(*Attribute_StringVal)(nil),
|
||||
(*Attribute_BoolVal)(nil),
|
||||
}
|
||||
}
|
||||
|
||||
func _Attribute_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
|
||||
m := msg.(*Attribute)
|
||||
// value
|
||||
switch x := m.Value.(type) {
|
||||
case *Attribute_FloatVal:
|
||||
b.EncodeVarint(1<<3 | proto.WireFixed64)
|
||||
b.EncodeFixed64(math.Float64bits(x.FloatVal))
|
||||
case *Attribute_IntVal:
|
||||
b.EncodeVarint(2<<3 | proto.WireVarint)
|
||||
b.EncodeVarint(uint64(x.IntVal))
|
||||
case *Attribute_StringVal:
|
||||
b.EncodeVarint(3<<3 | proto.WireBytes)
|
||||
b.EncodeStringBytes(x.StringVal)
|
||||
case *Attribute_BoolVal:
|
||||
t := uint64(0)
|
||||
if x.BoolVal {
|
||||
t = 1
|
||||
}
|
||||
b.EncodeVarint(4<<3 | proto.WireVarint)
|
||||
b.EncodeVarint(t)
|
||||
case nil:
|
||||
default:
|
||||
return fmt.Errorf("Attribute.Value has unexpected type %T", x)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func _Attribute_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
|
||||
m := msg.(*Attribute)
|
||||
switch tag {
|
||||
case 1: // value.float_val
|
||||
if wire != proto.WireFixed64 {
|
||||
return true, proto.ErrInternalBadWireType
|
||||
}
|
||||
x, err := b.DecodeFixed64()
|
||||
m.Value = &Attribute_FloatVal{math.Float64frombits(x)}
|
||||
return true, err
|
||||
case 2: // value.int_val
|
||||
if wire != proto.WireVarint {
|
||||
return true, proto.ErrInternalBadWireType
|
||||
}
|
||||
x, err := b.DecodeVarint()
|
||||
m.Value = &Attribute_IntVal{int64(x)}
|
||||
return true, err
|
||||
case 3: // value.string_val
|
||||
if wire != proto.WireBytes {
|
||||
return true, proto.ErrInternalBadWireType
|
||||
}
|
||||
x, err := b.DecodeStringBytes()
|
||||
m.Value = &Attribute_StringVal{x}
|
||||
return true, err
|
||||
case 4: // value.bool_val
|
||||
if wire != proto.WireVarint {
|
||||
return true, proto.ErrInternalBadWireType
|
||||
}
|
||||
x, err := b.DecodeVarint()
|
||||
m.Value = &Attribute_BoolVal{x != 0}
|
||||
return true, err
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
func _Attribute_OneofSizer(msg proto.Message) (n int) {
|
||||
m := msg.(*Attribute)
|
||||
// value
|
||||
switch x := m.Value.(type) {
|
||||
case *Attribute_FloatVal:
|
||||
n += 1 // tag and wire
|
||||
n += 8
|
||||
case *Attribute_IntVal:
|
||||
n += 1 // tag and wire
|
||||
n += proto.SizeVarint(uint64(x.IntVal))
|
||||
case *Attribute_StringVal:
|
||||
n += 1 // tag and wire
|
||||
n += proto.SizeVarint(uint64(len(x.StringVal)))
|
||||
n += len(x.StringVal)
|
||||
case *Attribute_BoolVal:
|
||||
n += 1 // tag and wire
|
||||
n += 1
|
||||
case nil:
|
||||
default:
|
||||
panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Attribute)(nil), "hashicorp.nomad.plugins.shared.structs.Attribute")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("attribute.proto", fileDescriptor_attribute_bfac9d16cf08e8f5) }
|
||||
|
||||
var fileDescriptor_attribute_bfac9d16cf08e8f5 = []byte{
|
||||
// 209 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x34, 0xcf, 0xb1, 0x4e, 0xc3, 0x30,
|
||||
0x10, 0x06, 0xe0, 0x98, 0x36, 0x4d, 0x7c, 0x0b, 0x92, 0xa7, 0x22, 0x84, 0xb0, 0x18, 0x90, 0x27,
|
||||
0x2f, 0x3c, 0x01, 0x9d, 0x3c, 0x7b, 0x60, 0x60, 0x41, 0x97, 0x36, 0x34, 0x96, 0x8c, 0x1d, 0xd9,
|
||||
0xe7, 0x3e, 0x0f, 0x8f, 0x8a, 0xec, 0x88, 0xe9, 0x4e, 0xff, 0x77, 0xff, 0x70, 0x70, 0x8f, 0x44,
|
||||
0xc9, 0x4d, 0x85, 0x66, 0xbd, 0xa6, 0x48, 0x51, 0xbc, 0x2e, 0x98, 0x17, 0x77, 0x8e, 0x69, 0xd5,
|
||||
0x21, 0xfe, 0xe0, 0x45, 0xaf, 0xbe, 0x5c, 0x5d, 0xc8, 0x3a, 0x2f, 0x98, 0xe6, 0x8b, 0xce, 0x94,
|
||||
0xca, 0x99, 0xf2, 0xcb, 0x2f, 0x03, 0xfe, 0xfe, 0xdf, 0x15, 0x4f, 0xc0, 0xbf, 0x7d, 0x44, 0xfa,
|
||||
0xba, 0xa1, 0x3f, 0x32, 0xc9, 0x14, 0x33, 0x9d, 0x1d, 0x5b, 0xf4, 0x81, 0x5e, 0x3c, 0xc0, 0xe0,
|
||||
0xc2, 0x86, 0x77, 0x92, 0xa9, 0x9d, 0xe9, 0xec, 0xc1, 0x85, 0x46, 0xcf, 0x00, 0x99, 0x92, 0x0b,
|
||||
0xd7, 0xa6, 0x3b, 0xc9, 0x14, 0x37, 0x9d, 0xe5, 0x5b, 0x56, 0x0f, 0x1e, 0x61, 0x9c, 0x62, 0xf4,
|
||||
0x8d, 0xf7, 0x92, 0xa9, 0xd1, 0x74, 0x76, 0xa8, 0x49, 0x45, 0x01, 0xfb, 0x12, 0x1c, 0x1d, 0xfb,
|
||||
0xda, 0xb3, 0x6d, 0x3f, 0x0d, 0xd0, 0xdf, 0xd0, 0x97, 0xf9, 0x34, 0x7c, 0xf6, 0xed, 0xa7, 0xe9,
|
||||
0xd0, 0xc6, 0xdb, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x79, 0x18, 0x15, 0x15, 0xed, 0x00, 0x00,
|
||||
0x00,
|
||||
}
|
25
plugins/shared/structs/proto/attribute.proto
Normal file
25
plugins/shared/structs/proto/attribute.proto
Normal file
|
@ -0,0 +1,25 @@
|
|||
syntax = "proto3";
|
||||
package hashicorp.nomad.plugins.shared.structs;
|
||||
option go_package = "proto";
|
||||
|
||||
// Attribute is used to describe the value of an attribute, optionally
|
||||
// specifying units
|
||||
message Attribute {
|
||||
oneof value {
|
||||
// float_val exposes a floating point value.
|
||||
double float_val = 1;
|
||||
|
||||
// int_numerator_val exposes a int value.
|
||||
int64 int_val = 2;
|
||||
|
||||
// string_val exposes a string value.
|
||||
string string_val = 3;
|
||||
|
||||
// bool_val exposes a boolean statistic.
|
||||
bool bool_val = 4;
|
||||
}
|
||||
|
||||
// unit gives the unit type: MHz, MB, etc.
|
||||
string unit = 5;
|
||||
}
|
||||
|
213
plugins/shared/structs/units.go
Normal file
213
plugins/shared/structs/units.go
Normal file
|
@ -0,0 +1,213 @@
|
|||
package structs
|
||||
|
||||
import "sort"
|
||||
|
||||
var (
|
||||
// numUnits is the number of known units
|
||||
numUnits = len(binarySIBytes) + len(decimalSIBytes) + len(binarySIByteRates) + len(decimalSIByteRates) + len(watts) + len(hertz)
|
||||
|
||||
// UnitIndex is a map of unit name to unit
|
||||
UnitIndex = make(map[string]*Unit, numUnits)
|
||||
|
||||
// lengthSortedUnits is a list of unit names sorted by length with longest
|
||||
// first
|
||||
lengthSortedUnits = make([]string, 0, numUnits)
|
||||
|
||||
binarySIBytes = []*Unit{
|
||||
{
|
||||
Name: "KiB",
|
||||
Base: UnitByte,
|
||||
Multiplier: 1 << 10,
|
||||
},
|
||||
{
|
||||
Name: "MiB",
|
||||
Base: UnitByte,
|
||||
Multiplier: 1 << 20,
|
||||
},
|
||||
{
|
||||
Name: "GiB",
|
||||
Base: UnitByte,
|
||||
Multiplier: 1 << 30,
|
||||
},
|
||||
{
|
||||
Name: "TiB",
|
||||
Base: UnitByte,
|
||||
Multiplier: 1 << 40,
|
||||
},
|
||||
{
|
||||
Name: "PiB",
|
||||
Base: UnitByte,
|
||||
Multiplier: 1 << 50,
|
||||
},
|
||||
{
|
||||
Name: "EiB",
|
||||
Base: UnitByte,
|
||||
Multiplier: 1 << 60,
|
||||
},
|
||||
}
|
||||
|
||||
decimalSIBytes = []*Unit{
|
||||
{
|
||||
Name: "kB",
|
||||
Base: UnitByte,
|
||||
Multiplier: Pow(1000, 1),
|
||||
},
|
||||
{
|
||||
Name: "KB", // Alternative name for kB
|
||||
Base: UnitByte,
|
||||
Multiplier: Pow(1000, 1),
|
||||
},
|
||||
{
|
||||
Name: "MB",
|
||||
Base: UnitByte,
|
||||
Multiplier: Pow(1000, 2),
|
||||
},
|
||||
{
|
||||
Name: "GB",
|
||||
Base: UnitByte,
|
||||
Multiplier: Pow(1000, 3),
|
||||
},
|
||||
{
|
||||
Name: "TB",
|
||||
Base: UnitByte,
|
||||
Multiplier: Pow(1000, 4),
|
||||
},
|
||||
{
|
||||
Name: "PB",
|
||||
Base: UnitByte,
|
||||
Multiplier: Pow(1000, 5),
|
||||
},
|
||||
{
|
||||
Name: "EB",
|
||||
Base: UnitByte,
|
||||
Multiplier: Pow(1000, 6),
|
||||
},
|
||||
}
|
||||
|
||||
binarySIByteRates = []*Unit{
|
||||
{
|
||||
Name: "KiB/s",
|
||||
Base: UnitByteRate,
|
||||
Multiplier: 1 << 10,
|
||||
},
|
||||
{
|
||||
Name: "MiB/s",
|
||||
Base: UnitByteRate,
|
||||
Multiplier: 1 << 20,
|
||||
},
|
||||
{
|
||||
Name: "GiB/s",
|
||||
Base: UnitByteRate,
|
||||
Multiplier: 1 << 30,
|
||||
},
|
||||
{
|
||||
Name: "TiB/s",
|
||||
Base: UnitByteRate,
|
||||
Multiplier: 1 << 40,
|
||||
},
|
||||
{
|
||||
Name: "PiB/s",
|
||||
Base: UnitByteRate,
|
||||
Multiplier: 1 << 50,
|
||||
},
|
||||
{
|
||||
Name: "EiB/s",
|
||||
Base: UnitByteRate,
|
||||
Multiplier: 1 << 60,
|
||||
},
|
||||
}
|
||||
|
||||
decimalSIByteRates = []*Unit{
|
||||
{
|
||||
Name: "kB/s",
|
||||
Base: UnitByteRate,
|
||||
Multiplier: Pow(1000, 1),
|
||||
},
|
||||
{
|
||||
Name: "KB/s", // Alternative name for kB/s
|
||||
Base: UnitByteRate,
|
||||
Multiplier: Pow(1000, 1),
|
||||
},
|
||||
{
|
||||
Name: "MB/s",
|
||||
Base: UnitByteRate,
|
||||
Multiplier: Pow(1000, 2),
|
||||
},
|
||||
{
|
||||
Name: "GB/s",
|
||||
Base: UnitByteRate,
|
||||
Multiplier: Pow(1000, 3),
|
||||
},
|
||||
{
|
||||
Name: "TB/s",
|
||||
Base: UnitByteRate,
|
||||
Multiplier: Pow(1000, 4),
|
||||
},
|
||||
{
|
||||
Name: "PB/s",
|
||||
Base: UnitByteRate,
|
||||
Multiplier: Pow(1000, 5),
|
||||
},
|
||||
{
|
||||
Name: "EB/s",
|
||||
Base: UnitByteRate,
|
||||
Multiplier: Pow(1000, 6),
|
||||
},
|
||||
}
|
||||
|
||||
hertz = []*Unit{
|
||||
{
|
||||
Name: "MHz",
|
||||
Base: UnitHertz,
|
||||
Multiplier: Pow(1000, 1),
|
||||
},
|
||||
{
|
||||
Name: "GHz",
|
||||
Base: UnitHertz,
|
||||
Multiplier: Pow(1000, 3),
|
||||
},
|
||||
}
|
||||
|
||||
watts = []*Unit{
|
||||
{
|
||||
Name: "mW",
|
||||
Base: UnitWatt,
|
||||
Multiplier: Pow(10, 3),
|
||||
InverseMultiplier: true,
|
||||
},
|
||||
{
|
||||
Name: "W",
|
||||
Base: UnitWatt,
|
||||
Multiplier: 1,
|
||||
},
|
||||
{
|
||||
Name: "kW",
|
||||
Base: UnitWatt,
|
||||
Multiplier: Pow(10, 3),
|
||||
},
|
||||
{
|
||||
Name: "MW",
|
||||
Base: UnitWatt,
|
||||
Multiplier: Pow(10, 6),
|
||||
},
|
||||
{
|
||||
Name: "GW",
|
||||
Base: UnitWatt,
|
||||
Multiplier: Pow(10, 9),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Build the index
|
||||
for _, units := range [][]*Unit{binarySIBytes, decimalSIBytes, binarySIByteRates, decimalSIByteRates, watts, hertz} {
|
||||
for _, unit := range units {
|
||||
UnitIndex[unit.Name] = unit
|
||||
lengthSortedUnits = append(lengthSortedUnits, unit.Name)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(lengthSortedUnits, func(i, j int) bool {
|
||||
return len(lengthSortedUnits[i]) >= len(lengthSortedUnits[j])
|
||||
})
|
||||
}
|
38
plugins/shared/structs/util.go
Normal file
38
plugins/shared/structs/util.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/nomad/helper"
|
||||
"github.com/hashicorp/nomad/plugins/shared/structs/proto"
|
||||
)
|
||||
|
||||
func ConvertProtoAttribute(in *proto.Attribute) *Attribute {
|
||||
out := &Attribute{
|
||||
Unit: in.Unit,
|
||||
}
|
||||
|
||||
switch in.Value.(type) {
|
||||
case *proto.Attribute_BoolVal:
|
||||
out.Bool = helper.BoolToPtr(in.GetBoolVal())
|
||||
case *proto.Attribute_FloatVal:
|
||||
out.Float = helper.Float64ToPtr(in.GetFloatVal())
|
||||
case *proto.Attribute_IntVal:
|
||||
out.Int = helper.Int64ToPtr(in.GetIntVal())
|
||||
case *proto.Attribute_StringVal:
|
||||
out.String = helper.StringToPtr(in.GetStringVal())
|
||||
default:
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func Pow(a, b int64) int64 {
|
||||
var p int64 = 1
|
||||
for b > 0 {
|
||||
if b&1 != 0 {
|
||||
p *= a
|
||||
}
|
||||
b >>= 1
|
||||
a *= a
|
||||
}
|
||||
return p
|
||||
}
|
Loading…
Reference in a new issue