Upgrade dependent version: github.com/open-policy-agent/opa v0.18.0 -> v0.45.0 Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io> Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io>
126 lines
2.9 KiB
Go
126 lines
2.9 KiB
Go
// Copyright 2022 The OPA Authors. All rights reserved.
|
|
// Use of this source code is governed by an Apache2
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package topdown
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"math/big"
|
|
"strings"
|
|
|
|
"github.com/open-policy-agent/opa/ast"
|
|
"github.com/open-policy-agent/opa/topdown/builtins"
|
|
)
|
|
|
|
// Binary Si unit constants are borrowed from topdown/parse_bytes
|
|
const siMilli = 0.001
|
|
const (
|
|
siK uint64 = 1000
|
|
siM = siK * 1000
|
|
siG = siM * 1000
|
|
siT = siG * 1000
|
|
siP = siT * 1000
|
|
siE = siP * 1000
|
|
)
|
|
|
|
func parseUnitsError(msg string) error {
|
|
return fmt.Errorf("%s: %s", ast.UnitsParse.Name, msg)
|
|
}
|
|
|
|
func errUnitNotRecognized(unit string) error {
|
|
return parseUnitsError(fmt.Sprintf("unit %s not recognized", unit))
|
|
}
|
|
|
|
var (
|
|
errNoAmount = parseUnitsError("no amount provided")
|
|
errNumConv = parseUnitsError("could not parse amount to a number")
|
|
errIncludesSpaces = parseUnitsError("spaces not allowed in resource strings")
|
|
)
|
|
|
|
// Accepts both normal SI and binary SI units.
|
|
func builtinUnits(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
|
|
var x big.Rat
|
|
|
|
raw, err := builtins.StringOperand(operands[0].Value, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// We remove escaped quotes from strings here to retain parity with units.parse_bytes.
|
|
s := string(raw)
|
|
s = strings.Replace(s, "\"", "", -1)
|
|
|
|
if strings.Contains(s, " ") {
|
|
return errIncludesSpaces
|
|
}
|
|
|
|
num, unit := extractNumAndUnit(s)
|
|
if num == "" {
|
|
return errNoAmount
|
|
}
|
|
|
|
// Unlike in units.parse_bytes, we only lowercase after the first letter,
|
|
// so that we can distinguish between 'm' and 'M'.
|
|
if len(unit) > 1 {
|
|
lower := strings.ToLower(unit[1:])
|
|
unit = unit[:1] + lower
|
|
}
|
|
|
|
switch unit {
|
|
case "m":
|
|
x.SetFloat64(siMilli)
|
|
case "":
|
|
x.SetUint64(none)
|
|
case "k", "K":
|
|
x.SetUint64(siK)
|
|
case "ki", "Ki":
|
|
x.SetUint64(ki)
|
|
case "M":
|
|
x.SetUint64(siM)
|
|
case "mi", "Mi":
|
|
x.SetUint64(mi)
|
|
case "g", "G":
|
|
x.SetUint64(siG)
|
|
case "gi", "Gi":
|
|
x.SetUint64(gi)
|
|
case "t", "T":
|
|
x.SetUint64(siT)
|
|
case "ti", "Ti":
|
|
x.SetUint64(ti)
|
|
case "p", "P":
|
|
x.SetUint64(siP)
|
|
case "pi", "Pi":
|
|
x.SetUint64(pi)
|
|
case "e", "E":
|
|
x.SetUint64(siE)
|
|
case "ei", "Ei":
|
|
x.SetUint64(ei)
|
|
default:
|
|
return errUnitNotRecognized(unit)
|
|
}
|
|
|
|
numRat, ok := new(big.Rat).SetString(num)
|
|
if !ok {
|
|
return errNumConv
|
|
}
|
|
|
|
numRat.Mul(numRat, &x)
|
|
|
|
// Cleaner printout when we have a pure integer value.
|
|
if numRat.IsInt() {
|
|
return iter(ast.NumberTerm(json.Number(numRat.Num().String())))
|
|
}
|
|
|
|
// When using just big.Float, we had floating-point precision
|
|
// issues because quantities like 0.001 are not exactly representable.
|
|
// Rationals (such as big.Rat) do not suffer this problem, but are
|
|
// more expensive to compute with in general.
|
|
return iter(ast.NumberTerm(json.Number(numRat.FloatString(10))))
|
|
}
|
|
|
|
func init() {
|
|
RegisterBuiltinFunc(ast.UnitsParse.Name, builtinUnits)
|
|
}
|